|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +import asyncio |
| 4 | + |
| 5 | +from zarr.storage._wrapper import WrapperStore |
| 6 | +from zarr.abc.store import ByteRequest, Store |
| 7 | +from zarr.core.buffer import Buffer, BufferPrototype |
| 8 | + |
| 9 | + |
| 10 | +class LatencyStore(WrapperStore[Store]): |
| 11 | + """ |
| 12 | + A wrapper class that takes any store class in its constructor and |
| 13 | + adds latency to the `set` and `get` methods. This can be used for |
| 14 | + performance testing. |
| 15 | +
|
| 16 | + Particularly useful for testing downstream applications which will |
| 17 | + interact with a high-latency zarr store implementation, |
| 18 | + such as one which read from or writes to remote object storage. |
| 19 | + For example, by using this class to wrap a ``MemoryStore`` instance, |
| 20 | + you can (crudely) simulate the latency of reading and writing from S3 |
| 21 | + without having to actually use the network, or a mock like MinIO. |
| 22 | +
|
| 23 | + Parameters |
| 24 | + ---------- |
| 25 | + store : Store |
| 26 | + Store to wrap |
| 27 | + get_latency : float |
| 28 | + Amount of latency to add to each get call, in seconds. Default is 0. |
| 29 | + set_latency : float |
| 30 | + Amount of latency to add to each set call, in seconds. Default is 0. |
| 31 | + """ |
| 32 | + |
| 33 | + get_latency: float |
| 34 | + set_latency: float |
| 35 | + |
| 36 | + def __init__(self, cls: Store, *, get_latency: float = 0, set_latency: float = 0) -> None: |
| 37 | + self.get_latency = float(get_latency) |
| 38 | + self.set_latency = float(set_latency) |
| 39 | + self._store = cls |
| 40 | + |
| 41 | + async def set(self, key: str, value: Buffer) -> None: |
| 42 | + """ |
| 43 | + Add latency to the ``set`` method. |
| 44 | +
|
| 45 | + Calls ``asyncio.sleep(self.set_latency)`` before invoking the wrapped ``set`` method. |
| 46 | +
|
| 47 | + Parameters |
| 48 | + ---------- |
| 49 | + key : str |
| 50 | + The key to set |
| 51 | + value : Buffer |
| 52 | + The value to set |
| 53 | +
|
| 54 | + Returns |
| 55 | + ------- |
| 56 | + None |
| 57 | + """ |
| 58 | + await asyncio.sleep(self.set_latency) |
| 59 | + await self._store.set(key, value) |
| 60 | + |
| 61 | + async def get( |
| 62 | + self, key: str, prototype: BufferPrototype, byte_range: ByteRequest | None = None |
| 63 | + ) -> Buffer | None: |
| 64 | + """ |
| 65 | + Add latency to the ``get`` method. |
| 66 | +
|
| 67 | + Calls ``asyncio.sleep(self.get_latency)`` before invoking the wrapped ``get`` method. |
| 68 | +
|
| 69 | + Parameters |
| 70 | + ---------- |
| 71 | + key : str |
| 72 | + The key to get |
| 73 | + prototype : BufferPrototype |
| 74 | + The BufferPrototype to use. |
| 75 | + byte_range : ByteRequest, optional |
| 76 | + An optional byte range. |
| 77 | +
|
| 78 | + Returns |
| 79 | + ------- |
| 80 | + buffer : Buffer or None |
| 81 | + """ |
| 82 | + await asyncio.sleep(self.get_latency) |
| 83 | + return await self._store.get(key, prototype=prototype, byte_range=byte_range) |
0 commit comments