diff --git a/lib/Db/StepMapper.php b/lib/Db/StepMapper.php index b92d335b670..a2da2d9898a 100644 --- a/lib/Db/StepMapper.php +++ b/lib/Db/StepMapper.php @@ -55,6 +55,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 bf142e979cd..0d0af9a8e7f 100644 --- a/lib/Service/DocumentService.php +++ b/lib/Service/DocumentService.php @@ -239,8 +239,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.