@@ -88,20 +88,60 @@ def __init__(self):
8888 "appmetadata-{}-{}" .format (environment or "" , client_id or "" ),
8989 }
9090
91- def find (self , credential_type , target = None , query = None ):
92- target = target or []
91+ def _get_access_token (
92+ self ,
93+ home_account_id , environment , client_id , realm , target , # Together they form a compound key
94+ default = None ,
95+ ): # O(1)
96+ return self ._get (
97+ self .CredentialType .ACCESS_TOKEN ,
98+ self .key_makers [TokenCache .CredentialType .ACCESS_TOKEN ](
99+ home_account_id = home_account_id ,
100+ environment = environment ,
101+ client_id = client_id ,
102+ realm = realm ,
103+ target = " " .join (target ),
104+ ),
105+ default = default )
106+
107+ def _get (self , credential_type , key , default = None ): # O(1)
108+ with self ._lock :
109+ return self ._cache .get (credential_type , {}).get (key , default )
110+
111+ def _find (self , credential_type , target = None , query = None ): # O(n) generator
112+ """Returns a generator of matching entries.
113+
114+ It is O(1) for AT hits, and O(n) for other types.
115+ Note that it holds a lock during the entire search.
116+ """
117+ target = sorted (target or []) # Match the order sorted by add()
93118 assert isinstance (target , list ), "Invalid parameter type"
119+
120+ preferred_result = None
121+ if (credential_type == self .CredentialType .ACCESS_TOKEN
122+ and "home_account_id" in query and "environment" in query
123+ and "client_id" in query and "realm" in query and target
124+ ): # Special case for O(1) AT lookup
125+ preferred_result = self ._get_access_token (
126+ query ["home_account_id" ], query ["environment" ],
127+ query ["client_id" ], query ["realm" ], target )
128+ if preferred_result :
129+ yield preferred_result
130+
94131 target_set = set (target )
95132 with self ._lock :
96133 # Since the target inside token cache key is (per schema) unsorted,
97134 # there is no point to attempt an O(1) key-value search here.
98135 # So we always do an O(n) in-memory search.
99- return [entry
100- for entry in self ._cache .get (credential_type , {}).values ()
101- if is_subdict_of (query or {}, entry )
102- and (target_set <= set (entry .get ("target" , "" ).split ())
103- if target else True )
104- ]
136+ for entry in self ._cache .get (credential_type , {}).values ():
137+ if is_subdict_of (query or {}, entry ) and (
138+ target_set <= set (entry .get ("target" , "" ).split ())
139+ if target else True ):
140+ if entry != preferred_result : # Avoid yielding the same entry twice
141+ yield entry
142+
143+ def find (self , credential_type , target = None , query = None ): # Obsolete. Use _find() instead.
144+ return list (self ._find (credential_type , target = target , query = query ))
105145
106146 def add (self , event , now = None ):
107147 """Handle a token obtaining event, and add tokens into cache."""
0 commit comments