fix(api): allocate unnamed provider-key display names atomically - #6227
fix(api): allocate unnamed provider-key display names atomically#6227WhoamiI00 wants to merge 2 commits into
Conversation
|
@WhoamiI00 is attempting to deploy a commit to the agenta projects Team on Vercel. A member of the Team first needs to authorize it. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (6)
Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review. 📝 WalkthroughSummary by CodeRabbit
WalkthroughUnnamed provider-key creation now derives names and slugs inside a PostgreSQL advisory-locked transaction. The DAO interface and service use a callback-based naming flow. Tests cover managed-secret metadata and concurrent and sequential allocation. ChangesProvider-key name allocation
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: ⚪ Minimal · up to The change makes concurrent unnamed provider-key creation allocate distinct display names atomically; no actionable merge-blocking risk remains beyond normal checks and review. Sequence Diagram(s)sequenceDiagram
participant VaultService
participant SecretsDAO
participant PostgreSQL
VaultService->>SecretsDAO: create_with_derived_naming
SecretsDAO->>PostgreSQL: acquire provider-scope advisory lock
SecretsDAO->>PostgreSQL: load scoped secrets
SecretsDAO->>SecretsDAO: derive name and slug
SecretsDAO->>PostgreSQL: persist and commit secret
SecretsDAO-->>VaultService: return SecretResponseDTO
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Linked Issues checkExplanation The implementation satisfies issue ✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: a7da49f8-926f-41b2-a31f-3f28ebf603e0
⛔ Files ignored due to path filters (1)
.github/pr-assets/6015-provider-key-name-race.pngis excluded by!**/*.png
📒 Files selected for processing (5)
api/oss/src/core/secrets/interfaces.pyapi/oss/src/core/secrets/services.pyapi/oss/src/dbs/postgres/secrets/dao.pyapi/oss/tests/pytest/unit/vault/conftest.pyapi/oss/tests/pytest/unit/vault/test_provider_key_name_allocation.py
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.
An unnamed provider_key connection is named after its provider ("OpenAI", then
"OpenAI 2") by reading the names already taken in the project. The read and the
insert ran in separate DAO sessions, and header.name has no uniqueness
constraint, so two concurrent unnamed creates for the same provider family both
observed the same set and both persisted "OpenAI".
Move the read and the insert into one transaction in SecretsDAO, serialized by
an advisory lock keyed on the project and provider kind. The naming policy stays
in VaultService, which passes it in as a callback; only the transaction boundary
moves. This mirrors SessionAttachmentsDAO, where quota enforcement sits in the
DAO because the transaction-scoped lock can only live where the session does.
No migration, so no partial unique index to backfill or roll back.
`VaultService` routes unnamed provider keys through `create_with_derived_naming`, so the in-memory DAO the service unit tests run against has to implement it too. It mirrors the postgres DAO — name the payload against the scope's current records, then write, carrying `management` down with it — minus the advisory lock, which has nothing to serialize here. The added case covers a managed connection created without a name: that combination is the one the derived-naming path could drop `management` on, turning a manager-owned credential into an ordinary editable one.
0790bcd to
e51af29
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
|
Rebased onto 1. 2. The early return also skipped the new Also worth noting: Verified on the rebased branch:
The earlier |
Fixes #6015
Summary
VaultServicenames an unnamedprovider_keyconnection after its provider — "OpenAI", then "OpenAI 2" — by reading the names already taken in the project. That read and the insert ran in separate DAO sessions, andheader.namehas no uniqueness constraint, so two concurrent unnamed creates for the same provider family both observed the sametaken_namesand both persisted "OpenAI".Approach
Of the two shapes suggested in the issue, this is the advisory lock rather than the partial unique index — it needs no migration, so there is no index to backfill on existing projects and nothing to roll back.
SecretsDAO.create_with_derived_namingtakes the lock, lists the scope's secrets, and inserts, all in one transaction:On the seam concern in the issue — the naming policy does not move.
VaultServicestill ownsnext_provider_key_nameand the slug derivation, and hands them to the DAO as a callback; only the transaction boundary moves. That mirrorsSessionAttachmentsDAO, where quota enforcement lives in the DAO for the same reason ("Quota enforcement lives here because the session advisory lock lives here") — a transaction-scoped lock can only live where the session does._name_and_slug_provider_keyis now synchronous and takes the already-read secrets, since it no longer does its own I/O.Tests
api/oss/tests/pytest/unit/vault/test_provider_key_name_allocation.py, markedintegrationand skipped when Postgres is unreachable, matching the session/git DAO suites.test_concurrent_unnamed_creates_get_distinct_names— the concurrent-create test the issue asks for. Twocreate_secretcalls against one project, bounded byasyncio.wait_forso a regression fails the run instead of hanging it. Onmainit fails with exactly the reported symptom:test_sequential_unnamed_creates_still_increment— passes with and without the change, so it pins that the lock does not alter ordinary one-at-a-time naming.Run against a real Postgres:
oss/tests/pytest/unit/vault5 passed.oss/tests/pytest/unit/sessionshas 4 pre-existing failures intest_records_turn_span_dao.pythat reproduce identically on a cleanmaincheckout, so they are unrelated.ruff formatandruff checkclean.