Skip to content

[GH-1174] Fix episode_type enum UniqueViolationError during concurrent startup - #1175

Merged
sscargal merged 1 commit into
MemMachine:mainfrom
o-love:fix/1174-episode-type-unique-violation
Mar 4, 2026
Merged

sscargal merged 1 commit into
MemMachine:mainfrom
o-love:fix/1174-episode-type-unique-violation

Conversation

@o-love

@o-love o-love commented Mar 4, 2026 •

Copy link
Copy Markdown
Contributor

Purpose of the change

Fix UniqueViolationError on pg_type_typname_nsp_index for the episode_type PostgreSQL enum when multiple uvicorn workers start concurrently.

Description

SQLAlchemy's metadata.create_all(checkfirst=True) queries pg_catalog.pg_type before issuing CREATE TYPE, but this is a Python-level TOCTOU (time-of-check-to-time-of-use) check — not an atomic SQL operation. When multiple uvicorn workers (MEMMACHINE_WORKERS > 1) run the startup lifespan concurrently, a race window exists where two workers both find the type missing and both attempt CREATE TYPE episode_type, causing the second to fail with UniqueViolationError.

Since PostgreSQL aborts the entire transaction on any error, the failed CREATE TYPE also prevents subsequent CREATE TABLE statements, leaving the database partially initialized and the container in a restart loop.

Changes:

  • Set create_type=False on the SAEnum column to prevent create_all from attempting automatic CREATE TYPE
  • Create a standalone pg_dialect.ENUM whose values are derived from the Python EpisodeType enum (stays in sync automatically)
  • In startup(), explicitly create the enum type inside a begin_nested() (SAVEPOINT) before create_all. If a concurrent worker already created the type, the IntegrityError rolls back only the savepoint — not the outer transaction — so create_all can proceed normally
  • Skip the explicit enum creation for non-PostgreSQL dialects (SQLite uses VARCHAR for enums, unaffected by create_type=False)

Fixes/Closes

Fixes #1174

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

  • Unit Test
  • Integration Test

Checklist

  • My code follows the style guidelines of this project (See STYLE_GUIDE.md)
  • I have performed a self-review of my own code
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes
  • I have checked my code and corrected any misspellings

Screenshots/Gifs

N/A

Prevent UniqueViolationError on pg_type_typname_nsp_index when multiple
uvicorn workers call metadata.create_all concurrently. SQLAlchemy's
checkfirst queries pg_type then issues CREATE TYPE in separate steps,
creating a TOCTOU window where a concurrent process can create the type
between check and create.

Disable automatic enum creation (create_type=False) and instead create
the type explicitly inside a SAVEPOINT so a duplicate-type IntegrityError
rolls back only the savepoint, keeping the outer transaction healthy for
subsequent CREATE TABLE statements.

Closes #1174

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a PostgreSQL startup race where multiple concurrent workers can collide while creating the episode_type enum, causing UniqueViolationError and aborting schema initialization.

Changes:

  • Disable SQLAlchemy’s automatic enum type creation (create_type=False) for Episode.episode_type.
  • Add explicit, PostgreSQL-only enum creation guarded by a SAVEPOINT (begin_nested()) during SqlAlchemyEpisodeStore.startup().
  • Add unit + integration tests covering the enum creation behavior and error handling.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
packages/server/src/memmachine_server/common/episode_store/episode_sqlalchemy_store.py Adds explicit PG enum creation during startup and disables automatic enum creation on the column.
packages/server/server_tests/memmachine_server/common/episode_store/test_sqlalchemy_episode_store_startup.py Adds tests for startup wrapping, enum idempotency behavior, and PostgreSQL integration verification.
Comments suppressed due to low confidence (1)

packages/server/src/memmachine_server/common/episode_store/episode_sqlalchemy_store.py:164

  • The enum-creation block catches IntegrityError and ProgrammingError unconditionally and assumes they mean "enum already exists". ProgrammingError can also represent real startup failures (e.g. permission denied, wrong schema/search_path), and swallowing it can lead to harder-to-diagnose errors later in create_all. Consider only suppressing the specific duplicate/unique-violation cases (e.g. by checking SQLSTATE such as 42710/23505 or matching the underlying driver exception/message for episode_type) and re-raising otherwise.
                    except (IntegrityError, ProgrammingError):
                        logger.debug(
                            "episode_type enum already exists (concurrent creation); continuing"
                        )

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@sscargal sscargal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for the quick fix. Tested ok. No errors seen on the console when spawning multiple workers.

@sscargal
sscargal merged commit 90c293e into MemMachine:main Mar 4, 2026
41 of 48 checks passed
@o-love
o-love deleted the fix/1174-episode-type-unique-violation branch March 4, 2026 23:08
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]: UniqueViolationError – duplicate key value violates unique constraint "pg_type_typname_nsp_index" after local Docker build

4 participants