Skip to content

Make create session idempotent - #1539

Merged
malatewang merged 1 commit into
speedkickfrom
session_sync
Sep 2, 2026
Merged

malatewang merged 1 commit into
speedkickfrom
session_sync

Conversation

@malatewang

Copy link
Copy Markdown
Contributor

Purpose of the change

Make the session creation to be idempotent

Description

To support scale out, it is possible that multiple workers create the same session simultaneously. This PR makes session creation idempotent.

Fixes/Closes

Fixes #(issue number)

Type of change

[Please delete options that are not relevant.]

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (does not change functionality, e.g., code style improvements, linting)
  • Documentation update
  • Project Maintenance (updates to build scripts, CI, etc., that do not affect the main project)
  • Security (improves security without changing functionality)

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration.

[Please delete options that are not relevant.]

  • Unit Test
  • Integration Test
  • End-to-end Test
  • Test Script (please provide)
  • Manual verification (list step-by-step instructions)

Test Results: [Attach logs, screenshots, or relevant output]

Checklist

[Please delete options that are not relevant.]

  • I have signed the commit(s) within this pull request
  • My code follows the style guidelines of this project (See STYLE_GUIDE.md)
  • I have performed a self-review of my own code
  • I have commented my code
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added unit tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules
  • I have checked my code and corrected any misspellings

Maintainer Checklist

  • Confirmed all checks passed
  • Contributor has signed the commit(s)
  • Reviewed the code
  • Run, Tested, and Verified the change(s) work as expected

Screenshots/Gifs

[If applicable, add screenshots or GIFs that show the changes in action. This is especially helpful for API responses. Otherwise, delete this section or type "N/A".]

Further comments

[Add any other relevant information here, such as potential side effects, future considerations, or any specific questions for the reviewer. Otherwise, type "None".]

@edwinyyyu

edwinyyyu commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor

Reviewed the idempotent-create behavior. Note the current diff here is just the create_new_session -> create_new_session_if_not_exist rename; the gate itself already landed on speedkick, so I could not anchor these inline. Both points are about that gate and both cut against the goal in the description ("multiple workers create the same session simultaneously").

1. Concurrent creates still fail rather than converge.

create_new_session_if_not_exist is SELECT-then-INSERT in one plain async_sessionmaker() session at default isolation, and session_key is the primary key. Two workers creating the same session simultaneously both find no row, both INSERT, and the loser gets an IntegrityError. IntegrityError is not a ValueError and is not in RestError.is_known_error, so create_project does not catch it and the caller sees a 500 instead of 201/409.

The self._session_locks[session_key].write_lock() in EpisodicMemoryManager only serializes within one worker, which is the case that already worked. So creation is currently idempotent for sequential retries but not for concurrent ones.

Suggest either an upsert (INSERT ... ON CONFLICT (session_key) DO NOTHING, then re-read and run the same comparison) or wrapping the insert in try/except IntegrityError that falls back into the equality branch. A test issuing two concurrent creates against a real engine would cover it; the tests added with the gate are all sequential.

2. The equality gate compares server-merged config, so it can 409 a semantically identical session.

param_data is the fully merged EpisodicMemoryConf (server defaults merged with the user partial in _with_default_episodic_memory_conf), not the caller's intent, so full-dict == couples idempotency to server-side state.

Two cases that matter for scale out: workers running different builds, where a newly added field with a default makes model_dump(mode="json") produce a superset of the stored dict; and rows written before the pickle-to-JSON migration, which need not round-trip byte-identical to a fresh dump. In both, re-creating an equivalent session raises SessionAlreadyExistsError (409) during exactly the rolling upgrade this is meant to survive.

Comparing a normalized subset, or only the fields the caller actually specified, would be more robust than ==.

3. Naming (this PR's diff).

create_new_session_if_not_exist reads as "create if absent, otherwise no-op", but it raises on any mismatch of configuration / param_data / user_metadata or on a Deleted row. Something like create_or_verify_session or ensure_session matches the actual contract more closely.


🤖 Written by Claude Code (Opus 5) on behalf of @edwinyyyu.

@malatewang
malatewang merged commit 3b0d9ec into speedkick Sep 2, 2026
26 of 42 checks passed
@malatewang
malatewang deleted the session_sync branch September 2, 2026 18:29
This was referenced Sep 16, 2026
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.

2 participants