Skip to content

Commit 9fc051a

Browse files
committed
Doc and version bump
1 parent 369e9a0 commit 9fc051a

File tree

3 files changed

+222
-20
lines changed

3 files changed

+222
-20
lines changed

README.md

+173-18
Original file line numberDiff line numberDiff line change
@@ -168,47 +168,114 @@ of flags, and features, but are very low level for general use.
168168
def meta_multiget(
169169
self,
170170
keys: List[Key],
171-
flags: Optional[Set[Flag]] = None,
172-
int_flags: Optional[Dict[IntFlag, int]] = None,
173-
token_flags: Optional[Dict[TokenFlag, bytes]] = None,
171+
flags: Optional[RequestFlags] = None,
172+
failure_handling: FailureHandling = DEFAULT_FAILURE_HANDLING,
174173
) -> Dict[Key, ReadResponse]:
175174
176175
def meta_get(
177176
self,
178177
key: Key,
179-
flags: Optional[Set[Flag]] = None,
180-
int_flags: Optional[Dict[IntFlag, int]] = None,
181-
token_flags: Optional[Dict[TokenFlag, bytes]] = None,
178+
flags: Optional[RequestFlags] = None,
179+
failure_handling: FailureHandling = DEFAULT_FAILURE_HANDLING,
182180
) -> ReadResponse:
183181
184182
def meta_set(
185183
self,
186184
key: Key,
187185
value: Any,
188186
ttl: int,
189-
flags: Optional[Set[Flag]] = None,
190-
int_flags: Optional[Dict[IntFlag, int]] = None,
191-
token_flags: Optional[Dict[TokenFlag, bytes]] = None,
187+
flags: Optional[RequestFlags] = None,
188+
failure_handling: FailureHandling = DEFAULT_FAILURE_HANDLING,
192189
) -> WriteResponse:
193190
194191
def meta_delete(
195192
self,
196193
key: Key,
197-
flags: Optional[Set[Flag]] = None,
198-
int_flags: Optional[Dict[IntFlag, int]] = None,
199-
token_flags: Optional[Dict[TokenFlag, bytes]] = None,
194+
flags: Optional[RequestFlags] = None,
195+
failure_handling: FailureHandling = DEFAULT_FAILURE_HANDLING,
200196
) -> WriteResponse:
201197
202198
def meta_arithmetic(
203199
self,
204200
key: Key,
205-
flags: Optional[Set[Flag]] = None,
206-
int_flags: Optional[Dict[IntFlag, int]] = None,
207-
token_flags: Optional[Dict[TokenFlag, bytes]] = None,
201+
flags: Optional[RequestFlags] = None,
202+
failure_handling: FailureHandling = DEFAULT_FAILURE_HANDLING,
208203
) -> WriteResponse:
209204
```
210-
211-
You won't use this api unless you are implementing some custom high-level
205+
### Special arguments:
206+
`RequestFlags` has the following arguments:
207+
* `no_reply`: Set to True if the server should not send a response
208+
* `return_client_flag`: Set to True if the server should return the client flag
209+
* `return_cas_token`: Set to True if the server should return the CAS token
210+
* `return_value`: Set to True if the server should return the value (Default)
211+
* `return_ttl`: Set to True if the server should return the TTL
212+
* `return_size`: Set to True if the server should return the size (useful if when paired with return_value=False, to get the size of the value)
213+
* `return_last_access`: Set to True if the server should return the last access time
214+
* `return_fetched`: Set to True if the server should return the fetched flag
215+
* `return_key`: Set to True if the server should return the key in the response
216+
* `no_update_lru`: Set to True if the server should not update the LRU on this access
217+
* `mark_stale`: Set to True if the server should mark the value as stale
218+
* `cache_ttl`: The TTL to set on the key
219+
* `recache_ttl`: The TTL to use for recache policy
220+
* `vivify_on_miss_ttl`: The TTL to use when vivifying a value on a miss
221+
* `client_flag`: The client flag to store along the value (Useful to store value type, compression, etc)
222+
* `ma_initial_value`: For arithmetic operations, the initial value to use (if the key does not exist)
223+
* `ma_delta_value`: For arithmetic operations, the delta value to use
224+
* `cas_token`: The CAS token to use when storing the value in the cache
225+
* `opaque`: The opaque flag (will be echoed back in the response)
226+
* `mode`: The mode to use when storing the value in the cache. See SET_MODE_* and MA_MODE_* constants
227+
228+
`FailureHandling` controls how the failures are handled. Has the arguments:
229+
* `raise_on_server_error`: (`Optional[bool]`) Wether to raise on error:
230+
- `True`: Raises on server errors
231+
- `False`: Returns miss for reads and false on writes
232+
- `None` (DEFAULT): Use the raise on error setting configured in the Router
233+
* `track_write_failures``: (`bool`) Wether to track failures:
234+
- `True` (DEFAULT): Track write failures
235+
- `False`: Do not notify write failures
236+
237+
The default settings are usually good, but there are situations when you want control.
238+
For example, a refill (populating an entry that was missing on cache) doesn't need to
239+
track write failures. If fails to be written, the cache will still be empty, so no need
240+
to track that as a write failure. Similarly sometimes you need to know if a write failed
241+
due to CAS semantics, or because it was an add vs when it is due to server failure.
242+
243+
### Responses:
244+
The responses are either:
245+
* `ReadResponse`: `Union[Miss, Value, Success]`
246+
* `WriteResponse`: `Union[Success, NotStored, Conflict, Miss]`
247+
248+
Which are:
249+
* `Miss`: For key not found. No arguments
250+
* `Success`: Successfull operation
251+
- `flags`: `ResponseFlags`
252+
* `Value`: For value responses
253+
- `flags`: `ResponseFlags`
254+
- `size`: `int` Size of the value
255+
- `value`: `Any` The value
256+
* `NotStored`: Not stored, for example "add" on exising key. No arguments.
257+
* `Conflict`: Not stored, for example due to CAS mismatch. No arguments.
258+
259+
The `ResponseFlags` contains the all the returned flags. This metadata gives a lot of
260+
control and posibilities, it is the strength of the meta protocol:
261+
* `cas_token`: Compare-And-Swap token (integer value) or `None` if not returned
262+
* `fetched`:
263+
- `True` if fetched since being set
264+
- `False` if not fetched since being set
265+
- `None` if the server did not return this flag info
266+
* `last_access`: time in seconds since last access (integer value) or `None` if not returned
267+
* `ttl`: time in seconds until the value expires (integer value) or `None` if not returned
268+
- The special value `-1` represents if the key will never expire
269+
* `client_flag`: integer value or `None` if not returned
270+
* `win`:
271+
- `True` if the client won the right to repopulate
272+
- `False` if the client lost the right to repopulate
273+
- `None` if the server did not return a win/lose flag
274+
* `stale`: `True` if the value is stale, `False` otherwise
275+
* `real_size`: integer value or `None` if not returned
276+
* `opaque flag`: bytes value or `None` if not returned
277+
278+
NOTE: You shouldn't use this api directly, unless you are implementing some custom high-level
212279
command. See below for the usual memcache api.
213280

214281
## High level commands:
@@ -233,6 +300,33 @@ Invalidation...
233300
stale_policy: Optional[StalePolicy] = None,
234301
set_mode: SetMode = SetMode.SET, # Other are ADD, REPLACE, APPEND...
235302
) -> bool:
303+
"""
304+
Write a value using the specified `set_mode`
305+
"""
306+
307+
def refill(
308+
self: HighLevelCommandMixinWithMetaCommands,
309+
key: Union[Key, str],
310+
value: Any,
311+
ttl: int,
312+
no_reply: bool = False,
313+
) -> bool:
314+
"""
315+
Try to refill a value.
316+
317+
Use this method when you got a cache miss, read from DB and
318+
are trying to refill the value.
319+
320+
DO NOT USE to write new state.
321+
322+
It will:
323+
* use "ADD" mode, so it will fail if the value is already
324+
present in cache.
325+
* It will also disable write failure tracking. The write
326+
failure tracking is often used to invalidate keys that
327+
fail to be written. Since this is not writting new state,
328+
there is no need to track failures.
329+
"""
236330
237331
def delete(
238332
self,
@@ -241,6 +335,12 @@ Invalidation...
241335
no_reply: bool = False,
242336
stale_policy: Optional[StalePolicy] = None,
243337
) -> bool:
338+
"""
339+
Returns True if the key existed and it was deleted.
340+
If the key is not found in the cache it will return False. If
341+
you just want to the key to be deleted not caring of whether
342+
it exists or not, use invalidate() instead.
343+
"""
244344
245345
def invalidate(
246346
self,
@@ -249,13 +349,19 @@ Invalidation...
249349
no_reply: bool = False,
250350
stale_policy: Optional[StalePolicy] = None,
251351
) -> bool:
352+
"""
353+
Returns true of the key deleted or it didn't exist anyway
354+
"""
252355
253356
def touch(
254357
self,
255358
key: Union[Key, str],
256359
ttl: int,
257360
no_reply: bool = False,
258361
) -> bool:
362+
"""
363+
Modify the TTL of a key without retrieving the value
364+
"""
259365
260366
def get_or_lease(
261367
self,
@@ -264,6 +370,13 @@ Invalidation...
264370
touch_ttl: Optional[int] = None,
265371
recache_policy: Optional[RecachePolicy] = None,
266372
) -> Optional[Any]:
373+
"""
374+
Get a key. On miss try to get a lease.
375+
376+
Guarantees only one cache client will get the miss and
377+
gets to repopulate cache, while the others are blocked
378+
waiting (according to the settings in the LeasePolicy)
379+
"""
267380
268381
def get_or_lease_cas(
269382
self,
@@ -272,27 +385,41 @@ Invalidation...
272385
touch_ttl: Optional[int] = None,
273386
recache_policy: Optional[RecachePolicy] = None,
274387
) -> Tuple[Optional[Any], Optional[int]]:
388+
"""
389+
Same as get_or_lease(), but also return the CAS token so
390+
it can be used during writes and detect races
391+
"""
275392
276393
def get(
277394
self,
278395
key: Union[Key, str],
279396
touch_ttl: Optional[int] = None,
280397
recache_policy: Optional[RecachePolicy] = None,
281398
) -> Optional[Any]:
399+
"""
400+
Get a key
401+
"""
282402
283403
def multi_get(
284404
self,
285405
keys: List[Union[Key, str]],
286406
touch_ttl: Optional[int] = None,
287407
recache_policy: Optional[RecachePolicy] = None,
288408
) -> Dict[Key, Optional[Any]]:
409+
"""
410+
Get multiple keys at once
411+
"""
289412
290413
def get_cas(
291414
self,
292415
key: Union[Key, str],
293416
touch_ttl: Optional[int] = None,
294417
recache_policy: Optional[RecachePolicy] = None,
295418
) -> Tuple[Optional[Any], Optional[int]]:
419+
"""
420+
Same as get(), but also return the CAS token so
421+
it can be used during writes and detect races
422+
"""
296423
297424
def get_typed(
298425
self,
@@ -302,6 +429,9 @@ Invalidation...
302429
recache_policy: Optional[RecachePolicy] = None,
303430
error_on_type_mismatch: bool = False,
304431
) -> Optional[T]:
432+
"""
433+
Same as get(), but ensure the type matched the provided cls
434+
"""
305435
306436
def get_cas_typed(
307437
self,
@@ -311,6 +441,10 @@ Invalidation...
311441
recache_policy: Optional[RecachePolicy] = None,
312442
error_on_type_mismatch: bool = False,
313443
) -> Tuple[Optional[T], Optional[int]]:
444+
"""
445+
Same as get_typed(), but also return the CAS token so
446+
it can be used during writes and detect races
447+
"""
314448
315449
def delta(
316450
self,
@@ -320,6 +454,9 @@ Invalidation...
320454
no_reply: bool = False,
321455
cas_token: Optional[int] = None,
322456
) -> bool:
457+
"""
458+
Increment/Decrement a key that contains a counter
459+
"""
323460
324461
def delta_initialize(
325462
self,
@@ -331,6 +468,11 @@ Invalidation...
331468
no_reply: bool = False,
332469
cas_token: Optional[int] = None,
333470
) -> bool:
471+
"""
472+
Increment/Decrement a key that contains a counter,
473+
creating and setting it to the initial value if the
474+
counter does not exist.
475+
"""
334476
335477
def delta_and_get(
336478
self,
@@ -339,6 +481,9 @@ Invalidation...
339481
refresh_ttl: Optional[int] = None,
340482
cas_token: Optional[int] = None,
341483
) -> Optional[int]:
484+
"""
485+
Same as delta(), but return the resulting value
486+
"""
342487
343488
def delta_initialize_and_get(
344489
self,
@@ -349,9 +494,19 @@ Invalidation...
349494
refresh_ttl: Optional[int] = None,
350495
cas_token: Optional[int] = None,
351496
) -> Optional[int]:
497+
"""
498+
Same as delta_initialize(), but return the resulting value
499+
"""
352500
```
353501

354-
# Anti-dogpiling techniques
502+
# Reliability, consistency and best practices
503+
We have published a deep-dive into some of the techniques to keep
504+
cache consistent and reliable under high load that RevenueCat uses,
505+
available thanks to this cache client.
506+
507+
See: https://www.revenuecat.com/blog/engineering/data-caching-revenuecat/
508+
509+
## Anti-dogpiling, preventing thundering herds:
355510
Some commands receive `RecachePolicy`, `StalePolicy` and `LeasePolicy` for the
356511
advanced anti-dogpiling control needed in high-qps environments:
357512

pyproject.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "meta-memcache"
3-
version = "2.0.0a1"
3+
version = "2.0.0"
44
description = "Modern, pure python, memcache client with support for new meta commands."
55
license = "MIT"
66
readme = "README.md"
@@ -9,7 +9,6 @@ repository = "https://github.com/RevenueCat/meta-memcache-py"
99
authors = ["Guillermo Perez <bisho@revenuecat.com>"]
1010
packages = [{include = "meta_memcache", from="src"}]
1111

12-
1312
[tool.poetry.dependencies]
1413
python = "^3.8"
1514
uhashring = "^2.1"

0 commit comments

Comments
 (0)