@@ -1564,65 +1564,81 @@ class SqliteIdentityStorage implements IdentityStorage {
15641564 } ;
15651565 }
15661566
1567- const lineage = backend . db
1568- . prepare < IdentityLineageRow > ( SELECT_IDENTITY_LINEAGE_SQL )
1569- . get ( normalizedIdentityId ) ;
1570- if ( ! lineage ) {
1571- return null ;
1572- }
1573-
1574- const identityRecord = hydrateIdentityLineage ( lineage ) ;
1575- if ( ! identityRecord ) {
1576- return null ;
1577- }
1578- const sponsorChain = backend . db
1579- . prepare < LineageMemberRow > ( SELECT_IDENTITY_LINEAGE_MEMBERS_SQL )
1580- . all ( normalizedIdentityId )
1581- . map ( ( row ) => normalizeOptionalString ( row . principal_id ) )
1582- . filter ( ( value ) : value is string => Boolean ( value ) ) ;
1567+ // Keep identity, member, and token snapshots consistent when another
1568+ // connection issues a token while this lineage request is in flight.
1569+ backend . db . exec ( "BEGIN" ) ;
1570+ try {
1571+ const lineage = backend . db
1572+ . prepare < IdentityLineageRow > ( SELECT_IDENTITY_LINEAGE_SQL )
1573+ . get ( normalizedIdentityId ) ;
1574+ if ( ! lineage ) {
1575+ backend . db . exec ( "COMMIT" ) ;
1576+ return null ;
1577+ }
15831578
1584- const tokenMembers = backend . db
1585- . prepare < TokenLineageMemberRow > (
1586- SELECT_TOKEN_LINEAGE_MEMBERS_BY_IDENTITY_SQL ,
1587- )
1588- . all ( normalizedIdentityId , LINEAGE_TOKEN_QUERY_LIMIT )
1589- . reduce ( ( byToken , member ) => {
1590- const tokenId = normalizeOptionalString ( member . token_id ) ;
1591- const principalId = normalizeOptionalString ( member . principal_id ) ;
1592- if ( tokenId && principalId ) {
1593- const members = byToken . get ( tokenId ) ?? [ ] ;
1594- members . push ( principalId ) ;
1595- byToken . set ( tokenId , members ) ;
1596- }
1597- return byToken ;
1598- } , new Map < string , string [ ] > ( ) ) ;
1599-
1600- const tokenRows = backend . db
1601- . prepare < TokenLineageRow > ( SELECT_TOKEN_LINEAGES_SQL )
1602- . all ( normalizedIdentityId , LINEAGE_TOKEN_QUERY_LIMIT ) ;
1603- const tokensTruncated = tokenRows . length > MAX_LINEAGE_TOKEN_RECORDS ;
1604- const tokens = tokenRows
1605- . slice ( 0 , MAX_LINEAGE_TOKEN_RECORDS )
1606- . map ( ( row ) => {
1607- const token = hydrateTokenLineage ( row ) ;
1608- if ( ! token ) {
1609- return null ;
1610- }
1611- return {
1612- ...token ,
1613- sponsorChain : [
1614- ...( tokenMembers . get ( normalizeOptionalString ( row . token_id ) ?? "" ) ?? [ ] ) ,
1615- ] ,
1616- } ;
1617- } )
1618- . filter ( ( token ) : token is TokenLineageRecord => token !== null ) ;
1579+ const identityRecord = hydrateIdentityLineage ( lineage ) ;
1580+ if ( ! identityRecord ) {
1581+ backend . db . exec ( "COMMIT" ) ;
1582+ return null ;
1583+ }
1584+ const sponsorChain = backend . db
1585+ . prepare < LineageMemberRow > ( SELECT_IDENTITY_LINEAGE_MEMBERS_SQL )
1586+ . all ( normalizedIdentityId )
1587+ . map ( ( row ) => normalizeOptionalString ( row . principal_id ) )
1588+ . filter ( ( value ) : value is string => Boolean ( value ) ) ;
1589+
1590+ const tokenMembers = backend . db
1591+ . prepare < TokenLineageMemberRow > (
1592+ SELECT_TOKEN_LINEAGE_MEMBERS_BY_IDENTITY_SQL ,
1593+ )
1594+ . all ( normalizedIdentityId , LINEAGE_TOKEN_QUERY_LIMIT )
1595+ . reduce ( ( byToken , member ) => {
1596+ const tokenId = normalizeOptionalString ( member . token_id ) ;
1597+ const principalId = normalizeOptionalString ( member . principal_id ) ;
1598+ if ( tokenId && principalId ) {
1599+ const members = byToken . get ( tokenId ) ?? [ ] ;
1600+ members . push ( principalId ) ;
1601+ byToken . set ( tokenId , members ) ;
1602+ }
1603+ return byToken ;
1604+ } , new Map < string , string [ ] > ( ) ) ;
1605+
1606+ const tokenRows = backend . db
1607+ . prepare < TokenLineageRow > ( SELECT_TOKEN_LINEAGES_SQL )
1608+ . all ( normalizedIdentityId , LINEAGE_TOKEN_QUERY_LIMIT ) ;
1609+ const tokensTruncated = tokenRows . length > MAX_LINEAGE_TOKEN_RECORDS ;
1610+ const tokens = tokenRows
1611+ . slice ( 0 , MAX_LINEAGE_TOKEN_RECORDS )
1612+ . map ( ( row ) => {
1613+ const token = hydrateTokenLineage ( row ) ;
1614+ if ( ! token ) {
1615+ return null ;
1616+ }
1617+ return {
1618+ ...token ,
1619+ sponsorChain : [
1620+ ...( tokenMembers . get ( normalizeOptionalString ( row . token_id ) ?? "" ) ?? [ ] ) ,
1621+ ] ,
1622+ } ;
1623+ } )
1624+ . filter ( ( token ) : token is TokenLineageRecord => token !== null ) ;
16191625
1620- return {
1621- ...identityRecord ,
1622- sponsorChain,
1623- tokens,
1624- tokensTruncated,
1625- } ;
1626+ const result = {
1627+ ...identityRecord ,
1628+ sponsorChain,
1629+ tokens,
1630+ tokensTruncated,
1631+ } ;
1632+ backend . db . exec ( "COMMIT" ) ;
1633+ return result ;
1634+ } catch ( error ) {
1635+ try {
1636+ backend . db . exec ( "ROLLBACK" ) ;
1637+ } catch {
1638+ // Preserve the original query failure.
1639+ }
1640+ throw error ;
1641+ }
16261642 }
16271643
16281644 private async getRequired ( id : string ) : Promise < StoredIdentity > {
0 commit comments