Repository navigation
Conversation
FirestoreSessionService stored event timestamps with Instant.toString(), which leaves out the fraction when it is zero, and ordered and filtered events by comparing those strings. Within one second, an event at .000 sorted after the later events, a whole-second afterTimestamp cursor left out the rest of that second, and afterTimestamp used > where InMemorySessionService, VertexAiSessionService and ADK Python keep events at or after the cursor. - Event timestamps are written with exactly three fraction digits. - afterTimestamp is rounded up to whole milliseconds, written the same way and matched with >=. - getSession and listEvents sort the events they read by timestamp, so events stored in the old format also come back in time order. The field stays a string. ADK Python stores a Firestore timestamp, but Firestore orders timestamps before strings, so sessions with events stored before this change would be misordered.
Contributor
|
Hi @innoprej, We greatly appreciate your contribution and the effort you put into this pull request. It is currently under review by our team. We will notify you if any additional details are needed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Please ensure you have read the contribution guide before creating a pull request.
Link to Issue or Description of Change
1. Link to an existing issue (if applicable):
2. Or, if no issue exists, describe the change:
Problem:
FirestoreSessionServicestores each event'stimestampasInstant.toString(), which leaves out the fraction when it is zero, and orders and filters events by comparing those strings. Within one second, an event at.000(…05Z) sorts after the later events (…05.400Z), sogetSessionandlistEventsreturn them out of order.afterTimestampis compared as text too and with>: a whole-second cursor leaves out the rest of that second, a millisecond cursor includes an earlier whole-second event, and an event at exactly the cursor is left out, whileInMemorySessionService,VertexAiSessionServiceand ADK Python keep the events at or after the cursor. #1646 has the details and a reproduction.Solution:
eventToMapwritestimestampwith exactly three fraction digits (DateTimeFormatterBuilder.appendInstant(3), for example2026-10-10T05:00:05.000Z). Event timestamps are whole milliseconds, so the digits are exact and text order is time order.eventFromMapandFirestoreMemoryServiceread both forms, as before.getSessionroundsafterTimestampup to whole milliseconds, writes it the same way and filters withwhereGreaterThanOrEqualTo. An event at millisecondmis at or after the cursortexactly whenm >= ceil(t), which is whatInMemorySessionServicekeeps. The query still orders and filters on the one field, so no new index is needed.getSessionandlistEventssort the events they read by timestamp, asVertexAiSessionService.filterEventsdoes. Events stored before this change keep their old strings, and this keeps those sessions in time order too.Testing Plan
Unit Tests:
./mvnw -pl contrib/firestore-session-service test: 55 run, 2 failures, both inFirestorePropertiesTest(shouldReturnHardcodedDefaultIfPropertyFileHasNoEntry,shouldLoadDevPropertiesWhenEnvIsSetToDev), which fail only on Windows, the same way onmain, and pass in the Ubuntu CI (local run: Microsoft Build of OpenJDK 17.0.19, Windows 11). The changed and new tests are inFirestoreSessionServiceTest:mainappendEvent_storesTimestampsWithThreeFractionDigits: events at05.000,05.400and06.000…05Z,…05.400Z,…06ZgetSession_withAfterTimestamp_queriesAtOrAfterRoundedCursor(replacesgetSession_withAfterTimestamp_appliesFilterToQuery, which usedInstant.now()): cursors…05Z,…05.200Zand…05.200300ZwhereGreaterThanwith…05ZwhereGreaterThanOrEqualTowith…05.000Z,…05.200Zand…05.201ZgetSession_eventsStoredOutOfOrder_returnsEventsInTimeOrder: documents…05.400Zand…05Z, in Firestore's text order05.400firstlistEvents_eventsStoredOutOfOrder_returnsEventsInTimeOrder: the same forlistEvents05.400firstRounding the cursor down instead of up fails the second test (
…05.200Zinstead of…05.201Z).Manual End-to-End (E2E) Tests:
Not run against a real Firestore database or the emulator. The tests check the strings and the query the service sends to a mocked
Firestore; the order and the result sets then follow from Firestore's documented ordering of strings by UTF-8 bytes. With this commit, #1643 and #1650 applied together, all 38 tests inFirestoreSessionServiceTestpass.Checklist
Additional context
numRecentEventscan return an old…05Zevent instead of a later…05.400Zone, andafterTimestampcan include an old…05Zevent although the cursor is later in that second. It no longer leaves out events at or after the cursor.timestampstays a string, a deliberate difference from ADK Python noted in theTIMESTAMP_FORMATTERJavadoc. ADK Python stores a Firestore timestamp, but Firestore sorts timestamps before strings, so sessions holding both would be misordered unless the stored events are migrated. If you would prefer the timestamp type with a migration, I can change this.FirestoreMemoryServicereturns the stored string asMemoryEntry.timestamp, so entries for events at a whole second now read…05.000Zinstead of…05Z. Both are ISO 8601.updateTimeis still written withtoString(); it is only parsed back, never compared in a query.