moat-kv 0.70.24__py3-none-any.whl → 0.71.0__py3-none-any.whl
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.
- build/lib/moat/kv/_cfg.yaml +2 -6
- build/lib/moat/kv/backend/mqtt.py +0 -3
- ci/rtd-requirements.txt +4 -0
- ci/test-requirements.txt +7 -0
- ci/travis.sh +96 -0
- debian/.gitignore +7 -0
- debian/changelog +1435 -0
- debian/control +43 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_cfg.yaml +2 -6
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/mqtt.py +0 -3
- debian/moat-kv.postinst +3 -0
- debian/rules +20 -0
- debian/source/format +1 -0
- debian/watch +4 -0
- docs/Makefile +20 -0
- docs/make.bat +36 -0
- docs/source/TODO.rst +61 -0
- docs/source/_static/.gitkeep +0 -0
- docs/source/acls.rst +80 -0
- docs/source/auth.rst +84 -0
- docs/source/client_protocol.rst +456 -0
- docs/source/code.rst +341 -0
- docs/source/command_line.rst +1187 -0
- docs/source/common_protocol.rst +47 -0
- docs/source/debugging.rst +70 -0
- docs/source/extend.rst +37 -0
- docs/source/history.rst +36 -0
- docs/source/index.rst +75 -0
- docs/source/model.rst +54 -0
- docs/source/overview.rst +83 -0
- docs/source/related.rst +89 -0
- docs/source/server_protocol.rst +450 -0
- docs/source/startup.rst +31 -0
- docs/source/translator.rst +244 -0
- docs/source/tutorial.rst +711 -0
- docs/source/v3.rst +168 -0
- examples/code/transform.scale.yml +21 -0
- examples/code/transform.switch.yml +82 -0
- examples/code/transform.timeslot.yml +63 -0
- moat/kv/_cfg.yaml +2 -6
- moat/kv/backend/mqtt.py +0 -3
- {moat_kv-0.70.24.dist-info → moat_kv-0.71.0.dist-info}/METADATA +2 -5
- {moat_kv-0.70.24.dist-info → moat_kv-0.71.0.dist-info}/RECORD +68 -17
- scripts/current +15 -0
- scripts/env +8 -0
- scripts/init +39 -0
- scripts/recover +17 -0
- scripts/rotate +33 -0
- scripts/run +29 -0
- scripts/run-all +10 -0
- scripts/run-any +10 -0
- scripts/run-single +15 -0
- scripts/success +4 -0
- systemd/moat-kv-recover.service +21 -0
- systemd/moat-kv-rotate.service +20 -0
- systemd/moat-kv-rotate.timer +10 -0
- systemd/moat-kv-run-all.service +26 -0
- systemd/moat-kv-run-all@.service +25 -0
- systemd/moat-kv-run-any.service +26 -0
- systemd/moat-kv-run-any@.service +25 -0
- systemd/moat-kv-run-single.service +26 -0
- systemd/moat-kv-run-single@.service +25 -0
- systemd/moat-kv.service +27 -0
- systemd/postinst +7 -0
- systemd/sysusers +3 -0
- build/lib/moat/kv/backend/serf.py +0 -45
- build/lib/moat/kv/mock/serf.py +0 -250
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/serf.py +0 -45
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/serf.py +0 -250
- moat/kv/backend/serf.py +0 -45
- moat/kv/mock/serf.py +0 -250
- {moat_kv-0.70.24.dist-info → moat_kv-0.71.0.dist-info}/WHEEL +0 -0
- {moat_kv-0.70.24.dist-info → moat_kv-0.71.0.dist-info}/licenses/LICENSE.txt +0 -0
- {moat_kv-0.70.24.dist-info → moat_kv-0.71.0.dist-info}/top_level.txt +0 -0
scripts/run
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
set -ex
|
3
|
+
|
4
|
+
# This script starts up MoaT-KV.
|
5
|
+
|
6
|
+
# Normally, starting MoaT-KV on a host where clients don't connect locally is
|
7
|
+
# a bad idea. However, on a system that has more than one network namespace
|
8
|
+
# you need to use a specific address. So set LOCAL=yes to force starting
|
9
|
+
# anyway.
|
10
|
+
|
11
|
+
if test "$LOCAL" != "yes" && test "$(moat util cfg kv.conn.host)" != "127.0.0.1"; then
|
12
|
+
echo "Server is not localhost: not starting."
|
13
|
+
exit 42 # mark as succeeded
|
14
|
+
fi
|
15
|
+
|
16
|
+
if test "$MODE" = "master" ; then
|
17
|
+
/usr/lib/moat/kv/current "$TEMP"
|
18
|
+
fi
|
19
|
+
|
20
|
+
if test ! -v NAME ; then
|
21
|
+
echo "NAME is not set. Exiting." >&2
|
22
|
+
exit 1
|
23
|
+
fi
|
24
|
+
|
25
|
+
if test -v TEMP && test -s "$TEMP" ; then
|
26
|
+
exec moat -c /etc/moat/moat.cfg kv server -a -l "$TEMP" "$NAME"
|
27
|
+
else
|
28
|
+
exec moat -c /etc/moat/moat.cfg kv server "$NAME"
|
29
|
+
fi
|
scripts/run-all
ADDED
scripts/run-any
ADDED
scripts/run-single
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
set -ex
|
3
|
+
|
4
|
+
# This script file starts up a MoaT-KV single-node runner.
|
5
|
+
|
6
|
+
if test ! -v NAME ; then
|
7
|
+
echo "NAME is not set. Exiting." >&2
|
8
|
+
exit 1
|
9
|
+
fi
|
10
|
+
|
11
|
+
if test $# = 1 ; then
|
12
|
+
exec moat -c /etc/moat/moat.cfg kv job -n "$NAME" -g "$1" run
|
13
|
+
else
|
14
|
+
exec moat -c /etc/moat/moat.cfg kv job -n "$NAME" run
|
15
|
+
fi
|
scripts/success
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
[Unit]
|
2
|
+
Description=MoaT-KV state recovery
|
3
|
+
Conflicts=moat-kv.service
|
4
|
+
Conflicts=distkv.service
|
5
|
+
|
6
|
+
AssertFileNotEmpty=/etc/moat/moat.cfg
|
7
|
+
|
8
|
+
[Install]
|
9
|
+
|
10
|
+
[Service]
|
11
|
+
Type=oneshot
|
12
|
+
|
13
|
+
ExecStart=/usr/lib/moat/kv/recover
|
14
|
+
ExecStartPost=!/bin/systemctl restart moat-kv.service
|
15
|
+
|
16
|
+
EnvironmentFile=/usr/lib/moat/kv/env
|
17
|
+
EnvironmentFile=-/etc/moat/kv.env
|
18
|
+
|
19
|
+
User=MoaT-KV
|
20
|
+
Group=MoaT
|
21
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
[Unit]
|
2
|
+
Description=MoaT-KV log rotate
|
3
|
+
Requires=moat-kv.service
|
4
|
+
After=moat-kv.service
|
5
|
+
Conflicts=distkv-rotate.service
|
6
|
+
|
7
|
+
ConditionFileNotEmpty=/etc/moat/moat.cfg
|
8
|
+
ConditionPathIsDirectory=/var/lib/moat/kv
|
9
|
+
|
10
|
+
[Install]
|
11
|
+
|
12
|
+
[Service]
|
13
|
+
ExecStart=/usr/lib/moat/kv/rotate
|
14
|
+
|
15
|
+
EnvironmentFile=/usr/lib/moat/kv/env
|
16
|
+
EnvironmentFile=-/etc/moat/kv.env
|
17
|
+
|
18
|
+
User=MoaT-KV
|
19
|
+
Group=MoaT
|
20
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
[Unit]
|
2
|
+
Description=MoaT-KV default all-nodes runner
|
3
|
+
After=moat-kv.service
|
4
|
+
Requires=moat-kv.service
|
5
|
+
Conflicts=distkv-run-all.service
|
6
|
+
|
7
|
+
ConditionFileNotEmpty=/etc/moat/moat.cfg
|
8
|
+
|
9
|
+
[Install]
|
10
|
+
WantedBy=multi-user.target
|
11
|
+
|
12
|
+
[Service]
|
13
|
+
Type=notify
|
14
|
+
ExecStart=/usr/lib/moat/kv/run-all
|
15
|
+
|
16
|
+
User=MoaT-KV
|
17
|
+
Group=MoaT
|
18
|
+
|
19
|
+
EnvironmentFile=/usr/lib/moat/kv/env
|
20
|
+
EnvironmentFile=-/etc/moat/kv.env
|
21
|
+
|
22
|
+
TimeoutSec=300
|
23
|
+
WatchdogSec=10
|
24
|
+
|
25
|
+
Restart=always
|
26
|
+
RestartSec=10
|
@@ -0,0 +1,25 @@
|
|
1
|
+
[Unit]
|
2
|
+
Description=MoaT-KV all-nodes runner for %i
|
3
|
+
After=moat-kv.service
|
4
|
+
Requires=moat-kv.service
|
5
|
+
|
6
|
+
ConditionFileNotEmpty=/etc/moat/moat.cfg
|
7
|
+
|
8
|
+
[Install]
|
9
|
+
WantedBy=multi-user.target
|
10
|
+
|
11
|
+
[Service]
|
12
|
+
Type=notify
|
13
|
+
ExecStart=/usr/lib/moat/kv/run-all %i
|
14
|
+
|
15
|
+
User=MoaT-KV
|
16
|
+
Group=MoaT
|
17
|
+
|
18
|
+
EnvironmentFile=/usr/lib/moat/kv/env
|
19
|
+
EnvironmentFile=-/etc/moat/kv.env
|
20
|
+
|
21
|
+
TimeoutSec=300
|
22
|
+
WatchdogSec=10
|
23
|
+
|
24
|
+
Restart=always
|
25
|
+
RestartSec=10
|
@@ -0,0 +1,26 @@
|
|
1
|
+
[Unit]
|
2
|
+
Description=MoaT-KV default any-nodes runner
|
3
|
+
After=moat-kv.service
|
4
|
+
Requires=moat-kv.service
|
5
|
+
Conflicts=distkv-run-any.service
|
6
|
+
|
7
|
+
ConditionFileNotEmpty=/etc/moat/moat.cfg
|
8
|
+
|
9
|
+
[Install]
|
10
|
+
WantedBy=multi-user.target
|
11
|
+
|
12
|
+
[Service]
|
13
|
+
Type=notify
|
14
|
+
ExecStart=/usr/lib/moat/kv/run-any
|
15
|
+
|
16
|
+
User=MoaT-KV
|
17
|
+
Group=MoaT
|
18
|
+
|
19
|
+
EnvironmentFile=/usr/lib/moat/kv/env
|
20
|
+
EnvironmentFile=-/etc/moat/kv.env
|
21
|
+
|
22
|
+
TimeoutSec=300
|
23
|
+
WatchdogSec=10
|
24
|
+
|
25
|
+
Restart=always
|
26
|
+
RestartSec=10
|
@@ -0,0 +1,25 @@
|
|
1
|
+
[Unit]
|
2
|
+
Description=MoaT-KV any-nodes runner for %i
|
3
|
+
After=moat-kv.service
|
4
|
+
Requires=moat-kv.service
|
5
|
+
|
6
|
+
ConditionFileNotEmpty=/etc/moat/moat.cfg
|
7
|
+
|
8
|
+
[Install]
|
9
|
+
WantedBy=multi-user.target
|
10
|
+
|
11
|
+
[Service]
|
12
|
+
Type=notify
|
13
|
+
ExecStart=/usr/lib/moat/kv/run-any %i
|
14
|
+
|
15
|
+
User=MoaT-KV
|
16
|
+
Group=MoaT
|
17
|
+
|
18
|
+
EnvironmentFile=/usr/lib/moat/kv/env
|
19
|
+
EnvironmentFile=-/etc/moat/kv.env
|
20
|
+
|
21
|
+
TimeoutSec=300
|
22
|
+
WatchdogSec=10
|
23
|
+
|
24
|
+
Restart=always
|
25
|
+
RestartSec=10
|
@@ -0,0 +1,26 @@
|
|
1
|
+
[Unit]
|
2
|
+
Description=MoaT-KV default single-node runner
|
3
|
+
After=moat-kv.service
|
4
|
+
Requires=moat-kv.service
|
5
|
+
Conflicts=distkv-run-single.service
|
6
|
+
|
7
|
+
ConditionFileNotEmpty=/etc/moat/moat.cfg
|
8
|
+
|
9
|
+
[Install]
|
10
|
+
WantedBy=multi-user.target
|
11
|
+
|
12
|
+
[Service]
|
13
|
+
Type=notify
|
14
|
+
ExecStart=/usr/lib/moat/kv/run-single
|
15
|
+
|
16
|
+
User=MoaT-KV
|
17
|
+
Group=MoaT
|
18
|
+
|
19
|
+
EnvironmentFile=/usr/lib/moat/kv/env
|
20
|
+
EnvironmentFile=-/etc/moat/kv.env
|
21
|
+
|
22
|
+
TimeoutSec=300
|
23
|
+
WatchdogSec=10
|
24
|
+
|
25
|
+
Restart=always
|
26
|
+
RestartSec=10
|
@@ -0,0 +1,25 @@
|
|
1
|
+
[Unit]
|
2
|
+
Description=MoaT-KV single-node runner for %i
|
3
|
+
After=moat-kv.service
|
4
|
+
Requires=moat-kv.service
|
5
|
+
|
6
|
+
ConditionFileNotEmpty=/etc/moat/moat.cfg
|
7
|
+
|
8
|
+
[Install]
|
9
|
+
WantedBy=multi-user.target
|
10
|
+
|
11
|
+
[Service]
|
12
|
+
Type=notify
|
13
|
+
ExecStart=/usr/lib/moat/kv/run-single %i
|
14
|
+
|
15
|
+
User=MoaT-KV
|
16
|
+
Group=MoaT
|
17
|
+
|
18
|
+
EnvironmentFile=/usr/lib/moat/kv/env
|
19
|
+
EnvironmentFile=-/etc/moat/kv.env
|
20
|
+
|
21
|
+
TimeoutSec=300
|
22
|
+
WatchdogSec=10
|
23
|
+
|
24
|
+
Restart=always
|
25
|
+
RestartSec=10
|
systemd/moat-kv.service
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
[Unit]
|
2
|
+
Description=MoaT-KV main server
|
3
|
+
After=mosquitto.service
|
4
|
+
Wants=mosquitto.service
|
5
|
+
Conflicts=moat-kv-recover.service
|
6
|
+
Conflicts=distkv.service
|
7
|
+
OnFailure=moat-kv-recover.service
|
8
|
+
|
9
|
+
ConditionFileNotEmpty=/etc/moat/moat.cfg
|
10
|
+
|
11
|
+
[Install]
|
12
|
+
WantedBy=multi-user.target
|
13
|
+
|
14
|
+
[Service]
|
15
|
+
Type=notify
|
16
|
+
ExecStart=/usr/lib/moat/kv/run
|
17
|
+
ExecStartPost=/usr/lib/moat/kv/success
|
18
|
+
SuccessExitStatus=42
|
19
|
+
|
20
|
+
User=MoaT-KV
|
21
|
+
Group=MoaT
|
22
|
+
|
23
|
+
EnvironmentFile=/usr/lib/moat/kv/env
|
24
|
+
EnvironmentFile=-/etc/moat/kv.env
|
25
|
+
|
26
|
+
TimeoutSec=300
|
27
|
+
WatchdogSec=10
|
systemd/postinst
ADDED
systemd/sysusers
ADDED
@@ -1,45 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
from contextlib import asynccontextmanager
|
3
|
-
|
4
|
-
import anyio
|
5
|
-
import asyncserf
|
6
|
-
|
7
|
-
from . import Backend
|
8
|
-
|
9
|
-
# Simply setting connect=asyncserf.serf_client interferes with mocking
|
10
|
-
# when testing.
|
11
|
-
|
12
|
-
|
13
|
-
class SerfBackend(Backend):
|
14
|
-
client = None
|
15
|
-
|
16
|
-
@asynccontextmanager
|
17
|
-
async def connect(self, *a, **k):
|
18
|
-
async with asyncserf.serf_client(*a, **k) as c:
|
19
|
-
self.client = c
|
20
|
-
try:
|
21
|
-
yield self
|
22
|
-
finally:
|
23
|
-
with anyio.CancelScope(shield=True):
|
24
|
-
await self.aclose()
|
25
|
-
self.client = None
|
26
|
-
|
27
|
-
def monitor(self, *topic): # pylint: disable=invalid-overridden-method
|
28
|
-
topic = "user:" + ".".join(topic)
|
29
|
-
# self.client.stream is also async, pass thru
|
30
|
-
return self.client.stream(topic)
|
31
|
-
|
32
|
-
def send(self, *topic, payload): # pylint: disable=invalid-overridden-method
|
33
|
-
"""
|
34
|
-
Send this payload to this topic.
|
35
|
-
"""
|
36
|
-
# self.client.event is also async, pass thru
|
37
|
-
return self.client.event(".".join(topic), payload=payload, coalesce=False)
|
38
|
-
|
39
|
-
|
40
|
-
@asynccontextmanager
|
41
|
-
async def connect(*a, **kw):
|
42
|
-
async with anyio.create_task_group() as tg:
|
43
|
-
c = SerfBackend(tg)
|
44
|
-
async with c.connect(*a, **kw):
|
45
|
-
yield c
|
build/lib/moat/kv/mock/serf.py
DELETED
@@ -1,250 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
import copy
|
3
|
-
import logging
|
4
|
-
import time
|
5
|
-
from contextlib import AsyncExitStack, asynccontextmanager
|
6
|
-
from functools import partial
|
7
|
-
|
8
|
-
import anyio
|
9
|
-
import attr
|
10
|
-
from unittest import mock
|
11
|
-
import trio
|
12
|
-
from asyncscope import main_scope, scope
|
13
|
-
from asyncserf.stream import SerfEvent
|
14
|
-
from moat.util import NotGiven, ValueEvent, attrdict, combine_dict, create_queue
|
15
|
-
|
16
|
-
from moat.kv.codec import unpacker
|
17
|
-
from moat.kv.mock import S as _S
|
18
|
-
from moat.kv.server import Server
|
19
|
-
|
20
|
-
logger = logging.getLogger(__name__)
|
21
|
-
|
22
|
-
otm = time.time
|
23
|
-
|
24
|
-
from . import CFG
|
25
|
-
|
26
|
-
|
27
|
-
@asynccontextmanager
|
28
|
-
async def stdtest(n=1, run=True, ssl=False, tocks=20, **kw):
|
29
|
-
C_OUT = CFG.get("_stdout", NotGiven)
|
30
|
-
if C_OUT is not NotGiven:
|
31
|
-
del CFG["_stdout"]
|
32
|
-
TESTCFG = copy.deepcopy(CFG["kv"])
|
33
|
-
TESTCFG.server.port = None
|
34
|
-
TESTCFG.server.backend = "serf"
|
35
|
-
TESTCFG.root = "test"
|
36
|
-
if C_OUT is not NotGiven:
|
37
|
-
CFG["_stdout"] = C_OUT
|
38
|
-
TESTCFG["_stdout"] = C_OUT
|
39
|
-
|
40
|
-
if ssl:
|
41
|
-
import ssl
|
42
|
-
|
43
|
-
import trustme
|
44
|
-
|
45
|
-
ca = trustme.CA()
|
46
|
-
cert = ca.issue_server_cert("127.0.0.1")
|
47
|
-
server_ctx = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
|
48
|
-
client_ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH)
|
49
|
-
ca.configure_trust(client_ctx)
|
50
|
-
cert.configure_cert(server_ctx)
|
51
|
-
else:
|
52
|
-
server_ctx = client_ctx = False
|
53
|
-
|
54
|
-
clock = trio.lowlevel.current_clock()
|
55
|
-
clock.autojump_threshold = 0.0
|
56
|
-
# clock.rate = 5
|
57
|
-
|
58
|
-
@attr.s
|
59
|
-
class S(_S):
|
60
|
-
splits = attr.ib(factory=set)
|
61
|
-
serfs = attr.ib(factory=set)
|
62
|
-
|
63
|
-
def split(self, s):
|
64
|
-
assert s not in self.splits
|
65
|
-
logger.debug("Split: add %d", s)
|
66
|
-
self.splits.add(s)
|
67
|
-
|
68
|
-
def join(self, s):
|
69
|
-
logger.debug("Split: join %d", s)
|
70
|
-
self.splits.remove(s)
|
71
|
-
|
72
|
-
async def mock_get_host_port(st, host):
|
73
|
-
i = int(host[host.rindex("_") + 1 :])
|
74
|
-
s = st.s[i]
|
75
|
-
await s.is_serving
|
76
|
-
for host, port, *_ in s.ports:
|
77
|
-
if host == "::" or host[0] != ":":
|
78
|
-
return host, port
|
79
|
-
|
80
|
-
def tm():
|
81
|
-
try:
|
82
|
-
return trio.current_time()
|
83
|
-
except RuntimeError:
|
84
|
-
return otm()
|
85
|
-
|
86
|
-
async def mock_set_tock(self, old):
|
87
|
-
assert self._tock < tocks, "Test didn't terminate. Limit:" + str(tocks)
|
88
|
-
await old()
|
89
|
-
|
90
|
-
async with main_scope("moat.kv.test.serf") as scp:
|
91
|
-
tg = scp._tg
|
92
|
-
st = S(tg, client_ctx)
|
93
|
-
async with AsyncExitStack() as ex:
|
94
|
-
st.ex = ex # pylint: disable=attribute-defined-outside-init
|
95
|
-
ex.enter_context(mock.patch("time.time", new=tm))
|
96
|
-
ex.enter_context(mock.patch("time.monotonic", new=tm))
|
97
|
-
logging._startTime = tm()
|
98
|
-
|
99
|
-
ex.enter_context(
|
100
|
-
mock.patch("asyncserf.serf_client", new=partial(mock_serf_client, st)),
|
101
|
-
)
|
102
|
-
|
103
|
-
for i in range(n):
|
104
|
-
name = "test_" + str(i)
|
105
|
-
args = kw.get(name, kw.get("args", attrdict()))
|
106
|
-
args["cfg"] = combine_dict(
|
107
|
-
args.get("cfg", {}),
|
108
|
-
{
|
109
|
-
"kv": {
|
110
|
-
"conn": {"ssl": client_ctx},
|
111
|
-
},
|
112
|
-
"server": {
|
113
|
-
"bind_default": {
|
114
|
-
"host": "127.0.0.1",
|
115
|
-
"port": i + 50120,
|
116
|
-
"ssl": server_ctx,
|
117
|
-
},
|
118
|
-
"serf": {"i": i},
|
119
|
-
},
|
120
|
-
},
|
121
|
-
TESTCFG,
|
122
|
-
)
|
123
|
-
s = Server(name, **args)
|
124
|
-
ex.enter_context(
|
125
|
-
mock.patch.object(s, "_set_tock", new=partial(mock_set_tock, s, s._set_tock)),
|
126
|
-
)
|
127
|
-
ex.enter_context(
|
128
|
-
mock.patch.object(s, "_get_host_port", new=partial(mock_get_host_port, st)),
|
129
|
-
)
|
130
|
-
st.s.append(s)
|
131
|
-
|
132
|
-
async def with_serf(s, *a, **k):
|
133
|
-
s._scope = scope.get()
|
134
|
-
return await s._scoped_serve(*a, **k)
|
135
|
-
|
136
|
-
evts = []
|
137
|
-
for i in range(n):
|
138
|
-
if kw.get("run_" + str(i), run):
|
139
|
-
evt = anyio.Event()
|
140
|
-
await scp.spawn_service(with_serf, st.s[i], ready_evt=evt)
|
141
|
-
evts.append(evt)
|
142
|
-
for e in evts:
|
143
|
-
await e.wait()
|
144
|
-
try:
|
145
|
-
yield st
|
146
|
-
finally:
|
147
|
-
with anyio.fail_after(2, shield=True):
|
148
|
-
logger.info("Runtime: %s", clock.current_time())
|
149
|
-
tg.cancel_scope.cancel()
|
150
|
-
logger.info("End")
|
151
|
-
pass # unwinding ex:AsyncExitStack
|
152
|
-
|
153
|
-
|
154
|
-
@asynccontextmanager
|
155
|
-
async def mock_serf_client(master, **cfg):
|
156
|
-
async with scope.using_scope():
|
157
|
-
ms = MockServ(master, **cfg)
|
158
|
-
master.serfs.add(ms)
|
159
|
-
ms._scope = scope.get() # pylint:disable=attribute-defined-outside-init
|
160
|
-
try:
|
161
|
-
yield ms
|
162
|
-
finally:
|
163
|
-
master.serfs.remove(ms)
|
164
|
-
pass # terminating mock_serf_client nursery
|
165
|
-
|
166
|
-
|
167
|
-
class MockServ:
|
168
|
-
def __init__(self, master, **cfg):
|
169
|
-
self.cfg = cfg
|
170
|
-
self._tg = scope._tg
|
171
|
-
self.streams = {}
|
172
|
-
self._master = master
|
173
|
-
|
174
|
-
def __hash__(self):
|
175
|
-
return id(self)
|
176
|
-
|
177
|
-
async def spawn(self, fn, *args, **kw):
|
178
|
-
async def run(evt):
|
179
|
-
with anyio.CancelScope() as sc:
|
180
|
-
await evt.set(sc)
|
181
|
-
await fn(*args, **kw)
|
182
|
-
|
183
|
-
evt = ValueEvent()
|
184
|
-
self._tg.spawn(run, evt)
|
185
|
-
return await evt.get()
|
186
|
-
|
187
|
-
async def event(self, name, payload, coalesce=True):
|
188
|
-
try:
|
189
|
-
logger.debug("SERF:%s: %r", name, unpacker(payload))
|
190
|
-
except Exception:
|
191
|
-
logger.debug("SERF:%s: %r (raw)", name, payload)
|
192
|
-
assert not coalesce, "'coalesce' must be cleared!"
|
193
|
-
|
194
|
-
i_self = self.cfg.get("i", 0)
|
195
|
-
for s in list(self._master.serfs):
|
196
|
-
i_s = s.cfg.get("i", 0)
|
197
|
-
for x in self._master.splits:
|
198
|
-
if (i_s < x) != (i_self < x):
|
199
|
-
break
|
200
|
-
else:
|
201
|
-
n = tuple(name.split("."))
|
202
|
-
while n:
|
203
|
-
sl = s.streams.get(n, ())
|
204
|
-
for sn in sl:
|
205
|
-
await sn.q.put((name, payload))
|
206
|
-
n = n[:-1]
|
207
|
-
|
208
|
-
def stream(self, typ):
|
209
|
-
"""compat for supporting asyncactor"""
|
210
|
-
if not typ.startswith("user:"):
|
211
|
-
raise RuntimeError("not supported")
|
212
|
-
typ = typ[5:]
|
213
|
-
return self.serf_mon(typ)
|
214
|
-
|
215
|
-
def serf_mon(self, typ):
|
216
|
-
if "," in typ:
|
217
|
-
raise RuntimeError("not supported")
|
218
|
-
s = MockSerfStream(self, "user:" + typ)
|
219
|
-
return s
|
220
|
-
|
221
|
-
async def serf_send(self, typ, payload):
|
222
|
-
"""compat for supporting asyncactor"""
|
223
|
-
return await self.event(typ, payload)
|
224
|
-
|
225
|
-
|
226
|
-
class MockSerfStream:
|
227
|
-
q = None
|
228
|
-
|
229
|
-
def __init__(self, serf, typ):
|
230
|
-
self.serf = serf
|
231
|
-
assert typ.startswith("user:")
|
232
|
-
self.typ = tuple(typ[5:].split("."))
|
233
|
-
|
234
|
-
async def __aenter__(self):
|
235
|
-
self.q = create_queue(100)
|
236
|
-
self.serf.streams.setdefault(self.typ, []).append(self)
|
237
|
-
return self
|
238
|
-
|
239
|
-
async def __aexit__(self, *tb):
|
240
|
-
self.serf.streams[self.typ].remove(self)
|
241
|
-
del self.q
|
242
|
-
|
243
|
-
def __aiter__(self):
|
244
|
-
return self
|
245
|
-
|
246
|
-
async def __anext__(self):
|
247
|
-
res = await self.q.get()
|
248
|
-
evt = SerfEvent(self)
|
249
|
-
evt.topic, evt.payload = res
|
250
|
-
return evt
|
@@ -1,45 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
from contextlib import asynccontextmanager
|
3
|
-
|
4
|
-
import anyio
|
5
|
-
import asyncserf
|
6
|
-
|
7
|
-
from . import Backend
|
8
|
-
|
9
|
-
# Simply setting connect=asyncserf.serf_client interferes with mocking
|
10
|
-
# when testing.
|
11
|
-
|
12
|
-
|
13
|
-
class SerfBackend(Backend):
|
14
|
-
client = None
|
15
|
-
|
16
|
-
@asynccontextmanager
|
17
|
-
async def connect(self, *a, **k):
|
18
|
-
async with asyncserf.serf_client(*a, **k) as c:
|
19
|
-
self.client = c
|
20
|
-
try:
|
21
|
-
yield self
|
22
|
-
finally:
|
23
|
-
with anyio.CancelScope(shield=True):
|
24
|
-
await self.aclose()
|
25
|
-
self.client = None
|
26
|
-
|
27
|
-
def monitor(self, *topic): # pylint: disable=invalid-overridden-method
|
28
|
-
topic = "user:" + ".".join(topic)
|
29
|
-
# self.client.stream is also async, pass thru
|
30
|
-
return self.client.stream(topic)
|
31
|
-
|
32
|
-
def send(self, *topic, payload): # pylint: disable=invalid-overridden-method
|
33
|
-
"""
|
34
|
-
Send this payload to this topic.
|
35
|
-
"""
|
36
|
-
# self.client.event is also async, pass thru
|
37
|
-
return self.client.event(".".join(topic), payload=payload, coalesce=False)
|
38
|
-
|
39
|
-
|
40
|
-
@asynccontextmanager
|
41
|
-
async def connect(*a, **kw):
|
42
|
-
async with anyio.create_task_group() as tg:
|
43
|
-
c = SerfBackend(tg)
|
44
|
-
async with c.connect(*a, **kw):
|
45
|
-
yield c
|