Skip to content

fix(firestore): enforce write preconditions and transaction conflicts - #114

Open
tdewitt wants to merge 3 commits into
floci-io:mainfrom
misfitdev:fix/firestore-preconditions-transactions
Open

fix(firestore): enforce write preconditions and transaction conflicts#114
tdewitt wants to merge 3 commits into
floci-io:mainfrom
misfitdev:fix/firestore-preconditions-transactions

Conversation

@tdewitt

@tdewitt tdewitt commented Aug 2, 2026

Copy link
Copy Markdown

Summary

Fixes #113

The Firestore emulator applied every write unconditionally: Write.current_document preconditions were ignored and Commit never validated its transaction, so double-creates succeeded, stale preconditions passed silently, and conflicting transactions all committed (losing updates).

  • fix(firestore): enforce current_document preconditions on all write paths — exists=falseALREADY_EXISTS, exists=trueNOT_FOUND, stale update_timeFAILED_PRECONDITION; CreateDocument/UpdateDocument/DeleteDocument pass their preconditions through
  • fix(firestore): transaction conflict detection — BeginTransaction registers server-side state, transactional reads (GetDocument, BatchGetDocuments, RunQuery, incl. new_transaction) record the returned snapshot's version, and Commit aborts with ABORTED if any read document changed; Rollback discards state
  • fix(firestore): Commit and the Write stream are atomic per request (preconditions validated before any write lands); BatchWrite is non-atomic with per-write status, matching real Firestore
  • feat(core): add GcpException.aborted() (gRPC ABORTED, HTTP 409)

Intentional deviations (documented in docs/services/firestore.md): query read sets cover returned documents only (no phantom-read detection), RunAggregationQuery ignores transactions, transaction state is in-memory with a 15-minute TTL.

Validated with the Java SDK against a running emulator: create/update precondition failures surface as SDK exceptions, and a transaction with a conflicting concurrent write is aborted and retried by the SDK exactly once with no lost update.

Type of change

  • Bug fix (fix:)
  • New feature (feat:)
  • Breaking change (feat!: or fix!:)
  • Docs / chore

Checklist

  • ./mvnw test passes locally
  • New or updated integration test added
  • Commit messages follow Conventional Commits

@greptile-apps

greptile-apps Bot commented Aug 2, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds Firestore write-precondition enforcement and optimistic transaction conflict detection while making Commit and streaming Write requests atomic with respect to validation.

  • Propagates and validates create, update, delete, and update-time preconditions.
  • Tracks transactional document-read versions and aborts conflicting commits.
  • Adds transaction lifecycle handling for begin, rollback, expiry pruning, and successful commit cleanup.
  • Returns per-write BatchWrite statuses and adds the ABORTED exception mapping.
  • Expands unit, SDK compatibility, and service documentation coverage.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete changed-code defect remaining after reviewing the precondition and transaction paths.

Preconditions are validated before writes are applied, transactional reads retain the returned snapshot versions, conflicting commits abort, and successful commits discard transaction state; the added tests cover the principal reachable failure paths.

Important Files Changed

Filename Overview
src/main/java/io/floci/gcp/services/firestore/FirestoreService.java Adds synchronized precondition validation, atomic validation-before-application, transaction read-set tracking, conflict detection, and lifecycle cleanup without an accepted defect.
src/main/java/io/floci/gcp/services/firestore/FirestoreController.java Routes preconditions and transaction selectors through the service, records returned snapshot versions, and emits per-write BatchWrite statuses.
src/main/java/io/floci/gcp/core/common/GcpException.java Adds the expected HTTP and gRPC mapping for transaction abort errors.
src/test/java/io/floci/gcp/services/firestore/FirestoreServiceTest.java Adds focused coverage for preconditions, atomic validation, conflicts, racing reads, retries, rollback, and malformed stored timestamps.
compatibility-tests/sdk-test-java/src/test/java/io/floci/gcp/test/FirestoreTest.java Adds Java SDK compatibility coverage for precondition errors and automatic transaction retry behavior.
docs/services/firestore.md Documents preconditions, optimistic concurrency, atomicity semantics, and intentional emulator deviations.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Controller as FirestoreController
    participant Service as FirestoreService
    participant Store as DocumentStore
    Client->>Controller: BeginTransaction
    Controller->>Service: beginTransaction()
    Service-->>Client: transaction ID
    Client->>Controller: Transactional read
    Controller->>Store: Read document snapshot
    Controller->>Service: recordTransactionRead(id, name, version)
    Controller-->>Client: Snapshot
    Client->>Controller: Commit(writes, transaction ID)
    Controller->>Service: commit(writes, id)
    Service->>Store: Validate recorded versions
    alt Read version changed
        Service-->>Client: ABORTED
    else Versions and preconditions valid
        Service->>Store: Apply all writes
        Service-->>Client: CommitResponse
    end
Loading

Reviews (1): Last reviewed commit: "fix(firestore): record transaction reads..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Firestore: write preconditions and transaction conflicts are not enforced

1 participant