moat-kv 0.70.23__tar.gz → 0.70.24__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.
- {moat_kv-0.70.23 → moat_kv-0.70.24}/PKG-INFO +2 -2
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/docs/source/conf.py +201 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/examples/pathify.py +45 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/__init__.py +98 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/deletor.py +139 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/__init__.py +444 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/_test.py +166 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/password.py +234 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/root.py +58 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/__init__.py +67 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/mqtt.py +74 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/serf.py +45 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/__init__.py +1 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/acl.py +180 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/auth.py +261 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/code.py +293 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/codec.py +186 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/data.py +265 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/dump/__init__.py +143 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/error.py +149 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/internal.py +248 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/job.py +433 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/log.py +53 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/server.py +114 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/type.py +201 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/__init__.py +101 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/mqtt.py +159 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/serf.py +250 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/tracer.py +63 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/__init__.py +646 -0
- moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/command.py +241 -0
- moat_kv-0.70.24/docs/source/conf.py +201 -0
- moat_kv-0.70.24/examples/pathify.py +45 -0
- moat_kv-0.70.24/moat/kv/__init__.py +19 -0
- moat_kv-0.70.24/moat/kv/_cfg.yaml +97 -0
- moat_kv-0.70.24/moat/kv/_main.py +91 -0
- moat_kv-0.70.24/moat/kv/actor/__init__.py +98 -0
- moat_kv-0.70.24/moat/kv/actor/deletor.py +139 -0
- moat_kv-0.70.24/moat/kv/auth/__init__.py +444 -0
- moat_kv-0.70.24/moat/kv/auth/_test.py +166 -0
- moat_kv-0.70.24/moat/kv/auth/password.py +234 -0
- moat_kv-0.70.24/moat/kv/auth/root.py +58 -0
- moat_kv-0.70.24/moat/kv/backend/__init__.py +67 -0
- moat_kv-0.70.24/moat/kv/backend/mqtt.py +74 -0
- moat_kv-0.70.24/moat/kv/backend/serf.py +45 -0
- moat_kv-0.70.24/moat/kv/client.py +1025 -0
- moat_kv-0.70.24/moat/kv/code.py +236 -0
- moat_kv-0.70.24/moat/kv/codec.py +11 -0
- moat_kv-0.70.24/moat/kv/command/__init__.py +1 -0
- moat_kv-0.70.24/moat/kv/command/acl.py +180 -0
- moat_kv-0.70.24/moat/kv/command/auth.py +261 -0
- moat_kv-0.70.24/moat/kv/command/code.py +293 -0
- moat_kv-0.70.24/moat/kv/command/codec.py +186 -0
- moat_kv-0.70.24/moat/kv/command/data.py +265 -0
- moat_kv-0.70.24/moat/kv/command/dump/__init__.py +143 -0
- moat_kv-0.70.24/moat/kv/command/error.py +149 -0
- moat_kv-0.70.24/moat/kv/command/internal.py +248 -0
- moat_kv-0.70.24/moat/kv/command/job.py +433 -0
- moat_kv-0.70.24/moat/kv/command/log.py +53 -0
- moat_kv-0.70.24/moat/kv/command/server.py +114 -0
- moat_kv-0.70.24/moat/kv/command/type.py +201 -0
- moat_kv-0.70.24/moat/kv/config.py +46 -0
- moat_kv-0.70.24/moat/kv/data.py +216 -0
- moat_kv-0.70.24/moat/kv/errors.py +561 -0
- moat_kv-0.70.24/moat/kv/exceptions.py +126 -0
- moat_kv-0.70.24/moat/kv/mock/__init__.py +101 -0
- moat_kv-0.70.24/moat/kv/mock/mqtt.py +159 -0
- moat_kv-0.70.24/moat/kv/mock/serf.py +250 -0
- moat_kv-0.70.24/moat/kv/mock/tracer.py +63 -0
- moat_kv-0.70.24/moat/kv/model.py +1069 -0
- moat_kv-0.70.24/moat/kv/obj/__init__.py +646 -0
- moat_kv-0.70.24/moat/kv/obj/command.py +241 -0
- moat_kv-0.70.24/moat/kv/runner.py +1347 -0
- moat_kv-0.70.24/moat/kv/server.py +2809 -0
- moat_kv-0.70.24/moat/kv/types.py +513 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24}/moat_kv.egg-info/PKG-INFO +2 -2
- moat_kv-0.70.24/moat_kv.egg-info/SOURCES.txt +100 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24}/moat_kv.egg-info/requires.txt +1 -1
- moat_kv-0.70.24/moat_kv.egg-info/top_level.txt +9 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24}/pyproject.toml +4 -3
- moat_kv-0.70.23/moat_kv.egg-info/SOURCES.txt +0 -26
- moat_kv-0.70.23/moat_kv.egg-info/top_level.txt +0 -1
- {moat_kv-0.70.23 → moat_kv-0.70.24}/LICENSE +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24}/LICENSE.APACHE2 +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24}/LICENSE.MIT +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24}/LICENSE.txt +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24}/MANIFEST.in +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24}/README.rst +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/__init__.py +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/_cfg.yaml +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/_main.py +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/client.py +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/code.py +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/codec.py +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/config.py +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/data.py +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/errors.py +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/exceptions.py +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/model.py +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/runner.py +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/server.py +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24/debian/moat-kv/usr/lib/python3/dist-packages}/moat/kv/types.py +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24}/moat_kv.egg-info/dependency_links.txt +0 -0
- {moat_kv-0.70.23 → moat_kv-0.70.24}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: moat-kv
|
3
|
-
Version: 0.70.
|
3
|
+
Version: 0.70.24
|
4
4
|
Summary: A distributed no-master key-value store
|
5
5
|
Author-email: Matthias Urlichs <matthias@urlichs.de>
|
6
6
|
Project-URL: homepage, https://m-o-a-t.org
|
@@ -34,7 +34,7 @@ Requires-Dist: PyNaCl>=1.3
|
|
34
34
|
Requires-Dist: moat-lib-diffiehellman~=0.13.4
|
35
35
|
Requires-Dist: psutil
|
36
36
|
Requires-Dist: simpleeval>=0.9.10
|
37
|
-
Requires-Dist: moat-mqtt~=0.42.
|
37
|
+
Requires-Dist: moat-mqtt~=0.42.3
|
38
38
|
Requires-Dist: moat-util~=0.56.4
|
39
39
|
Requires-Dist: exceptiongroup; python_version < "3.11"
|
40
40
|
Provides-Extra: dev
|
@@ -0,0 +1,201 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
#
|
4
|
+
# Documentation build configuration file, created by
|
5
|
+
# sphinx-quickstart on Sat Jan 21 19:11:14 2017.
|
6
|
+
#
|
7
|
+
# This file is execfile()d with the current directory set to its
|
8
|
+
# containing dir.
|
9
|
+
#
|
10
|
+
# Note that not all possible configuration values are present in this
|
11
|
+
# autogenerated file.
|
12
|
+
#
|
13
|
+
# All configuration values have a default; values that are commented out
|
14
|
+
# serve to show the default.
|
15
|
+
|
16
|
+
# If extensions (or modules to document with autodoc) are in another directory,
|
17
|
+
# add these directories to sys.path here. If the directory is relative to the
|
18
|
+
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
19
|
+
#
|
20
|
+
import os
|
21
|
+
import sys
|
22
|
+
|
23
|
+
# So autodoc can import our package
|
24
|
+
sys.path.insert(0, os.path.abspath("../.."))
|
25
|
+
|
26
|
+
# Warn about all references to unknown targets
|
27
|
+
nitpicky = True
|
28
|
+
# Except for these ones, which we expect to point to unknown targets:
|
29
|
+
nitpick_ignore = [
|
30
|
+
# Format is ("sphinx reference type", "string"), e.g.:
|
31
|
+
("py:obj", "bytes-like"),
|
32
|
+
("py:class", "RangeSet"),
|
33
|
+
("py:class", "Any"),
|
34
|
+
]
|
35
|
+
|
36
|
+
autodoc_inherit_docstrings = False
|
37
|
+
|
38
|
+
# -- General configuration ------------------------------------------------
|
39
|
+
|
40
|
+
# If your documentation needs a minimal Sphinx version, state it here.
|
41
|
+
#
|
42
|
+
# needs_sphinx = '1.0'
|
43
|
+
|
44
|
+
# Add any Sphinx extension module names here, as strings. They can be
|
45
|
+
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
46
|
+
# ones.
|
47
|
+
extensions = [
|
48
|
+
"sphinx.ext.autodoc",
|
49
|
+
"sphinx.ext.intersphinx",
|
50
|
+
"sphinx.ext.coverage",
|
51
|
+
"sphinx.ext.napoleon",
|
52
|
+
"sphinxcontrib_trio",
|
53
|
+
]
|
54
|
+
|
55
|
+
intersphinx_mapping = {
|
56
|
+
"python": ("https://docs.python.org/3", None),
|
57
|
+
"trio": ("https://trio.readthedocs.io/en/stable", None),
|
58
|
+
}
|
59
|
+
|
60
|
+
autodoc_member_order = "bysource"
|
61
|
+
|
62
|
+
# Add any paths that contain templates here, relative to this directory.
|
63
|
+
templates_path = []
|
64
|
+
|
65
|
+
# The suffix(es) of source filenames.
|
66
|
+
# You can specify multiple suffix as a list of string:
|
67
|
+
#
|
68
|
+
# source_suffix = ['.rst', '.md']
|
69
|
+
source_suffix = ".rst"
|
70
|
+
|
71
|
+
# The master toctree document.
|
72
|
+
master_doc = "index"
|
73
|
+
|
74
|
+
# General information about the project.
|
75
|
+
project = "MoaT-KV"
|
76
|
+
copyright = "The MoaT-KV authors"
|
77
|
+
author = "The MoaT-KV authors"
|
78
|
+
|
79
|
+
# The version info for the project you're documenting, acts as replacement for
|
80
|
+
# |version| and |release|, also used in various other places throughout the
|
81
|
+
# built documents.
|
82
|
+
#
|
83
|
+
# The short X.Y version.
|
84
|
+
import pkg_resources
|
85
|
+
|
86
|
+
version = pkg_resources.get_distribution("moat.kv")._version
|
87
|
+
|
88
|
+
# The full version, including alpha/beta/rc tags.
|
89
|
+
release = version
|
90
|
+
|
91
|
+
# The language for content autogenerated by Sphinx. Refer to documentation
|
92
|
+
# for a list of supported languages.
|
93
|
+
#
|
94
|
+
# This is also used if you do content translation via gettext catalogs.
|
95
|
+
# Usually you set "language" from the command line for these cases.
|
96
|
+
language = None
|
97
|
+
|
98
|
+
# List of patterns, relative to source directory, that match files and
|
99
|
+
# directories to ignore when looking for source files.
|
100
|
+
# This patterns also effect to html_static_path and html_extra_path
|
101
|
+
exclude_patterns = []
|
102
|
+
|
103
|
+
# The name of the Pygments (syntax highlighting) style to use.
|
104
|
+
pygments_style = "sphinx"
|
105
|
+
|
106
|
+
# The default language for :: blocks
|
107
|
+
highlight_language = "python3"
|
108
|
+
|
109
|
+
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
110
|
+
todo_include_todos = False
|
111
|
+
|
112
|
+
|
113
|
+
# -- Options for HTML output ----------------------------------------------
|
114
|
+
|
115
|
+
# The theme to use for HTML and HTML Help pages. See the documentation for
|
116
|
+
# a list of builtin themes.
|
117
|
+
#
|
118
|
+
# html_theme = 'alabaster'
|
119
|
+
|
120
|
+
# We have to set this ourselves, not only because it's useful for local
|
121
|
+
# testing, but also because if we don't then RTD will throw away our
|
122
|
+
# html_theme_options.
|
123
|
+
import sphinx_rtd_theme
|
124
|
+
|
125
|
+
html_theme = "sphinx_rtd_theme"
|
126
|
+
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
127
|
+
|
128
|
+
# Theme options are theme-specific and customize the look and feel of a theme
|
129
|
+
# further. For a list of options available for each theme, see the
|
130
|
+
# documentation.
|
131
|
+
#
|
132
|
+
html_theme_options = {
|
133
|
+
# default is 2
|
134
|
+
# show deeper nesting in the RTD theme's sidebar TOC
|
135
|
+
# https://stackoverflow.com/questions/27669376/
|
136
|
+
# I'm not 100% sure this actually does anything with our current
|
137
|
+
# versions/settings...
|
138
|
+
"navigation_depth": 4,
|
139
|
+
"logo_only": True,
|
140
|
+
}
|
141
|
+
|
142
|
+
# Add any paths that contain custom static files (such as style sheets) here,
|
143
|
+
# relative to this directory. They are copied after the builtin static files,
|
144
|
+
# so a file named "default.css" will overwrite the builtin "default.css".
|
145
|
+
html_static_path = ["_static"]
|
146
|
+
|
147
|
+
|
148
|
+
# -- Options for HTMLHelp output ------------------------------------------
|
149
|
+
|
150
|
+
# Output file base name for HTML help builder.
|
151
|
+
htmlhelp_basename = "moat.kv-doc"
|
152
|
+
|
153
|
+
|
154
|
+
# -- Options for LaTeX output ---------------------------------------------
|
155
|
+
|
156
|
+
latex_elements = {
|
157
|
+
# The paper size ('letterpaper' or 'a4paper').
|
158
|
+
#
|
159
|
+
# 'papersize': 'letterpaper',
|
160
|
+
# The font size ('10pt', '11pt' or '12pt').
|
161
|
+
#
|
162
|
+
# 'pointsize': '10pt',
|
163
|
+
# Additional stuff for the LaTeX preamble.
|
164
|
+
#
|
165
|
+
# 'preamble': '',
|
166
|
+
# Latex figure (float) alignment
|
167
|
+
#
|
168
|
+
# 'figure_align': 'htbp',
|
169
|
+
}
|
170
|
+
|
171
|
+
# Grouping the document tree into LaTeX files. List of tuples
|
172
|
+
# (source start file, target name, title,
|
173
|
+
# author, documentclass [howto, manual, or own class]).
|
174
|
+
latex_documents = [
|
175
|
+
(master_doc, "moat.kv.tex", "Trio Documentation", author, "manual"),
|
176
|
+
]
|
177
|
+
|
178
|
+
|
179
|
+
# -- Options for manual page output ---------------------------------------
|
180
|
+
|
181
|
+
# One entry per manual page. List of tuples
|
182
|
+
# (source start file, name, description, authors, manual section).
|
183
|
+
man_pages = [(master_doc, "moat.kv", "MoaT-KV Documentation", [author], 1)]
|
184
|
+
|
185
|
+
|
186
|
+
# -- Options for Texinfo output -------------------------------------------
|
187
|
+
|
188
|
+
# Grouping the document tree into Texinfo files. List of tuples
|
189
|
+
# (source start file, target name, title, author,
|
190
|
+
# dir menu entry, description, category)
|
191
|
+
texinfo_documents = [
|
192
|
+
(
|
193
|
+
master_doc,
|
194
|
+
"moat.kv",
|
195
|
+
"MoaT-KV Documentation",
|
196
|
+
author,
|
197
|
+
"MoaT-KV",
|
198
|
+
"A distributed no-master key-value store",
|
199
|
+
"Miscellaneous",
|
200
|
+
),
|
201
|
+
]
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/python3
|
2
|
+
|
3
|
+
# Batch-convert data. In this case I had some entries which were stored as
|
4
|
+
# a list, but using Path made much more sense (esp when you need to
|
5
|
+
# view/edit the yaml export).
|
6
|
+
|
7
|
+
import anyio
|
8
|
+
from moat.kv.client import open_client
|
9
|
+
from moat.util import P, yload, Path
|
10
|
+
import asyncclick as click
|
11
|
+
|
12
|
+
|
13
|
+
def conv(m, s: str) -> bool:
|
14
|
+
try:
|
15
|
+
d = m.value[s]
|
16
|
+
except KeyError:
|
17
|
+
return 0
|
18
|
+
if isinstance(d, Path):
|
19
|
+
return 0
|
20
|
+
if not isinstance(d, Sequence):
|
21
|
+
return 0
|
22
|
+
d = Path.build(d)
|
23
|
+
m.value[s] = d
|
24
|
+
return 1
|
25
|
+
|
26
|
+
|
27
|
+
@click.command()
|
28
|
+
@click.argument("path", type=P)
|
29
|
+
@click.argument("keys", type=str, nargs=-1)
|
30
|
+
async def main(path, keys):
|
31
|
+
if not keys:
|
32
|
+
keys = "src dest dst state".split()
|
33
|
+
with open("/etc/moat.kv.cfg") as cff:
|
34
|
+
cfg = yload(cff)
|
35
|
+
async with open_client(**cfg) as client:
|
36
|
+
async for m in client.get_tree(path, nchain=2):
|
37
|
+
n = 0
|
38
|
+
for k in keys:
|
39
|
+
n += conv(m, k)
|
40
|
+
if n:
|
41
|
+
await client.set(ORIG + m.path, value=m.value, chain=m.chain)
|
42
|
+
|
43
|
+
|
44
|
+
if __name__ == "__main__":
|
45
|
+
main()
|
@@ -0,0 +1,98 @@
|
|
1
|
+
"""
|
2
|
+
This module implements a :class:`asyncactor.Actor` which works on top of
|
3
|
+
a MoaT-KV client.
|
4
|
+
"""
|
5
|
+
from __future__ import annotations
|
6
|
+
|
7
|
+
from asyncactor import Actor
|
8
|
+
from asyncactor.abc import MonitorStream, Transport
|
9
|
+
|
10
|
+
__all__ = [
|
11
|
+
"ClientActor",
|
12
|
+
"ActorState",
|
13
|
+
"BrokenState",
|
14
|
+
"DetachedState",
|
15
|
+
"PartialState",
|
16
|
+
"CompleteState",
|
17
|
+
]
|
18
|
+
|
19
|
+
|
20
|
+
class ClientActor(Actor):
|
21
|
+
def __init__(self, client, *a, topic, **kw):
|
22
|
+
super().__init__(ClientTransport(client, topic), *a, **kw)
|
23
|
+
|
24
|
+
|
25
|
+
class ClientTransport(Transport):
|
26
|
+
"""
|
27
|
+
This class exports the client's direct messaging interface to the
|
28
|
+
actor.
|
29
|
+
"""
|
30
|
+
|
31
|
+
def __init__(self, client, topic):
|
32
|
+
self.client = client
|
33
|
+
self.topic = topic
|
34
|
+
|
35
|
+
def monitor(self):
|
36
|
+
return ClientMonitor(self)
|
37
|
+
|
38
|
+
async def send(self, payload):
|
39
|
+
await self.client.msg_send(self.topic, payload)
|
40
|
+
|
41
|
+
|
42
|
+
class ClientMonitor(MonitorStream):
|
43
|
+
_mon1 = None
|
44
|
+
_mon2 = None
|
45
|
+
_it = None
|
46
|
+
|
47
|
+
async def __aenter__(self):
|
48
|
+
self._mon1 = self.transport.client.msg_monitor(self.transport.topic)
|
49
|
+
self._mon2 = await self._mon1.__aenter__()
|
50
|
+
return self
|
51
|
+
|
52
|
+
async def __aexit__(self, *tb):
|
53
|
+
return await self._mon1.__aexit__(*tb)
|
54
|
+
|
55
|
+
def __aiter__(self):
|
56
|
+
self._it = self._mon2.__aiter__()
|
57
|
+
return self
|
58
|
+
|
59
|
+
async def __anext__(self):
|
60
|
+
msg = await self._it.__anext__()
|
61
|
+
return msg.data
|
62
|
+
|
63
|
+
|
64
|
+
# The following events are used by Runner etc. to notify running jobs
|
65
|
+
# about the current connectivity state.
|
66
|
+
#
|
67
|
+
class ActorState:
|
68
|
+
"""base class for states"""
|
69
|
+
|
70
|
+
def __init__(self, msg=None):
|
71
|
+
self.msg = msg
|
72
|
+
|
73
|
+
def __repr__(self):
|
74
|
+
return "<%s:%r>" % (self.__class__.__name__, self.msg)
|
75
|
+
|
76
|
+
|
77
|
+
class BrokenState(ActorState):
|
78
|
+
"""I have no idea what's happening, probably nothing good"""
|
79
|
+
|
80
|
+
pass
|
81
|
+
|
82
|
+
|
83
|
+
class DetachedState(ActorState):
|
84
|
+
"""I am detached, my actor group is not visible"""
|
85
|
+
|
86
|
+
pass
|
87
|
+
|
88
|
+
|
89
|
+
class PartialState(ActorState):
|
90
|
+
"""Some but not all members of my actor group are visible"""
|
91
|
+
|
92
|
+
pass
|
93
|
+
|
94
|
+
|
95
|
+
class CompleteState(ActorState):
|
96
|
+
"""All members of my actor group are visible"""
|
97
|
+
|
98
|
+
pass
|
@@ -0,0 +1,139 @@
|
|
1
|
+
"""
|
2
|
+
This module implements additional code for the server-side DeleteActor,
|
3
|
+
which is used to clean up the list of deleted nodes.
|
4
|
+
"""
|
5
|
+
|
6
|
+
from __future__ import annotations
|
7
|
+
|
8
|
+
import weakref
|
9
|
+
from collections import deque
|
10
|
+
|
11
|
+
import anyio
|
12
|
+
from asyncactor import Actor, PingEvent, TagEvent
|
13
|
+
from asyncactor.backend import get_transport
|
14
|
+
|
15
|
+
TAGS = 4
|
16
|
+
|
17
|
+
|
18
|
+
class DeleteActor:
|
19
|
+
_enabled = None
|
20
|
+
|
21
|
+
def __init__(self, server):
|
22
|
+
self._server = weakref.ref(server)
|
23
|
+
self.deleted = deque()
|
24
|
+
self.tags = []
|
25
|
+
self.actor = None
|
26
|
+
|
27
|
+
self.max_seen = 0
|
28
|
+
self.n_tags = 0
|
29
|
+
self.n_pings = 0
|
30
|
+
self.n_nodes = 0
|
31
|
+
|
32
|
+
@property
|
33
|
+
def server(self):
|
34
|
+
return self._server()
|
35
|
+
|
36
|
+
async def tock_me(self):
|
37
|
+
"""
|
38
|
+
Add the current tock to our buffer.
|
39
|
+
|
40
|
+
This is updated whenever a new leader is selected.
|
41
|
+
"""
|
42
|
+
self.tags.append(self.server.tock)
|
43
|
+
self.tags = self.tags[-TAGS:]
|
44
|
+
await self.actor.set_value((self.tags[0], self.tags[-1]))
|
45
|
+
|
46
|
+
def add_deleted(self, nodes: NodeSet): # noqa: F821
|
47
|
+
"""
|
48
|
+
These nodes are deleted. Remember them for some time.
|
49
|
+
"""
|
50
|
+
if self.n_nodes == 0:
|
51
|
+
return
|
52
|
+
self.deleted.append((self.server.tock, nodes))
|
53
|
+
|
54
|
+
def purge_to(self, tock):
|
55
|
+
"""
|
56
|
+
Sufficient time has passed since this tock was seen, while all
|
57
|
+
Delete actor nodes were active. Finally flush the entries that have
|
58
|
+
been deleted before it.
|
59
|
+
"""
|
60
|
+
while self.deleted and self.deleted[0][0] < tock:
|
61
|
+
d = self.deleted.popleft()
|
62
|
+
self.server.purge_deleted(d[1])
|
63
|
+
|
64
|
+
async def enable(self, n):
|
65
|
+
"""
|
66
|
+
Enable this actor, as a group of N.
|
67
|
+
"""
|
68
|
+
if self.actor is None:
|
69
|
+
self._enabled = True
|
70
|
+
else:
|
71
|
+
await self.actor.enable(n)
|
72
|
+
self.n_tags = 0
|
73
|
+
self.n_pings = 0
|
74
|
+
self.n_nodes = n
|
75
|
+
|
76
|
+
async def disable(self, n: int = 0):
|
77
|
+
"""
|
78
|
+
Disable this actor. It will still listen, and require N Delete
|
79
|
+
actor members in order to flush its deletion entries.
|
80
|
+
|
81
|
+
Completely disable deletion flushing by passing n=0.
|
82
|
+
"""
|
83
|
+
if self.actor is None:
|
84
|
+
self._enabled = False
|
85
|
+
else:
|
86
|
+
await self.actor.disable()
|
87
|
+
self.n_tags = 0
|
88
|
+
self.n_pings = 0
|
89
|
+
self.n_nodes = n
|
90
|
+
|
91
|
+
async def run(self, evt: anyio.abc.Event = None):
|
92
|
+
"""
|
93
|
+
The task that monitors the Delete actor.
|
94
|
+
"""
|
95
|
+
try:
|
96
|
+
T = get_transport("moat_kv")
|
97
|
+
async with Actor(
|
98
|
+
T(self.server.backend, *self.server.cfg.server.root, "del"),
|
99
|
+
name=self.server.node.name,
|
100
|
+
cfg=self.server.cfg.server.delete,
|
101
|
+
enabled=False,
|
102
|
+
) as actor:
|
103
|
+
self.actor = actor
|
104
|
+
if self._enabled is not None:
|
105
|
+
if self._enabled:
|
106
|
+
await actor.enable()
|
107
|
+
else:
|
108
|
+
await actor.disable()
|
109
|
+
if evt is not None:
|
110
|
+
evt.set()
|
111
|
+
async for evt in actor:
|
112
|
+
if isinstance(evt, PingEvent):
|
113
|
+
val = evt.value
|
114
|
+
if val is None:
|
115
|
+
self.n_pings = self.n_tags = 0
|
116
|
+
continue
|
117
|
+
if len(evt.msg.history) < self.n_nodes:
|
118
|
+
self.n_pings = self.n_tags = 0
|
119
|
+
continue
|
120
|
+
self.n_pings += 1
|
121
|
+
if self.n_pings > self.n_nodes:
|
122
|
+
mx, self.max_seen = (
|
123
|
+
self.max_seen,
|
124
|
+
max(self.max_seen, val[1]),
|
125
|
+
)
|
126
|
+
if val[0] > mx > 0:
|
127
|
+
await self.server.resync_deleted(evt.msg.history)
|
128
|
+
continue
|
129
|
+
self.purge_to(val[0])
|
130
|
+
self.max_seen = max(self.max_seen, val[1])
|
131
|
+
|
132
|
+
elif isinstance(evt, TagEvent):
|
133
|
+
if actor.history_size == self.n_nodes:
|
134
|
+
self.n_tags += 1
|
135
|
+
if self.n_tags > 2:
|
136
|
+
self.purge_to(self.tags[0])
|
137
|
+
await self.tock_me()
|
138
|
+
finally:
|
139
|
+
self.actor = None
|