Skip to content

Commit 339f258

Browse files
feat: add key, collection, and version fields to serialized documents
This change adds support for storing unsanitized key and collection names along with a version number in serialized ManagedEntry documents. This enables proper enumeration support in stores that perform sanitization or hashing of keys and collection names. Changes: - Updated SerializationAdapter.dump_dict() to accept optional key, collection, and version parameters - Updated SerializationAdapter.dump_json() to pass through these parameters - Added version field (default: 1) to all serialized documents - Updated all store implementations to pass key and collection when serializing entries - Updated Elasticsearch mapping to include version field (integer type) - Fixed MongoDB _setup_collection to use sanitized collection name in filter - Fixed MongoDB _delete_collection to properly remove collection from cache Stores updated: - Elasticsearch, MongoDB, Keyring, WindowsRegistry, Memcached (sanitize keys/collections) - DynamoDB, Redis, RocksDB, Simple, Valkey, Vault, Disk (consistency) Resolves #203 Co-authored-by: William Easton <strawgate@users.noreply.github.com>
1 parent 7a1780b commit 339f258

File tree

25 files changed

+108
-45
lines changed

25 files changed

+108
-45
lines changed

key-value/key-value-aio/src/key_value/aio/stores/disk/multi_store.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,11 @@ async def _put_managed_entry(
132132
collection: str,
133133
managed_entry: ManagedEntry,
134134
) -> None:
135-
_ = self._cache[collection].set(key=key, value=self._serialization_adapter.dump_json(entry=managed_entry), expire=managed_entry.ttl)
135+
_ = self._cache[collection].set(
136+
key=key,
137+
value=self._serialization_adapter.dump_json(entry=managed_entry, key=key, collection=collection),
138+
expire=managed_entry.ttl,
139+
)
136140

137141
@override
138142
async def _delete_managed_entry(self, *, key: str, collection: str) -> bool:

key-value/key-value-aio/src/key_value/aio/stores/disk/store.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ async def _put_managed_entry(
107107
) -> None:
108108
combo_key: str = compound_key(collection=collection, key=key)
109109

110-
_ = self._cache.set(key=combo_key, value=self._serialization_adapter.dump_json(entry=managed_entry), expire=managed_entry.ttl)
110+
_ = self._cache.set(
111+
key=combo_key,
112+
value=self._serialization_adapter.dump_json(entry=managed_entry, key=key, collection=collection),
113+
expire=managed_entry.ttl,
114+
)
111115

112116
@override
113117
async def _delete_managed_entry(self, *, key: str, collection: str) -> bool:

key-value/key-value-aio/src/key_value/aio/stores/dynamodb/store.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ async def _put_managed_entry(
219219
managed_entry: ManagedEntry,
220220
) -> None:
221221
"""Store a managed entry in DynamoDB."""
222-
json_value = self._serialization_adapter.dump_json(entry=managed_entry)
222+
json_value = self._serialization_adapter.dump_json(entry=managed_entry, key=key, collection=collection)
223223

224224
item: dict[str, Any] = {
225225
"collection": {"S": collection},

key-value/key-value-aio/src/key_value/aio/stores/elasticsearch/store.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@
6767
"key": {
6868
"type": "keyword",
6969
},
70+
"version": {
71+
"type": "integer",
72+
},
7073
"value": {
7174
"properties": {
7275
"flattened": {
@@ -357,7 +360,7 @@ async def _put_managed_entry(
357360
index_name: str = self._get_index_name(collection=collection)
358361
document_id: str = self._get_document_id(key=key)
359362

360-
document: dict[str, Any] = self._serializer.dump_dict(entry=managed_entry)
363+
document: dict[str, Any] = self._serializer.dump_dict(entry=managed_entry, key=key, collection=collection)
361364

362365
try:
363366
_ = await self._client.index(
@@ -395,7 +398,7 @@ async def _put_managed_entries(
395398

396399
index_action: dict[str, Any] = new_bulk_action(action="index", index=index_name, document_id=document_id)
397400

398-
document: dict[str, Any] = self._serializer.dump_dict(entry=managed_entry)
401+
document: dict[str, Any] = self._serializer.dump_dict(entry=managed_entry, key=key, collection=collection)
399402

400403
operations.extend([index_action, document])
401404

key-value/key-value-aio/src/key_value/aio/stores/keyring/store.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ async def _put_managed_entry(self, *, key: str, collection: str, managed_entry:
105105

106106
combo_key: str = compound_key(collection=sanitized_collection, key=sanitized_key)
107107

108-
json_str: str = self._serialization_adapter.dump_json(entry=managed_entry)
108+
json_str: str = self._serialization_adapter.dump_json(entry=managed_entry, key=key, collection=collection)
109109

110110
keyring.set_password(service_name=self._service_name, username=combo_key, password=json_str)
111111

key-value/key-value-aio/src/key_value/aio/stores/memcached/store.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ async def _put_managed_entry(
125125
else:
126126
exptime = max(int(managed_entry.ttl), 1)
127127

128-
json_value: str = self._serialization_adapter.dump_json(entry=managed_entry)
128+
json_value: str = self._serialization_adapter.dump_json(entry=managed_entry, key=key, collection=collection)
129129

130130
_ = await self._client.set(
131131
key=combo_key.encode(encoding="utf-8"),

key-value/key-value-aio/src/key_value/aio/stores/mongodb/store.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ async def _setup_collection(self, *, collection: str) -> None:
219219
# Ensure index on the unique combo key and supporting queries
220220
sanitized_collection = self._sanitize_collection(collection=collection)
221221

222-
collection_filter: dict[str, str] = {"name": collection}
222+
collection_filter: dict[str, str] = {"name": sanitized_collection}
223223
matching_collections: list[str] = await self._db.list_collection_names(filter=collection_filter)
224224

225225
if matching_collections:
@@ -273,7 +273,7 @@ async def _put_managed_entry(
273273
collection: str,
274274
managed_entry: ManagedEntry,
275275
) -> None:
276-
mongo_doc = self._adapter.dump_dict(entry=managed_entry)
276+
mongo_doc = self._adapter.dump_dict(entry=managed_entry, key=key, collection=collection)
277277

278278
try:
279279
# Ensure that the value is serializable to JSON
@@ -308,7 +308,7 @@ async def _put_managed_entries(
308308

309309
operations: list[UpdateOne] = []
310310
for key, managed_entry in zip(keys, managed_entries, strict=True):
311-
mongo_doc = self._adapter.dump_dict(entry=managed_entry)
311+
mongo_doc = self._adapter.dump_dict(entry=managed_entry, key=key, collection=collection)
312312

313313
operations.append(
314314
UpdateOne(
@@ -346,8 +346,7 @@ async def _delete_collection(self, *, collection: str) -> bool:
346346

347347
_ = await self._db.drop_collection(name_or_collection=collection_name)
348348

349-
if collection_name in self._collections_by_name:
350-
del self._collections_by_name[collection]
349+
self._collections_by_name.pop(collection, None)
351350

352351
return True
353352

key-value/key-value-aio/src/key_value/aio/stores/redis/store.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ async def _put_managed_entry(
132132
) -> None:
133133
combo_key: str = compound_key(collection=collection, key=key)
134134

135-
json_value: str = self._adapter.dump_json(entry=managed_entry)
135+
json_value: str = self._adapter.dump_json(entry=managed_entry, key=key, collection=collection)
136136

137137
if managed_entry.ttl is not None:
138138
# Redis does not support <= 0 TTLs
@@ -160,7 +160,7 @@ async def _put_managed_entries(
160160
# If there is no TTL, we can just do a simple mset
161161
mapping: dict[str, str] = {}
162162
for key, managed_entry in zip(keys, managed_entries, strict=True):
163-
json_value = self._adapter.dump_json(entry=managed_entry)
163+
json_value = self._adapter.dump_json(entry=managed_entry, key=key, collection=collection)
164164
mapping[compound_key(collection=collection, key=key)] = json_value
165165

166166
await self._client.mset(mapping=mapping)
@@ -175,7 +175,7 @@ async def _put_managed_entries(
175175

176176
for key, managed_entry in zip(keys, managed_entries, strict=True):
177177
combo_key: str = compound_key(collection=collection, key=key)
178-
json_value = self._adapter.dump_json(entry=managed_entry)
178+
json_value = self._adapter.dump_json(entry=managed_entry, key=key, collection=collection)
179179

180180
pipeline.setex(name=combo_key, time=ttl_seconds, value=json_value)
181181

key-value/key-value-aio/src/key_value/aio/stores/rocksdb/store.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ async def _put_managed_entry(
124124
self._fail_on_closed_store()
125125

126126
combo_key: str = compound_key(collection=collection, key=key)
127-
json_value: str = self._serialization_adapter.dump_json(entry=managed_entry)
127+
json_value: str = self._serialization_adapter.dump_json(entry=managed_entry, key=key, collection=collection)
128128

129129
self._db[combo_key] = json_value.encode("utf-8")
130130

@@ -147,7 +147,7 @@ async def _put_managed_entries(
147147
batch = WriteBatch()
148148
for key, managed_entry in zip(keys, managed_entries, strict=True):
149149
combo_key: str = compound_key(collection=collection, key=key)
150-
json_value: str = self._serialization_adapter.dump_json(entry=managed_entry)
150+
json_value: str = self._serialization_adapter.dump_json(entry=managed_entry, key=key, collection=collection)
151151
batch.put(combo_key, json_value.encode("utf-8"))
152152

153153
self._db.write(batch)

key-value/key-value-aio/src/key_value/aio/stores/simple/store.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ async def _put_managed_entry(self, *, key: str, collection: str, managed_entry:
6969
_ = self._data.pop(next(iter(self._data)))
7070

7171
self._data[combo_key] = SimpleStoreEntry(
72-
json_str=self._serialization_adapter.dump_json(entry=managed_entry),
72+
json_str=self._serialization_adapter.dump_json(entry=managed_entry, key=key, collection=collection),
7373
expires_at=managed_entry.expires_at,
7474
created_at=managed_entry.created_at,
7575
)

0 commit comments

Comments
 (0)