-
Notifications
You must be signed in to change notification settings - Fork 59
Description
I have observed this issue over some time, but had a hard time reproducing it. In rare instances during deployments, projections could loose their stream positions and start from the beginning.
This can happen in instances where a pcntl stop handler invokes a projection's stop() method before the projection has loaded the stream positions.
The problematic section is in the run() method, before the actual main loop:
// Initial State is ProjectionStatus::IDLE()
// $this->streamPositions is an empty array
if (! $this->projectionExists()) {
$this->createProjection();
}
$this->acquireLock();
if (! $this->readModel->isInitialized()) {
$this->readModel->init();
}
$this->prepareStreamPositions();
$this->load(); // << Only here $this->streamPositions will be initialized
Calling stop() from the signal handler before load() loaded the stream positions, causes a call to persist((). The persist call saves the uninitialized stream positions (an empty array).
This issue becomes more frequent / likely the longer it takes the projection to load the stream positions. Putting a sleep() call after the acquireLock() call in the example above makes it rather easy to reproduce (we have replaced the locking mechanism with metadata locks on a 60 second timeout, hence more likely for us to run into this issue).
The current workaround for us seems to be to introduce a new flag $this->streamPositionsLoaded and have an early return in persist() if positions have not been loaded yet.
private function persist(): void
{
if (!$this->streamPositionsLoaded) {
return;
}
$this->readModel->persist();
// ...
We are running this in testing at the moment to see if this actually fixes the issue. Any other input / feedback / solutions are welcome though.