diff --git a/lib/Db/StepMapper.php b/lib/Db/StepMapper.php index 3f9a2138420..959f797c34b 100644 --- a/lib/Db/StepMapper.php +++ b/lib/Db/StepMapper.php @@ -56,6 +56,25 @@ public function getLatestVersion(int $documentId): ?int { return $data['id']; } + public function getBeforeVersion(int $documentId, int $version, int $offset): int { + $qb = $this->db->getQueryBuilder(); + $result = $qb->select('id') + ->from($this->getTableName()) + ->where($qb->expr()->eq('document_id', $qb->createNamedParameter($documentId))) + ->andWhere($qb->expr()->lt('id', $qb->createNamedParameter($version))) + ->setMaxResults($offset) + ->orderBy('id', 'DESC') + ->executeQuery(); + + $rows = $result->fetchAll(); + $data = end($rows); + if ($data === false) { + return $version; + } + + return $data['id']; + } + public function deleteAll(int $documentId): void { $qb = $this->db->getQueryBuilder(); $qb->delete($this->getTableName()) diff --git a/lib/Service/DocumentService.php b/lib/Service/DocumentService.php index e37dfd70dca..4b0cf031bad 100644 --- a/lib/Service/DocumentService.php +++ b/lib/Service/DocumentService.php @@ -238,8 +238,9 @@ public function addStep(Document $document, Session $session, array $steps, int $stateFile = $this->getStateFile($documentId); $documentState = $stateFile->getContent(); $this->logger->debug('Existing document, state file loaded ' . $documentId); - // If there were any queries in the steps, send all steps since last save. - $getStepsSinceVersion = $document->getLastSavedVersion(); + // If there were any queries in the steps, send all steps starting 200 steps before last save. + // Adding 200 previous steps to workaround race conditions where state with missing step got persisted in the document state. See #7692 + $getStepsSinceVersion = $this->stepMapper->getBeforeVersion($documentId, $document->getLastSavedVersion(), 200); } catch (NotFoundException $e) { $this->logger->debug('Existing document, but no state file found for ' . $documentId); // If there is no state file, include all the steps.