meta-memcache 1.1.2.dev5__tar.gz → 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/PKG-INFO +192 -23
  2. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/README.md +189 -22
  3. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/pyproject.toml +4 -3
  4. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/__init__.py +2 -3
  5. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/base/base_serializer.py +2 -1
  6. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/cache_client.py +4 -4
  7. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/commands/high_level_commands.py +138 -134
  8. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/commands/meta_commands.py +7 -29
  9. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/configuration.py +10 -10
  10. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/connection/memcache_socket.py +46 -89
  11. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/executors/default.py +48 -67
  12. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/extras/client_wrapper.py +7 -29
  13. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/extras/migrating_cache_client.py +12 -44
  14. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/extras/probabilistic_hot_cache.py +5 -4
  15. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/interfaces/executor.py +5 -10
  16. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/interfaces/meta_commands.py +7 -19
  17. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/interfaces/router.py +5 -10
  18. meta_memcache-2.0.0/src/meta_memcache/protocol.py +129 -0
  19. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/routers/default.py +5 -14
  20. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/routers/ephemeral.py +7 -17
  21. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/routers/gutter.py +7 -21
  22. meta_memcache-2.0.0/src/meta_memcache/routers/helpers.py +24 -0
  23. meta_memcache-2.0.0/src/meta_memcache/serializer.py +229 -0
  24. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/settings.py +4 -1
  25. meta_memcache-1.1.2.dev5/src/meta_memcache/protocol.py +0 -198
  26. meta_memcache-1.1.2.dev5/src/meta_memcache/routers/helpers.py +0 -23
  27. meta_memcache-1.1.2.dev5/src/meta_memcache/serializer.py +0 -55
  28. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/LICENSE +0 -0
  29. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/base/__init__.py +0 -0
  30. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/base/base_cache_client.py +0 -0
  31. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/commands/__init__.py +0 -0
  32. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/connection/__init__.py +0 -0
  33. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/connection/pool.py +0 -0
  34. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/connection/providers.py +0 -0
  35. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/errors.py +0 -0
  36. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/events/__init__.py +0 -0
  37. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/events/write_failure_event.py +0 -0
  38. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/executors/__init__.py +0 -0
  39. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/extras/__init__.py +0 -0
  40. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/interfaces/__init__.py +0 -0
  41. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/interfaces/cache_api.py +0 -0
  42. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/interfaces/commands.py +0 -0
  43. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/interfaces/high_level_commands.py +0 -0
  44. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/metrics/__init__.py +0 -0
  45. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/metrics/base.py +0 -0
  46. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/metrics/prometheus.py +0 -0
  47. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/py.typed +0 -0
  48. {meta_memcache-1.1.2.dev5 → meta_memcache-2.0.0}/src/meta_memcache/routers/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: meta-memcache
3
- Version: 1.1.2.dev5
3
+ Version: 2.0.0
4
4
  Summary: Modern, pure python, memcache client with support for new meta commands.
5
5
  Home-page: https://github.com/RevenueCat/meta-memcache-py
6
6
  License: MIT
@@ -14,7 +14,9 @@ Classifier: Programming Language :: Python :: 3.9
14
14
  Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Requires-Dist: marisa-trie (>=1.0.0,<2.0.0)
17
+ Requires-Dist: meta-memcache-socket (==0.1.3)
17
18
  Requires-Dist: uhashring (>=2.1,<3.0)
19
+ Requires-Dist: zstandard (>=0.22.0,<0.23.0)
18
20
  Project-URL: Repository, https://github.com/RevenueCat/meta-memcache-py
19
21
  Description-Content-Type: text/markdown
20
22
 
@@ -117,14 +119,26 @@ will be gone or present, since they are stored in the same server). Note this is
117
119
  also risky, if you place all keys of a user in the same server, and the server
118
120
  goes down, the user life will be miserable.
119
121
 
120
- ### Unicode keys:
121
- Unicode keys are supported, the keys will be hashed according to Meta commands
122
+ ### Custom domains:
123
+ You can add a domain to keys. This domain can be used for custom per-domain
124
+ metrics like hit ratios or to control serialization of the values.
125
+ ```python:
126
+ Key("key:1:2", domain="example")
127
+ ```
128
+ For example the ZstdSerializer allows to configure different dictionaries by
129
+ domain, so you can compress more efficiently data of different domains.
130
+
131
+ ### Unicode/binary keys:
132
+ Both unicode and binary keys are supported, the keys will be hashed/encoded according to Meta commands
122
133
  [binary encoded keys](https://github.com/memcached/memcached/wiki/MetaCommands#binary-encoded-keys)
123
134
  specification.
124
135
 
125
- To use this, mark the key as unicode:
136
+ Using binary keys can have benefits, saving space in memory. While over the wire the key
137
+ is transmited b64 encoded, the memcache server will use the byte representation, so it will
138
+ not have the 1/4 overhead of b64 encoding.
139
+
126
140
  ```python:
127
- Key("🍺", unicode=True)
141
+ Key("🍺")
128
142
  ```
129
143
 
130
144
  ### Large keys:
@@ -176,17 +190,15 @@ of flags, and features, but are very low level for general use.
176
190
  def meta_multiget(
177
191
  self,
178
192
  keys: List[Key],
179
- flags: Optional[Set[Flag]] = None,
180
- int_flags: Optional[Dict[IntFlag, int]] = None,
181
- token_flags: Optional[Dict[TokenFlag, bytes]] = None,
193
+ flags: Optional[RequestFlags] = None,
194
+ failure_handling: FailureHandling = DEFAULT_FAILURE_HANDLING,
182
195
  ) -> Dict[Key, ReadResponse]:
183
196
 
184
197
  def meta_get(
185
198
  self,
186
199
  key: Key,
187
- flags: Optional[Set[Flag]] = None,
188
- int_flags: Optional[Dict[IntFlag, int]] = None,
189
- token_flags: Optional[Dict[TokenFlag, bytes]] = None,
200
+ flags: Optional[RequestFlags] = None,
201
+ failure_handling: FailureHandling = DEFAULT_FAILURE_HANDLING,
190
202
  ) -> ReadResponse:
191
203
 
192
204
  def meta_set(
@@ -194,29 +206,98 @@ of flags, and features, but are very low level for general use.
194
206
  key: Key,
195
207
  value: Any,
196
208
  ttl: int,
197
- flags: Optional[Set[Flag]] = None,
198
- int_flags: Optional[Dict[IntFlag, int]] = None,
199
- token_flags: Optional[Dict[TokenFlag, bytes]] = None,
209
+ flags: Optional[RequestFlags] = None,
210
+ failure_handling: FailureHandling = DEFAULT_FAILURE_HANDLING,
200
211
  ) -> WriteResponse:
201
212
 
202
213
  def meta_delete(
203
214
  self,
204
215
  key: Key,
205
- flags: Optional[Set[Flag]] = None,
206
- int_flags: Optional[Dict[IntFlag, int]] = None,
207
- token_flags: Optional[Dict[TokenFlag, bytes]] = None,
216
+ flags: Optional[RequestFlags] = None,
217
+ failure_handling: FailureHandling = DEFAULT_FAILURE_HANDLING,
208
218
  ) -> WriteResponse:
209
219
 
210
220
  def meta_arithmetic(
211
221
  self,
212
222
  key: Key,
213
- flags: Optional[Set[Flag]] = None,
214
- int_flags: Optional[Dict[IntFlag, int]] = None,
215
- token_flags: Optional[Dict[TokenFlag, bytes]] = None,
223
+ flags: Optional[RequestFlags] = None,
224
+ failure_handling: FailureHandling = DEFAULT_FAILURE_HANDLING,
216
225
  ) -> WriteResponse:
217
226
  ```
218
-
219
- You won't use this api unless you are implementing some custom high-level
227
+ ### Special arguments:
228
+ `RequestFlags` has the following arguments:
229
+ * `no_reply`: Set to True if the server should not send a response
230
+ * `return_client_flag`: Set to True if the server should return the client flag
231
+ * `return_cas_token`: Set to True if the server should return the CAS token
232
+ * `return_value`: Set to True if the server should return the value (Default)
233
+ * `return_ttl`: Set to True if the server should return the TTL
234
+ * `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)
235
+ * `return_last_access`: Set to True if the server should return the last access time
236
+ * `return_fetched`: Set to True if the server should return the fetched flag
237
+ * `return_key`: Set to True if the server should return the key in the response
238
+ * `no_update_lru`: Set to True if the server should not update the LRU on this access
239
+ * `mark_stale`: Set to True if the server should mark the value as stale
240
+ * `cache_ttl`: The TTL to set on the key
241
+ * `recache_ttl`: The TTL to use for recache policy
242
+ * `vivify_on_miss_ttl`: The TTL to use when vivifying a value on a miss
243
+ * `client_flag`: The client flag to store along the value (Useful to store value type, compression, etc)
244
+ * `ma_initial_value`: For arithmetic operations, the initial value to use (if the key does not exist)
245
+ * `ma_delta_value`: For arithmetic operations, the delta value to use
246
+ * `cas_token`: The CAS token to use when storing the value in the cache
247
+ * `opaque`: The opaque flag (will be echoed back in the response)
248
+ * `mode`: The mode to use when storing the value in the cache. See SET_MODE_* and MA_MODE_* constants
249
+
250
+ `FailureHandling` controls how the failures are handled. Has the arguments:
251
+ * `raise_on_server_error`: (`Optional[bool]`) Wether to raise on error:
252
+ - `True`: Raises on server errors
253
+ - `False`: Returns miss for reads and false on writes
254
+ - `None` (DEFAULT): Use the raise on error setting configured in the Router
255
+ * `track_write_failures``: (`bool`) Wether to track failures:
256
+ - `True` (DEFAULT): Track write failures
257
+ - `False`: Do not notify write failures
258
+
259
+ The default settings are usually good, but there are situations when you want control.
260
+ For example, a refill (populating an entry that was missing on cache) doesn't need to
261
+ track write failures. If fails to be written, the cache will still be empty, so no need
262
+ to track that as a write failure. Similarly sometimes you need to know if a write failed
263
+ due to CAS semantics, or because it was an add vs when it is due to server failure.
264
+
265
+ ### Responses:
266
+ The responses are either:
267
+ * `ReadResponse`: `Union[Miss, Value, Success]`
268
+ * `WriteResponse`: `Union[Success, NotStored, Conflict, Miss]`
269
+
270
+ Which are:
271
+ * `Miss`: For key not found. No arguments
272
+ * `Success`: Successfull operation
273
+ - `flags`: `ResponseFlags`
274
+ * `Value`: For value responses
275
+ - `flags`: `ResponseFlags`
276
+ - `size`: `int` Size of the value
277
+ - `value`: `Any` The value
278
+ * `NotStored`: Not stored, for example "add" on exising key. No arguments.
279
+ * `Conflict`: Not stored, for example due to CAS mismatch. No arguments.
280
+
281
+ The `ResponseFlags` contains the all the returned flags. This metadata gives a lot of
282
+ control and posibilities, it is the strength of the meta protocol:
283
+ * `cas_token`: Compare-And-Swap token (integer value) or `None` if not returned
284
+ * `fetched`:
285
+ - `True` if fetched since being set
286
+ - `False` if not fetched since being set
287
+ - `None` if the server did not return this flag info
288
+ * `last_access`: time in seconds since last access (integer value) or `None` if not returned
289
+ * `ttl`: time in seconds until the value expires (integer value) or `None` if not returned
290
+ - The special value `-1` represents if the key will never expire
291
+ * `client_flag`: integer value or `None` if not returned
292
+ * `win`:
293
+ - `True` if the client won the right to repopulate
294
+ - `False` if the client lost the right to repopulate
295
+ - `None` if the server did not return a win/lose flag
296
+ * `stale`: `True` if the value is stale, `False` otherwise
297
+ * `real_size`: integer value or `None` if not returned
298
+ * `opaque flag`: bytes value or `None` if not returned
299
+
300
+ NOTE: You shouldn't use this api directly, unless you are implementing some custom high-level
220
301
  command. See below for the usual memcache api.
221
302
 
222
303
  ## High level commands:
@@ -241,6 +322,33 @@ Invalidation...
241
322
  stale_policy: Optional[StalePolicy] = None,
242
323
  set_mode: SetMode = SetMode.SET, # Other are ADD, REPLACE, APPEND...
243
324
  ) -> bool:
325
+ """
326
+ Write a value using the specified `set_mode`
327
+ """
328
+
329
+ def refill(
330
+ self: HighLevelCommandMixinWithMetaCommands,
331
+ key: Union[Key, str],
332
+ value: Any,
333
+ ttl: int,
334
+ no_reply: bool = False,
335
+ ) -> bool:
336
+ """
337
+ Try to refill a value.
338
+
339
+ Use this method when you got a cache miss, read from DB and
340
+ are trying to refill the value.
341
+
342
+ DO NOT USE to write new state.
343
+
344
+ It will:
345
+ * use "ADD" mode, so it will fail if the value is already
346
+ present in cache.
347
+ * It will also disable write failure tracking. The write
348
+ failure tracking is often used to invalidate keys that
349
+ fail to be written. Since this is not writting new state,
350
+ there is no need to track failures.
351
+ """
244
352
 
245
353
  def delete(
246
354
  self,
@@ -249,6 +357,12 @@ Invalidation...
249
357
  no_reply: bool = False,
250
358
  stale_policy: Optional[StalePolicy] = None,
251
359
  ) -> bool:
360
+ """
361
+ Returns True if the key existed and it was deleted.
362
+ If the key is not found in the cache it will return False. If
363
+ you just want to the key to be deleted not caring of whether
364
+ it exists or not, use invalidate() instead.
365
+ """
252
366
 
253
367
  def invalidate(
254
368
  self,
@@ -257,6 +371,9 @@ Invalidation...
257
371
  no_reply: bool = False,
258
372
  stale_policy: Optional[StalePolicy] = None,
259
373
  ) -> bool:
374
+ """
375
+ Returns true of the key deleted or it didn't exist anyway
376
+ """
260
377
 
261
378
  def touch(
262
379
  self,
@@ -264,6 +381,9 @@ Invalidation...
264
381
  ttl: int,
265
382
  no_reply: bool = False,
266
383
  ) -> bool:
384
+ """
385
+ Modify the TTL of a key without retrieving the value
386
+ """
267
387
 
268
388
  def get_or_lease(
269
389
  self,
@@ -272,6 +392,13 @@ Invalidation...
272
392
  touch_ttl: Optional[int] = None,
273
393
  recache_policy: Optional[RecachePolicy] = None,
274
394
  ) -> Optional[Any]:
395
+ """
396
+ Get a key. On miss try to get a lease.
397
+
398
+ Guarantees only one cache client will get the miss and
399
+ gets to repopulate cache, while the others are blocked
400
+ waiting (according to the settings in the LeasePolicy)
401
+ """
275
402
 
276
403
  def get_or_lease_cas(
277
404
  self,
@@ -280,6 +407,10 @@ Invalidation...
280
407
  touch_ttl: Optional[int] = None,
281
408
  recache_policy: Optional[RecachePolicy] = None,
282
409
  ) -> Tuple[Optional[Any], Optional[int]]:
410
+ """
411
+ Same as get_or_lease(), but also return the CAS token so
412
+ it can be used during writes and detect races
413
+ """
283
414
 
284
415
  def get(
285
416
  self,
@@ -287,6 +418,9 @@ Invalidation...
287
418
  touch_ttl: Optional[int] = None,
288
419
  recache_policy: Optional[RecachePolicy] = None,
289
420
  ) -> Optional[Any]:
421
+ """
422
+ Get a key
423
+ """
290
424
 
291
425
  def multi_get(
292
426
  self,
@@ -294,6 +428,9 @@ Invalidation...
294
428
  touch_ttl: Optional[int] = None,
295
429
  recache_policy: Optional[RecachePolicy] = None,
296
430
  ) -> Dict[Key, Optional[Any]]:
431
+ """
432
+ Get multiple keys at once
433
+ """
297
434
 
298
435
  def get_cas(
299
436
  self,
@@ -301,6 +438,10 @@ Invalidation...
301
438
  touch_ttl: Optional[int] = None,
302
439
  recache_policy: Optional[RecachePolicy] = None,
303
440
  ) -> Tuple[Optional[Any], Optional[int]]:
441
+ """
442
+ Same as get(), but also return the CAS token so
443
+ it can be used during writes and detect races
444
+ """
304
445
 
305
446
  def get_typed(
306
447
  self,
@@ -310,6 +451,9 @@ Invalidation...
310
451
  recache_policy: Optional[RecachePolicy] = None,
311
452
  error_on_type_mismatch: bool = False,
312
453
  ) -> Optional[T]:
454
+ """
455
+ Same as get(), but ensure the type matched the provided cls
456
+ """
313
457
 
314
458
  def get_cas_typed(
315
459
  self,
@@ -319,6 +463,10 @@ Invalidation...
319
463
  recache_policy: Optional[RecachePolicy] = None,
320
464
  error_on_type_mismatch: bool = False,
321
465
  ) -> Tuple[Optional[T], Optional[int]]:
466
+ """
467
+ Same as get_typed(), but also return the CAS token so
468
+ it can be used during writes and detect races
469
+ """
322
470
 
323
471
  def delta(
324
472
  self,
@@ -328,6 +476,9 @@ Invalidation...
328
476
  no_reply: bool = False,
329
477
  cas_token: Optional[int] = None,
330
478
  ) -> bool:
479
+ """
480
+ Increment/Decrement a key that contains a counter
481
+ """
331
482
 
332
483
  def delta_initialize(
333
484
  self,
@@ -339,6 +490,11 @@ Invalidation...
339
490
  no_reply: bool = False,
340
491
  cas_token: Optional[int] = None,
341
492
  ) -> bool:
493
+ """
494
+ Increment/Decrement a key that contains a counter,
495
+ creating and setting it to the initial value if the
496
+ counter does not exist.
497
+ """
342
498
 
343
499
  def delta_and_get(
344
500
  self,
@@ -347,6 +503,9 @@ Invalidation...
347
503
  refresh_ttl: Optional[int] = None,
348
504
  cas_token: Optional[int] = None,
349
505
  ) -> Optional[int]:
506
+ """
507
+ Same as delta(), but return the resulting value
508
+ """
350
509
 
351
510
  def delta_initialize_and_get(
352
511
  self,
@@ -357,9 +516,19 @@ Invalidation...
357
516
  refresh_ttl: Optional[int] = None,
358
517
  cas_token: Optional[int] = None,
359
518
  ) -> Optional[int]:
519
+ """
520
+ Same as delta_initialize(), but return the resulting value
521
+ """
360
522
  ```
361
523
 
362
- # Anti-dogpiling techniques
524
+ # Reliability, consistency and best practices
525
+ We have published a deep-dive into some of the techniques to keep
526
+ cache consistent and reliable under high load that RevenueCat uses,
527
+ available thanks to this cache client.
528
+
529
+ See: https://www.revenuecat.com/blog/engineering/data-caching-revenuecat/
530
+
531
+ ## Anti-dogpiling, preventing thundering herds:
363
532
  Some commands receive `RecachePolicy`, `StalePolicy` and `LeasePolicy` for the
364
533
  advanced anti-dogpiling control needed in high-qps environments:
365
534