@@ -151,9 +151,7 @@ async def open(
151
151
152
152
# Get a new instance by name.
153
153
else :
154
- kvs_path = (
155
- kvs_base_path / cls ._STORAGE_SUBSUBDIR_DEFAULT if name is None else kvs_base_path / name
156
- )
154
+ kvs_path = kvs_base_path / cls ._STORAGE_SUBSUBDIR_DEFAULT if name is None else kvs_base_path / name
157
155
metadata_path = kvs_path / METADATA_FILENAME
158
156
159
157
# If the key-value store directory exists, reconstruct the client from the metadata file.
@@ -235,7 +233,7 @@ async def get_value(self, *, key: str) -> KeyValueStoreRecord | None:
235
233
return None
236
234
237
235
# Read the metadata file
238
- async with self ._lock :
236
+ async with self ._lock : # DEADLOCK
239
237
file = await asyncio .to_thread (open , record_metadata_filepath )
240
238
try :
241
239
metadata_content = json .load (file )
@@ -364,41 +362,42 @@ async def iterate_keys(
364
362
if not self .path_to_kvs .exists ():
365
363
return
366
364
367
- count = 0
365
+ # List and sort all files *inside* a brief lock, then release it immediately:
368
366
async with self ._lock :
369
- # Get all files in the KVS directory, sorted alphabetically
370
367
files = sorted (await asyncio .to_thread (list , self .path_to_kvs .glob ('*' )))
371
368
372
- for file_path in files :
373
- # Skip the main metadata file
374
- if file_path .name == METADATA_FILENAME :
375
- continue
369
+ count = 0
376
370
377
- # Only process metadata files for records
378
- if not file_path .name .endswith (f'.{ METADATA_FILENAME } ' ):
379
- continue
371
+ for file_path in files :
372
+ # Skip the main metadata file
373
+ if file_path .name == METADATA_FILENAME :
374
+ continue
380
375
381
- # Extract the base key name from the metadata filename
382
- key_name = self ._decode_key (file_path .name [: - len (f'.{ METADATA_FILENAME } ' )])
376
+ # Only process metadata files for records
377
+ if not file_path .name .endswith (f'.{ METADATA_FILENAME } ' ):
378
+ continue
383
379
384
- # Apply exclusive_start_key filter if provided
385
- if exclusive_start_key is not None and key_name <= exclusive_start_key :
386
- continue
380
+ # Extract the base key name from the metadata filename
381
+ key_name = self ._decode_key (file_path .name [: - len (f'.{ METADATA_FILENAME } ' )])
387
382
388
- # Try to read and parse the metadata file
389
- try :
390
- metadata_content = await asyncio .to_thread (file_path .read_text , encoding = 'utf-8' )
391
- metadata_dict = json .loads (metadata_content )
392
- record_metadata = KeyValueStoreRecordMetadata (** metadata_dict )
383
+ # Apply exclusive_start_key filter if provided
384
+ if exclusive_start_key is not None and key_name <= exclusive_start_key :
385
+ continue
386
+
387
+ # Try to read and parse the metadata file
388
+ try :
389
+ metadata_content = await asyncio .to_thread (file_path .read_text , encoding = 'utf-8' )
390
+ metadata_dict = json .loads (metadata_content )
391
+ record_metadata = KeyValueStoreRecordMetadata (** metadata_dict )
393
392
394
- yield record_metadata
393
+ yield record_metadata
395
394
396
- count += 1
397
- if limit and count >= limit :
398
- break
395
+ count += 1
396
+ if limit and count >= limit :
397
+ break
399
398
400
- except (json .JSONDecodeError , ValidationError ) as e :
401
- logger .warning (f'Failed to parse metadata file { file_path } : { e } ' )
399
+ except (json .JSONDecodeError , ValidationError ) as e :
400
+ logger .warning (f'Failed to parse metadata file { file_path } : { e } ' )
402
401
403
402
# Update accessed_at timestamp
404
403
await self ._update_metadata (update_accessed_at = True )
0 commit comments