Skip to content

Commit 4076872

Browse files
refactor: pass ttl and timestamps as parameters to _put_managed_entries
Instead of extracting TTL and timestamps from managed entries, these values are now passed as explicit parameters to _put_managed_entries. This avoids the need to inspect managed entries to extract metadata. Changes: - Base store: Added ttl, created_at, expires_at parameters to signature - Base store put_many: Pre-calculates timestamps once for all entries - Redis store: Uses ttl parameter instead of extracting from first entry - MongoDB store: Uses timestamp parameters instead of extracting from first entry - Elasticsearch store: Uses timestamp parameters instead of extracting from first entry - RocksDB store: Updated signature (stores full JSON, doesn't use params) Co-authored-by: William Easton <[email protected]>
1 parent e4e9b03 commit 4076872

File tree

5 files changed

+86
-27
lines changed

5 files changed

+86
-27
lines changed

key-value/key-value-aio/src/key_value/aio/stores/base.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from asyncio.locks import Lock
77
from collections import defaultdict
88
from collections.abc import Mapping, Sequence
9+
from datetime import datetime, timedelta
910
from types import MappingProxyType, TracebackType
1011
from typing import Any, SupportsFloat
1112

@@ -206,9 +207,26 @@ async def _put_managed_entry(self, *, collection: str, key: str, managed_entry:
206207
"""Store a managed entry by key in the specified collection."""
207208
...
208209

209-
async def _put_managed_entries(self, *, collection: str, keys: Sequence[str], managed_entries: Sequence[ManagedEntry]) -> None:
210-
"""Store multiple managed entries by key in the specified collection."""
210+
async def _put_managed_entries(
211+
self,
212+
*,
213+
collection: str,
214+
keys: Sequence[str],
215+
managed_entries: Sequence[ManagedEntry],
216+
ttl: float | None, # noqa: ARG002
217+
created_at: datetime, # noqa: ARG002
218+
expires_at: datetime | None, # noqa: ARG002
219+
) -> None:
220+
"""Store multiple managed entries by key in the specified collection.
211221
222+
Args:
223+
collection: The collection to store entries in
224+
keys: The keys for the entries
225+
managed_entries: The managed entries to store
226+
ttl: The TTL in seconds (None for no expiration)
227+
created_at: The creation timestamp for all entries
228+
expires_at: The expiration timestamp for all entries (None if no TTL)
229+
"""
212230
for key, managed_entry in zip(keys, managed_entries, strict=True):
213231
await self._put_managed_entry(
214232
collection=collection,
@@ -261,9 +279,20 @@ async def put_many(
261279

262280
keys, values, ttl_for_entries = self._prepare_put_many(keys=keys, values=values, ttl=ttl)
263281

264-
managed_entries: list[ManagedEntry] = [ManagedEntry(value=value, ttl=ttl_for_entries, created_at=now()) for value in values]
282+
# Pre-calculate timestamps once for all entries
283+
created_at = now()
284+
expires_at = created_at + timedelta(seconds=ttl_for_entries) if ttl_for_entries is not None else None
285+
286+
managed_entries: list[ManagedEntry] = [ManagedEntry(value=value, ttl=ttl_for_entries, created_at=created_at) for value in values]
265287

266-
await self._put_managed_entries(collection=collection, keys=keys, managed_entries=managed_entries)
288+
await self._put_managed_entries(
289+
collection=collection,
290+
keys=keys,
291+
managed_entries=managed_entries,
292+
ttl=ttl_for_entries,
293+
created_at=created_at,
294+
expires_at=expires_at,
295+
)
267296

268297
@abstractmethod
269298
async def _delete_managed_entry(self, *, key: str, collection: str) -> bool:

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

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from collections.abc import Sequence
2+
from datetime import datetime
23
from typing import TYPE_CHECKING, Any, overload
34

45
from key_value.shared.utils.compound import compound_key
@@ -35,8 +36,6 @@
3536
raise ImportError(msg) from e
3637

3738
if TYPE_CHECKING:
38-
from datetime import datetime
39-
4039
from elastic_transport import ObjectApiResponse
4140

4241
DEFAULT_INDEX_PREFIX = "kv_store"
@@ -267,15 +266,22 @@ async def _put_managed_entry(
267266
)
268267

269268
@override
270-
async def _put_managed_entries(self, *, collection: str, keys: Sequence[str], managed_entries: Sequence[ManagedEntry]) -> None:
269+
async def _put_managed_entries(
270+
self,
271+
*,
272+
collection: str,
273+
keys: Sequence[str],
274+
managed_entries: Sequence[ManagedEntry],
275+
ttl: float | None,
276+
created_at: datetime,
277+
expires_at: datetime | None,
278+
) -> None:
271279
if not keys:
272280
return
273281

274-
# All entries in a batch have the same timestamps (from BaseStore.put_many)
275-
# Extract timestamps once from the first entry
276-
first_entry = managed_entries[0]
277-
created_at_iso: str | None = first_entry.created_at.isoformat() if first_entry.created_at else None
278-
expires_at_iso: str | None = first_entry.expires_at.isoformat() if first_entry.expires_at else None
282+
# Convert timestamps to ISO format for Elasticsearch
283+
created_at_iso: str = created_at.isoformat()
284+
expires_at_iso: str | None = expires_at.isoformat() if expires_at else None
279285

280286
# Use bulk API for efficient batch indexing
281287
operations: list[dict[str, Any]] = []
@@ -286,10 +292,9 @@ async def _put_managed_entries(self, *, collection: str, keys: Sequence[str], ma
286292
"collection": collection,
287293
"key": key,
288294
"value": managed_entry.to_json(include_metadata=False),
295+
"created_at": created_at_iso,
289296
}
290297

291-
if created_at_iso:
292-
document["created_at"] = created_at_iso
293298
if expires_at_iso:
294299
document["expires_at"] = expires_at_iso
295300

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

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,17 +203,24 @@ async def _put_managed_entry(
203203
)
204204

205205
@override
206-
async def _put_managed_entries(self, *, collection: str, keys: Sequence[str], managed_entries: Sequence[ManagedEntry]) -> None:
206+
async def _put_managed_entries(
207+
self,
208+
*,
209+
collection: str,
210+
keys: Sequence[str],
211+
managed_entries: Sequence[ManagedEntry],
212+
ttl: float | None,
213+
created_at: datetime,
214+
expires_at: datetime | None,
215+
) -> None:
207216
if not keys:
208217
return
209218

210219
collection = self._sanitize_collection_name(collection=collection)
211220

212-
# All entries in a batch have the same timestamps (from BaseStore.put_many)
213-
# Extract timestamps once from the first entry
214-
first_entry = managed_entries[0]
215-
created_at_iso: str | None = first_entry.created_at.isoformat() if first_entry.created_at else None
216-
expires_at_iso: str | None = first_entry.expires_at.isoformat() if first_entry.expires_at else None
221+
# Convert timestamps to ISO format for MongoDB
222+
created_at_iso: str = created_at.isoformat()
223+
expires_at_iso: str | None = expires_at.isoformat() if expires_at else None
217224
updated_at_iso: str = now().isoformat()
218225

219226
# Use bulk_write for efficient batch operations

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from collections.abc import Sequence
2+
from datetime import datetime
23
from typing import Any, overload
34
from urllib.parse import urlparse
45

@@ -134,17 +135,24 @@ async def _put_managed_entry(
134135
_ = await self._client.set(name=combo_key, value=json_value) # pyright: ignore[reportAny]
135136

136137
@override
137-
async def _put_managed_entries(self, *, collection: str, keys: Sequence[str], managed_entries: Sequence[ManagedEntry]) -> None:
138+
async def _put_managed_entries(
139+
self,
140+
*,
141+
collection: str,
142+
keys: Sequence[str],
143+
managed_entries: Sequence[ManagedEntry],
144+
ttl: float | None,
145+
created_at: datetime,
146+
expires_at: datetime | None,
147+
) -> None:
138148
if not keys:
139149
return
140150

141-
# All entries in a batch have the same TTL (from BaseStore.put_many)
142-
# Extract TTL once from the first entry
143-
first_entry = managed_entries[0]
151+
# Convert TTL to integer seconds for Redis
144152
ttl_seconds: int | None = None
145-
if first_entry.ttl is not None:
153+
if ttl is not None:
146154
# Redis does not support <= 0 TTLs
147-
ttl_seconds = max(int(first_entry.ttl), 1)
155+
ttl_seconds = max(int(ttl), 1)
148156

149157
# Use pipeline for bulk operations
150158
pipeline = self._client.pipeline()

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from collections.abc import Sequence
2+
from datetime import datetime
23
from pathlib import Path
34
from typing import Any, overload
45

@@ -128,7 +129,16 @@ async def _put_managed_entry(
128129
self._db[combo_key] = json_value.encode("utf-8")
129130

130131
@override
131-
async def _put_managed_entries(self, *, collection: str, keys: Sequence[str], managed_entries: Sequence[ManagedEntry]) -> None:
132+
async def _put_managed_entries(
133+
self,
134+
*,
135+
collection: str,
136+
keys: Sequence[str],
137+
managed_entries: Sequence[ManagedEntry],
138+
ttl: float | None,
139+
created_at: datetime,
140+
expires_at: datetime | None,
141+
) -> None:
132142
self._fail_on_closed_store()
133143

134144
if not keys:

0 commit comments

Comments
 (0)