Repository navigation
Conversation
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.
Summary
handleDeviceAlias(lambda/shared/device-lookup.ts) read the alias record, decided a Notecard swap had occurred, then issued an unconditionalUpdateCommand. Two events for the sameserial_numberarriving concurrently (a real possibility with Notehub batch delivery + Lambda fan-out) could both read the samedevice_uidand race — the later write silently clobbering the earlier one, andlist_appendblindly duplicating uids inprevious_device_uids.Changes (
lambda/shared/device-lookup.ts)updateAliasOnSwapnow takes the caller's observedpreviousDeviceUidsand:ConditionExpression: 'device_uid = :expected_old'so it only applies whendevice_uidis still what the caller read (closes the TOCTOU window),previous_device_uidsas a de-duplicated set (Set) that excludes the new current uid, instead of unconditionallist_append.handleDeviceAliaswraps the read→swap in a retry loop (max 3 attempts). OnConditionalCheckFailedExceptionit re-reads and retries; if a concurrent writer already set our targetdevice_uid, the equality check returns a clean no-op. The new-device path still relies oncreateAlias's existingattribute_not_existscondition.Tests (
lambda/shared/device-lookup.test.ts)ConditionExpression+:expected_oldvalue.UpdateCommandrejects withConditionalCheckFailedException, retry succeeds (2 update calls).{ isSwap: false }.npm run build(tsc) andnpm test(vitest) both pass — 455 tests, 32 files.Closes review finding M11