singleflight
Coalesce multiple identical call into one, preventing thundering-herd/stampede to database/other backends
It is a python port of golang's groupcache singleflight implementation
This module does not provide caching mechanism. Rather, this module can used behind a caching abstraction to deduplicate cache-filling call
Only support python 3.5+
Installation
pip install singleflight
Usage
This modules has 3 implementation, which can be imported as follows
from singleflight.basic import SingleFlight # for multi-threaded apps
from singleflight.gevent import SingleFlightGevent as SingleFlight # for gevent apps
from singleflight.asynchronous import SingleFlightAsync as SingleFlight # for asyncio/curio apps
Then you can use it as follows (the example shows the multi-threaded version). Note that, key is important for the modules to know which call should be de-duplicated
from time import sleep
from concurrent.futures import ThreadPoolExecutor
from functools import partial
from singleflight.basic import SingleFlight
if __name__ == '__main__':
sf = SingleFlight()
executor = ThreadPoolExecutor(max_workers=10)
counter = 0
result = "this is the result"
def work(num):
global counter, result
sleep(0.1) # emulate bit slower call
counter += 1
return (result, num)
res = []
for i in range(10):
sfc = partial(sf.call, work, "key", i+1)
r = executor.submit(sfc)
res.append(r)
for r in res:
assert r.result()[0] == result
# because only the first one can get the lock
# and only that one request call
assert r.result()[1] == 1
assert counter == 1
For decorator fans, you can also use it to wrap your function.
from time import sleep
from concurrent.futures import ThreadPoolExecutor
from functools import partial
from singleflight.basic import SingleFlight
if __name__ == '__main__':
sf = SingleFlight()
executor = ThreadPoolExecutor(max_workers=10)
# success case
counter = 0
result = "this is the result"
@sf.wrap
def work(num):
global counter, result
sleep(0.1) # emulate bit slower call
counter += 1
return (result, num)
res = []
for i in range(10):
sfc = partial(work, "key", i+1)
r = executor.submit(sfc)
res.append(r)
for r in res:
assert r.result()[0] == result
# because only the first one can get the lock
# and only that one request call
assert r.result()[1] == 1
assert counter == 1
All *args and **kwargs your function has is passed directly later. Exceptions are also raised normally.
Metadata
Release files for singleflight 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| singleflight-0.1.3.tar.gz | 4.1 kB | Details |
Release files / singleflight-0.1.3.tar.gz
| Download URL | singleflight-0.1.3.tar.gz |
|---|---|
| Size | 4.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
411889f200804e5f19e9279d4b8d3213a1f44f35250a5e5eba6f1f3d16b7b524
|
|
BLAKE2b-256 checksum How to use checksums |
42b4aed8e832246f417105464939a71bb7d4d5547a56b53870b87d2a43ff1d24
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.1.1 pkginfo/1.5.0.1 requests/2.21.0 setuptools/49.1.0 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.7.3
|