signup: close two concurrency holes from final review
(1) RegisterAnonymousViewer now catches the unique-violation
race (SQLSTATE 23505 on Postgres / code 19 on SQLite) and
re-reads by UDID, returning the existing row instead of
surfacing 500 to the second concurrent /tool/signup caller.
New repo test exercises the back-to-back register path.
(2) Add unique index on SocialAccountConnection (AccountType,
AccountId). The auth handler's find-or-link path claimed
this index existed as the dedup backstop; the claim was
accurate as design intent but the schema was missing. Now
matched. Comment in handler updated.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -157,6 +157,17 @@ public class SVSimDbContext : DbContext
|
||||
b.HasIndex("ViewerId", "ProductId").IsUnique();
|
||||
});
|
||||
|
||||
// A given social account links to exactly one viewer — two viewers cannot share the same
|
||||
// Steam (or Facebook, etc.) account. This is the dedup backstop the auth handler's find-
|
||||
// or-link path (SteamSessionAuthenticationHandler) relies on: two concurrent first-Steam-
|
||||
// touch requests can both pass the .Any(...) check in LinkSteamToViewer, but the second
|
||||
// SaveChanges() throws unique-violation and surfaces a clean 500 instead of silently
|
||||
// appending duplicate connections.
|
||||
modelBuilder.Entity<Viewer>().OwnsMany(v => v.SocialAccountConnections, b =>
|
||||
{
|
||||
b.HasIndex("AccountType", "AccountId").IsUnique();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<BuildDeckSeriesEntry>().OwnsMany(s => s.SeriesRewards);
|
||||
modelBuilder.Entity<BuildDeckProductEntry>().OwnsMany(p => p.Cards);
|
||||
modelBuilder.Entity<BuildDeckProductEntry>().OwnsMany(p => p.Rewards);
|
||||
|
||||
Reference in New Issue
Block a user