picopyn 0.3.0.dev1__tar.gz → 2.0.0.dev1__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.
- {picopyn-0.3.0.dev1/picopyn.egg-info → picopyn-2.0.0.dev1}/PKG-INFO +48 -13
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/README.md +40 -6
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/benchmark/bench.py +1 -1
- picopyn-2.0.0.dev1/docs/async.md +6 -0
- picopyn-2.0.0.dev1/docs/async_internal.md +18 -0
- picopyn-2.0.0.dev1/docs/bucket.md +7 -0
- picopyn-2.0.0.dev1/docs/changelog.md +3 -0
- picopyn-2.0.0.dev1/docs/contributing.md +7 -0
- picopyn-2.0.0.dev1/docs/diagrams/_pool_init.md +64 -0
- picopyn-2.0.0.dev1/docs/diagrams/_pool_init_discovery_disabled.md +46 -0
- picopyn-2.0.0.dev1/docs/diagrams/_pool_init_discovery_enabled.md +55 -0
- picopyn-2.0.0.dev1/docs/diagrams/_pool_lifecycle.md +44 -0
- picopyn-2.0.0.dev1/docs/diagrams/_query_metadata_pool_init.md +55 -0
- picopyn-2.0.0.dev1/docs/diagrams/_query_metadata_usage_lookup.md +44 -0
- picopyn-2.0.0.dev1/docs/diagrams/_reconcile_node_outside_pool_died.md +50 -0
- picopyn-2.0.0.dev1/docs/diagrams/_reconcile_pool_with_topology.md +46 -0
- picopyn-2.0.0.dev1/docs/diagrams/_reconcile_pooled_node_died_size_eq.md +50 -0
- picopyn-2.0.0.dev1/docs/diagrams/_reconcile_pooled_node_died_size_gt.md +54 -0
- picopyn-2.0.0.dev1/docs/diagrams/_reconcile_pooled_node_died_size_lt.md +48 -0
- picopyn-2.0.0.dev1/docs/diagrams/_request_if_missing.md +27 -0
- picopyn-2.0.0.dev1/docs/diagrams/_topology_apply_bucket_event.md +20 -0
- picopyn-2.0.0.dev1/docs/diagrams/_topology_apply_bucket_replicaset_event.md +52 -0
- picopyn-2.0.0.dev1/docs/diagrams/_topology_components.md +30 -0
- picopyn-2.0.0.dev1/docs/diagrams/_topology_refresh.md +52 -0
- picopyn-2.0.0.dev1/docs/diagrams/_topology_tracking.md +36 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/docs/explain.md +1 -1
- picopyn-2.0.0.dev1/docs/index.md +73 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/docs/installation.md +1 -1
- picopyn-2.0.0.dev1/docs/query_metadata.md +8 -0
- picopyn-2.0.0.dev1/docs/sync.md +17 -0
- picopyn-2.0.0.dev1/docs/topology.md +9 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/picopyn/__init__.py +1 -4
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/picopyn/asynchronous/__init__.py +0 -4
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/picopyn/asynchronous/client.py +10 -2
- picopyn-2.0.0.dev1/picopyn/asynchronous/connection.py +358 -0
- picopyn-2.0.0.dev1/picopyn/asynchronous/metadata_service.py +177 -0
- picopyn-2.0.0.dev1/picopyn/asynchronous/pool.py +1262 -0
- picopyn-2.0.0.dev1/picopyn/asynchronous/topology_tracker.py +408 -0
- picopyn-2.0.0.dev1/picopyn/bucket.py +256 -0
- picopyn-2.0.0.dev1/picopyn/query_metadata.py +227 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/picopyn/ssl.py +8 -3
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/picopyn/synchronous/__init__.py +6 -6
- picopyn-2.0.0.dev1/picopyn/synchronous/client.py +168 -0
- picopyn-2.0.0.dev1/picopyn/synchronous/connection.py +205 -0
- picopyn-2.0.0.dev1/picopyn/synchronous/cursor.py +272 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/picopyn/synchronous/errors.py +26 -0
- picopyn-2.0.0.dev1/picopyn/synchronous/pool.py +652 -0
- picopyn-2.0.0.dev1/picopyn/topology.py +422 -0
- picopyn-2.0.0.dev1/picopyn/utils/__init__.py +42 -0
- picopyn-2.0.0.dev1/picopyn/utils/discovery.py +108 -0
- picopyn-2.0.0.dev1/picopyn/utils/dsn.py +141 -0
- picopyn-2.0.0.dev1/picopyn/utils/queries.py +72 -0
- picopyn-2.0.0.dev1/picopyn/utils/topology.py +154 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1/picopyn.egg-info}/PKG-INFO +48 -13
- picopyn-2.0.0.dev1/picopyn.egg-info/SOURCES.txt +69 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/picopyn.egg-info/requires.txt +4 -5
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/pyproject.toml +10 -9
- picopyn-0.3.0.dev1/docs/async.md +0 -3
- picopyn-0.3.0.dev1/docs/index.md +0 -41
- picopyn-0.3.0.dev1/docs/sync.md +0 -3
- picopyn-0.3.0.dev1/picopyn/asynchronous/connection.py +0 -138
- picopyn-0.3.0.dev1/picopyn/asynchronous/pool.py +0 -500
- picopyn-0.3.0.dev1/picopyn/synchronous/connection.py +0 -62
- picopyn-0.3.0.dev1/picopyn/synchronous/cursor.py +0 -100
- picopyn-0.3.0.dev1/picopyn/utils/__init__.py +0 -16
- picopyn-0.3.0.dev1/picopyn/utils/discovery.py +0 -42
- picopyn-0.3.0.dev1/picopyn/utils/dsn.py +0 -48
- picopyn-0.3.0.dev1/picopyn/utils/queries.py +0 -21
- picopyn-0.3.0.dev1/picopyn.egg-info/SOURCES.txt +0 -38
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/AUTHORS.md +0 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/LICENSE +0 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/MANIFEST.in +0 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/benchmark/README.md +0 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/benchmark/examples/10k_rows_1_thread_1_batch/overall_speed_time.png +0 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/benchmark/examples/10k_rows_1_thread_1_batch/threads_speed_time.png +0 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/benchmark/examples/1m_rows_10_threads_100_batch/errors_cumulative.png +0 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/benchmark/examples/1m_rows_10_threads_100_batch/overall_speed_time.png +0 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/benchmark/examples/1m_rows_10_threads_100_batch/threads_speed_time.png +0 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/benchmark/generate_data_to_csv.py +0 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/picopyn/explain.py +0 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/picopyn.egg-info/dependency_links.txt +0 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/picopyn.egg-info/top_level.txt +0 -0
- {picopyn-0.3.0.dev1 → picopyn-2.0.0.dev1}/setup.cfg +0 -0
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: picopyn
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 2.0.0.dev1
|
|
4
4
|
Summary: Python connector for working with the distributed Picodata database.
|
|
5
|
-
Author-email: Asya Lomakina <a.lomakina@picodata.io>, Igor Kuznetsov <i.kuznetsov@picodata.io>, Dmitriy
|
|
5
|
+
Author-email: Asya Lomakina <a.lomakina@picodata.io>, Igor Kuznetsov <i.kuznetsov@picodata.io>, Dmitriy Koltsov <dkoltsov@picodata.io>, Daniil Gorlyakov <d.gorlyakov@picodata.io>
|
|
6
6
|
License-Expression: BSD-2-Clause
|
|
7
|
-
Project-URL: Source, https://git.picodata.io/
|
|
7
|
+
Project-URL: Source, https://git.picodata.io/core/drivers/picopyn
|
|
8
8
|
Project-URL: Documentation, https://picopyn.readthedocs.io/en/stable/
|
|
9
9
|
Project-URL: Changelog, https://git.picodata.io/core/drivers/picopyn/-/blob/main/CHANGELOG.md
|
|
10
10
|
Keywords: database,picodata
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
|
-
Classifier: Development Status ::
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
15
|
Classifier: Topic :: Database
|
|
16
16
|
Classifier: Framework :: AsyncIO
|
|
@@ -18,10 +18,11 @@ Requires-Python: >=3.10
|
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE
|
|
20
20
|
License-File: AUTHORS.md
|
|
21
|
-
Requires-Dist: async-timeout==5.0.1
|
|
22
21
|
Requires-Dist: asyncpg==0.30.0
|
|
23
|
-
Requires-Dist: typing_extensions
|
|
22
|
+
Requires-Dist: typing_extensions<5,>=4.12.2
|
|
24
23
|
Requires-Dist: psycopg[binary]==3.3.4
|
|
24
|
+
Requires-Dist: msgpack<2,>=1.0.0
|
|
25
|
+
Requires-Dist: mmh3<6,>=5.2.1
|
|
25
26
|
Provides-Extra: test
|
|
26
27
|
Requires-Dist: pytest==8.3.5; extra == "test"
|
|
27
28
|
Requires-Dist: pytest-asyncio==0.25.3; extra == "test"
|
|
@@ -29,7 +30,7 @@ Requires-Dist: ruff==0.11.13; extra == "test"
|
|
|
29
30
|
Requires-Dist: mypy==1.16.0; extra == "test"
|
|
30
31
|
Requires-Dist: black==25.1.0; extra == "test"
|
|
31
32
|
Provides-Extra: docs
|
|
32
|
-
Requires-Dist: mkdocs<2.0,>=1.
|
|
33
|
+
Requires-Dist: mkdocs<2.0,>=1.6; extra == "docs"
|
|
33
34
|
Requires-Dist: mkdocs-material; extra == "docs"
|
|
34
35
|
Requires-Dist: mkdocstrings[python]; extra == "docs"
|
|
35
36
|
Provides-Extra: benchmark
|
|
@@ -48,11 +49,23 @@ Dynamic: license-file
|
|
|
48
49
|
|
|
49
50
|
**Picopyn** is a Python package for working with the distributed [Picodata](https://picodata.io/en/) database.
|
|
50
51
|
|
|
52
|
+
**Version compatibility**:
|
|
53
|
+
| Picodata version | Picopyn version |
|
|
54
|
+
| ----------------- | ----------------- |
|
|
55
|
+
| >=25.2.1, <25.4.4 | 0.1.1 |
|
|
56
|
+
| >=25.4.4, <25.5.1 | 0.2.0 |
|
|
57
|
+
| >=25.5.1, <26.1.x | 1.0.0 |
|
|
58
|
+
| >=26.1.1, <26.2.x | 2.0.0 |
|
|
59
|
+
| 26.2.x | ⚠️ Unreleased yet |
|
|
60
|
+
|
|
51
61
|
## Features
|
|
52
62
|
* Connection pooling with configurable pool size
|
|
53
63
|
* Optional automatic node discovery
|
|
54
64
|
* Pluggable load-balancing strategies
|
|
55
|
-
*
|
|
65
|
+
* Asynchronous API based on [asyncpg](https://github.com/MagicStack/asyncpg)
|
|
66
|
+
* (async only) Shard-aware routing
|
|
67
|
+
* (async only) Automatic pool membership reconciliation
|
|
68
|
+
* Synchronous [DPAPI-compatible](https://peps.python.org/pep-0249/) API based on [psycopg](https://www.psycopg.org/)
|
|
56
69
|
|
|
57
70
|
## Navigation
|
|
58
71
|
* [Installation](#installation)
|
|
@@ -73,16 +86,18 @@ pip install picopyn
|
|
|
73
86
|
Or from source:
|
|
74
87
|
|
|
75
88
|
```sh
|
|
76
|
-
git clone https://git.picodata.io/
|
|
89
|
+
git clone https://git.picodata.io/core/drivers/picopyn.git
|
|
77
90
|
cd picopyn
|
|
78
91
|
pip install -e .
|
|
79
92
|
```
|
|
80
93
|
|
|
81
94
|
## Quickstart
|
|
82
95
|
|
|
96
|
+
Async:
|
|
97
|
+
|
|
83
98
|
```python
|
|
84
99
|
import asyncio
|
|
85
|
-
from picopyn import Client
|
|
100
|
+
from picopyn.asynchronous import Client
|
|
86
101
|
|
|
87
102
|
async def main():
|
|
88
103
|
# create and connect client to the picodata cluster
|
|
@@ -94,9 +109,9 @@ async def main():
|
|
|
94
109
|
CREATE TABLE "warehouse" (id INTEGER NOT NULL, item TEXT NOT NULL, PRIMARY KEY (id)) USING memtx DISTRIBUTED BY (id) OPTION (TIMEOUT = 3.0);
|
|
95
110
|
''')
|
|
96
111
|
|
|
97
|
-
# execute DML operations
|
|
98
|
-
await client.execute('INSERT INTO
|
|
99
|
-
rows = await client.fetch('SELECT * FROM
|
|
112
|
+
# execute DML/DQL operations
|
|
113
|
+
await client.execute('INSERT INTO "warehouse" VALUES ($1::int, $2::varchar)', 1, "test")
|
|
114
|
+
rows = await client.fetch('SELECT * FROM "warehouse"')
|
|
100
115
|
print(rows)
|
|
101
116
|
|
|
102
117
|
await client.close()
|
|
@@ -104,6 +119,26 @@ async def main():
|
|
|
104
119
|
asyncio.run(main())
|
|
105
120
|
```
|
|
106
121
|
|
|
122
|
+
Sync:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from picopyn.synchronous import connect
|
|
126
|
+
|
|
127
|
+
# create and connect to the picodata cluster
|
|
128
|
+
with connect("postgresql://admin:pass@localhost:5432") as conn:
|
|
129
|
+
cur = conn.cursor()
|
|
130
|
+
|
|
131
|
+
# execute DDL operations
|
|
132
|
+
cur.execute('''
|
|
133
|
+
CREATE TABLE "warehouse" (id INTEGER NOT NULL, item TEXT NOT NULL, PRIMARY KEY (id)) USING memtx DISTRIBUTED BY (id) OPTION (TIMEOUT = 3.0);
|
|
134
|
+
''')
|
|
135
|
+
|
|
136
|
+
# execute DML/DQL operations
|
|
137
|
+
cur.execute('INSERT INTO "warehouse" VALUES (%s, %s)', (1, "test"))
|
|
138
|
+
cur.execute('SELECT * FROM "warehouse"')
|
|
139
|
+
print(cur.fetchall())
|
|
140
|
+
```
|
|
141
|
+
|
|
107
142
|
## Development
|
|
108
143
|
For development we use [uv](https://docs.astral.sh/uv/getting-started/installation/) as package manager and [docker compose](https://docs.docker.com/compose/install/) for test environment.
|
|
109
144
|
|
|
@@ -6,11 +6,23 @@
|
|
|
6
6
|
|
|
7
7
|
**Picopyn** is a Python package for working with the distributed [Picodata](https://picodata.io/en/) database.
|
|
8
8
|
|
|
9
|
+
**Version compatibility**:
|
|
10
|
+
| Picodata version | Picopyn version |
|
|
11
|
+
| ----------------- | ----------------- |
|
|
12
|
+
| >=25.2.1, <25.4.4 | 0.1.1 |
|
|
13
|
+
| >=25.4.4, <25.5.1 | 0.2.0 |
|
|
14
|
+
| >=25.5.1, <26.1.x | 1.0.0 |
|
|
15
|
+
| >=26.1.1, <26.2.x | 2.0.0 |
|
|
16
|
+
| 26.2.x | ⚠️ Unreleased yet |
|
|
17
|
+
|
|
9
18
|
## Features
|
|
10
19
|
* Connection pooling with configurable pool size
|
|
11
20
|
* Optional automatic node discovery
|
|
12
21
|
* Pluggable load-balancing strategies
|
|
13
|
-
*
|
|
22
|
+
* Asynchronous API based on [asyncpg](https://github.com/MagicStack/asyncpg)
|
|
23
|
+
* (async only) Shard-aware routing
|
|
24
|
+
* (async only) Automatic pool membership reconciliation
|
|
25
|
+
* Synchronous [DPAPI-compatible](https://peps.python.org/pep-0249/) API based on [psycopg](https://www.psycopg.org/)
|
|
14
26
|
|
|
15
27
|
## Navigation
|
|
16
28
|
* [Installation](#installation)
|
|
@@ -31,16 +43,18 @@ pip install picopyn
|
|
|
31
43
|
Or from source:
|
|
32
44
|
|
|
33
45
|
```sh
|
|
34
|
-
git clone https://git.picodata.io/
|
|
46
|
+
git clone https://git.picodata.io/core/drivers/picopyn.git
|
|
35
47
|
cd picopyn
|
|
36
48
|
pip install -e .
|
|
37
49
|
```
|
|
38
50
|
|
|
39
51
|
## Quickstart
|
|
40
52
|
|
|
53
|
+
Async:
|
|
54
|
+
|
|
41
55
|
```python
|
|
42
56
|
import asyncio
|
|
43
|
-
from picopyn import Client
|
|
57
|
+
from picopyn.asynchronous import Client
|
|
44
58
|
|
|
45
59
|
async def main():
|
|
46
60
|
# create and connect client to the picodata cluster
|
|
@@ -52,9 +66,9 @@ async def main():
|
|
|
52
66
|
CREATE TABLE "warehouse" (id INTEGER NOT NULL, item TEXT NOT NULL, PRIMARY KEY (id)) USING memtx DISTRIBUTED BY (id) OPTION (TIMEOUT = 3.0);
|
|
53
67
|
''')
|
|
54
68
|
|
|
55
|
-
# execute DML operations
|
|
56
|
-
await client.execute('INSERT INTO
|
|
57
|
-
rows = await client.fetch('SELECT * FROM
|
|
69
|
+
# execute DML/DQL operations
|
|
70
|
+
await client.execute('INSERT INTO "warehouse" VALUES ($1::int, $2::varchar)', 1, "test")
|
|
71
|
+
rows = await client.fetch('SELECT * FROM "warehouse"')
|
|
58
72
|
print(rows)
|
|
59
73
|
|
|
60
74
|
await client.close()
|
|
@@ -62,6 +76,26 @@ async def main():
|
|
|
62
76
|
asyncio.run(main())
|
|
63
77
|
```
|
|
64
78
|
|
|
79
|
+
Sync:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from picopyn.synchronous import connect
|
|
83
|
+
|
|
84
|
+
# create and connect to the picodata cluster
|
|
85
|
+
with connect("postgresql://admin:pass@localhost:5432") as conn:
|
|
86
|
+
cur = conn.cursor()
|
|
87
|
+
|
|
88
|
+
# execute DDL operations
|
|
89
|
+
cur.execute('''
|
|
90
|
+
CREATE TABLE "warehouse" (id INTEGER NOT NULL, item TEXT NOT NULL, PRIMARY KEY (id)) USING memtx DISTRIBUTED BY (id) OPTION (TIMEOUT = 3.0);
|
|
91
|
+
''')
|
|
92
|
+
|
|
93
|
+
# execute DML/DQL operations
|
|
94
|
+
cur.execute('INSERT INTO "warehouse" VALUES (%s, %s)', (1, "test"))
|
|
95
|
+
cur.execute('SELECT * FROM "warehouse"')
|
|
96
|
+
print(cur.fetchall())
|
|
97
|
+
```
|
|
98
|
+
|
|
65
99
|
## Development
|
|
66
100
|
For development we use [uv](https://docs.astral.sh/uv/getting-started/installation/) as package manager and [docker compose](https://docs.docker.com/compose/install/) for test environment.
|
|
67
101
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Async: Internal
|
|
2
|
+
|
|
3
|
+
!!! warning
|
|
4
|
+
Not public API. Driver wires this up and owns it; nothing here is meant
|
|
5
|
+
to be constructed or called directly. Documented anyway so you can see
|
|
6
|
+
how it actually works
|
|
7
|
+
|
|
8
|
+
::: picopyn.asynchronous.topology_tracker
|
|
9
|
+
options:
|
|
10
|
+
show_root_heading: true
|
|
11
|
+
filters:
|
|
12
|
+
- "!^_"
|
|
13
|
+
|
|
14
|
+
::: picopyn.asynchronous.metadata_service
|
|
15
|
+
options:
|
|
16
|
+
show_root_heading: true
|
|
17
|
+
filters:
|
|
18
|
+
- "!^_"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Contributions are welcome! The project is hosted on GitLab:
|
|
4
|
+
|
|
5
|
+
**[git.picodata.io/core/drivers/picopyn](https://git.picodata.io/core/drivers/picopyn)**
|
|
6
|
+
|
|
7
|
+
To get started with the development environment, running tests, and code style requirements, see the [Development](https://git.picodata.io/core/drivers/picopyn#development) section in the project README.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
---
|
|
3
|
+
title: Pool initialization
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
flowchart TD
|
|
7
|
+
%% === Initialization ===
|
|
8
|
+
classDef success fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
|
|
9
|
+
classDef error fill:#ffebee,stroke:#f44336,color:#b71c1c
|
|
10
|
+
classDef neutral fill:#f5f5f5,stroke:#9e9e9e,color:#424242
|
|
11
|
+
classDef note fill:#fffde7,stroke:#f9a825,stroke-dasharray:4,color:#555
|
|
12
|
+
|
|
13
|
+
START([Start]):::neutral
|
|
14
|
+
DONE([Pool ready]):::success
|
|
15
|
+
ERROR([Initialization failed]):::error
|
|
16
|
+
CFG[/"Pool settings<br>· DSN string<br>· pool size<br>· forbidden tiers<br>· cluster discovery mode"/]
|
|
17
|
+
|
|
18
|
+
USAGE_DETAILS_NOTE>"Return to pool lifecycle details"]:::note
|
|
19
|
+
click USAGE_DETAILS_NOTE "#picopyn.asynchronous.Pool" "Return to pool lifecycle details"
|
|
20
|
+
|
|
21
|
+
%% === Data objects ===
|
|
22
|
+
POOL[[Connection pool]]
|
|
23
|
+
B_DSN[[DSN node list]]
|
|
24
|
+
B_SYS[(Picodata system tables)]
|
|
25
|
+
D_DSN[[DSN node list]]
|
|
26
|
+
D_SYS[(Picodata system tables)]
|
|
27
|
+
CAND[[Candidate node list]]
|
|
28
|
+
|
|
29
|
+
%% === Actions ===
|
|
30
|
+
|
|
31
|
+
START --> CFG --> PARSE[Parse DSN string to DSN node list] --> DISCOVERY{Cluster discovery enabled?}
|
|
32
|
+
|
|
33
|
+
DISCOVERY -->|yes| D_FILTER[Use DSN nodes to connect to<br>Picodata cluster and retrieve<br>online nodes with allowed tiers<br>as a candidate node list]
|
|
34
|
+
DISCOVERY -->|no| B_FILTER[Filter DSN nodes: use only online<br>nodes with allowed tiers]
|
|
35
|
+
|
|
36
|
+
subgraph DiscoverMode["Cluster discovery mode"]
|
|
37
|
+
D_FILTER --> D_FILL_POOL[Try to fill pool with connections<br>to candidates up to pool size]
|
|
38
|
+
|
|
39
|
+
D_DETAILS_NOTE>"Discovery mode details"]:::note
|
|
40
|
+
click D_DETAILS_NOTE "#pool-init-discovery-enabled" "Discovery mode details"
|
|
41
|
+
|
|
42
|
+
D_FILTER <-. write .-> CAND
|
|
43
|
+
CAND <-. read .-> D_FILL_POOL
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
subgraph BootstrapMode["Bootstrap mode"]
|
|
47
|
+
B_FILTER --> B_FILL_POOL[Try to fill pool with connections<br>to DSN nodes up to pool size]
|
|
48
|
+
|
|
49
|
+
B_DETAILS_NOTE>"Bootstrap mode details"]:::note
|
|
50
|
+
click B_DETAILS_NOTE "#pool-init-discovery-disabled" "Bootstrap mode details"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
B_DSN <-. read .-> B_FILTER
|
|
54
|
+
B_SYS <-. read .-> B_FILTER
|
|
55
|
+
B_FILL_POOL <-. write .-> POOL
|
|
56
|
+
|
|
57
|
+
D_DSN <-. read .-> D_FILTER
|
|
58
|
+
D_SYS <-. read .-> D_FILTER
|
|
59
|
+
D_FILL_POOL <-. write .-> POOL
|
|
60
|
+
|
|
61
|
+
POOL <-.-> POOL_CHECK{Pool full?}
|
|
62
|
+
POOL_CHECK -->|no| ERROR
|
|
63
|
+
POOL_CHECK -->|yes| DONE
|
|
64
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
---
|
|
3
|
+
title: Pool initialization with cluster discovery disabled
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
flowchart TD
|
|
7
|
+
|
|
8
|
+
%% === Initialization ===
|
|
9
|
+
classDef success fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
|
|
10
|
+
classDef error fill:#ffebee,stroke:#f44336,color:#b71c1c
|
|
11
|
+
classDef neutral fill:#f5f5f5,stroke:#9e9e9e,color:#424242
|
|
12
|
+
classDef note fill:#fffde7,stroke:#f9a825,stroke-dasharray:4,color:#555
|
|
13
|
+
|
|
14
|
+
START([Start]):::neutral
|
|
15
|
+
DONE([Pool ready]):::success
|
|
16
|
+
ERROR([Initialization failed]):::error
|
|
17
|
+
CFG[/"Pool settings<br>· DSN string<br>· pool size<br>· forbidden tiers<br>· cluster discovery disabled"/]
|
|
18
|
+
|
|
19
|
+
GENERAL_DETAILS_NOTE>"Return to general algorithm"]:::note
|
|
20
|
+
click GENERAL_DETAILS_NOTE "#picopyn.asynchronous.Pool.connect" "Return to general algorithm"
|
|
21
|
+
|
|
22
|
+
%% === Data objects ===
|
|
23
|
+
B_DSN[[DSN node list]]
|
|
24
|
+
BOOTSTRAP_POOL[[Connection pool]]
|
|
25
|
+
SYS[(Picodata system tables)]
|
|
26
|
+
|
|
27
|
+
%% === Bootstrap mode ===
|
|
28
|
+
START --> CFG --> PARSE[Parse DSN string to DSN node list] --> B_PoolFilling
|
|
29
|
+
PARSE <-. write .-> B_DSN
|
|
30
|
+
B_DSN <-. read .-> B_CONNECT_NODE[Connect to DSN node]
|
|
31
|
+
|
|
32
|
+
B_PoolFilling_NOTE>"iterate over DSN nodes until either:<br>- the list is empty<br>- pool_size connections<br>have been established"]:::note
|
|
33
|
+
B_PoolFilling_NOTE -.-> B_PoolFilling
|
|
34
|
+
subgraph B_PoolFilling["Pool filling"]
|
|
35
|
+
direction TB
|
|
36
|
+
SYS <-. read .-> B_CHECK_NODE_TIER[Check node tier]
|
|
37
|
+
B_CONNECT_NODE --> B_CHECK_NODE_TIER
|
|
38
|
+
B_CHECK_NODE_TIER -->|node offline or tier forbidden| B_EXCLUDE[Exclude DSN node]
|
|
39
|
+
B_CHECK_NODE_TIER -->|node online and tier allowed| B_SAVE[Save connection to pool]
|
|
40
|
+
B_SAVE <-. write .-> BOOTSTRAP_POOL
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
B_PoolFilling --> POOL_CHECK{Pool full?}
|
|
44
|
+
POOL_CHECK -->|no| ERROR
|
|
45
|
+
POOL_CHECK -->|yes| DONE
|
|
46
|
+
```
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
---
|
|
3
|
+
title: Pool initialization with cluster discovery enabled
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
flowchart TD
|
|
7
|
+
|
|
8
|
+
%% === Initialization ===
|
|
9
|
+
classDef success fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
|
|
10
|
+
classDef error fill:#ffebee,stroke:#f44336,color:#b71c1c
|
|
11
|
+
classDef neutral fill:#f5f5f5,stroke:#9e9e9e,color:#424242
|
|
12
|
+
classDef note fill:#fffde7,stroke:#f9a825,stroke-dasharray:4,color:#555
|
|
13
|
+
|
|
14
|
+
START([Start]):::neutral
|
|
15
|
+
DONE([Pool ready]):::success
|
|
16
|
+
D_ERROR_CAND([Initialization failed]):::error
|
|
17
|
+
ERROR([Initialization failed]):::error
|
|
18
|
+
CFG[/"Pool settings<br>· DSN string<br>· pool_size<br>· forbidden tiers<br>· cluster discovery enabled"/]
|
|
19
|
+
|
|
20
|
+
GENERAL_DETAILS_NOTE>"Return to general algorithm"]:::note
|
|
21
|
+
click GENERAL_DETAILS_NOTE "#picopyn.asynchronous.Pool.connect" "Return to general algorithm"
|
|
22
|
+
|
|
23
|
+
%% === Data objects ===
|
|
24
|
+
D_DSN[[DSN node list]]
|
|
25
|
+
CAND[[Candidate node list]]
|
|
26
|
+
DISCOVERY_POOL[[Connection pool]]
|
|
27
|
+
SYS[(Picodata system tables)]
|
|
28
|
+
|
|
29
|
+
%% === Discovery mode ===
|
|
30
|
+
START --> CFG --> PARSE[Parse DSN string to<br>DSN node list] --> DiscoverNodes
|
|
31
|
+
PARSE <-. write .-> D_DSN
|
|
32
|
+
|
|
33
|
+
D_DiscoverNodes_NOTE>"iterate over DSN nodes<br>to establish a temporary<br>connection and retrieve the<br>list of online nodes with<br>allowed tiers"]:::note
|
|
34
|
+
D_DiscoverNodes_NOTE -.-> DiscoverNodes
|
|
35
|
+
D_DSN <-. read .-> D_RECEIVE[Try to connect and receive cluster nodes]
|
|
36
|
+
subgraph DiscoverNodes["Discover cluster nodes"]
|
|
37
|
+
SYS <-. read .-> D_RECEIVE
|
|
38
|
+
D_RECEIVE -->|could not select candidates| D_ERROR_CAND
|
|
39
|
+
D_RECEIVE <-. write .-> CAND
|
|
40
|
+
end
|
|
41
|
+
DiscoverNodes --> D_PoolFilling
|
|
42
|
+
|
|
43
|
+
D_PoolFilling_NOTE>"iterate over candidate nodes<br>until either:<br>- the list is empty<br>- pool_size connections<br>have been established"]:::note
|
|
44
|
+
D_PoolFilling_NOTE -.-> D_PoolFilling
|
|
45
|
+
CAND <-. read .-> D_CONNECT_NODE[Connect to candidate node]
|
|
46
|
+
subgraph D_PoolFilling["Pool filling"]
|
|
47
|
+
D_CONNECT_NODE -->|connection error| D_EXCLUDE[Exclude candidate node]
|
|
48
|
+
D_CONNECT_NODE -->|connected| D_SAVE[Save opened connection to pool]
|
|
49
|
+
D_SAVE <-. write .-> DISCOVERY_POOL
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
D_PoolFilling --> POOL_CHECK{Pool full?}
|
|
53
|
+
POOL_CHECK -->|no| ERROR
|
|
54
|
+
POOL_CHECK -->|yes| DONE
|
|
55
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
---
|
|
3
|
+
title: Pool lifecycle
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
flowchart TD
|
|
7
|
+
%% === Style ===
|
|
8
|
+
classDef neutral fill:#f5f5f5,stroke:#9e9e9e,color:#424242
|
|
9
|
+
classDef note fill:#fffde7,stroke:#f9a825,stroke-dasharray:4,color:#555
|
|
10
|
+
|
|
11
|
+
%% === Data objects ===
|
|
12
|
+
POOL[[Connection pool]]
|
|
13
|
+
|
|
14
|
+
%% === Lifecycle ===
|
|
15
|
+
START([Start]):::neutral
|
|
16
|
+
DONE([Pool closed]):::neutral
|
|
17
|
+
|
|
18
|
+
CFG[/"Pool settings<br>· DSN<br>· pool size<br>· forbidden tiers<br>· cluster discovery mode"/]
|
|
19
|
+
|
|
20
|
+
START --> CFG --> CREATE["Pool.__init__"] --> CONNECT["pool.connect()"] --> UsePool
|
|
21
|
+
CONNECT <-. write .-> POOL
|
|
22
|
+
|
|
23
|
+
CONNECT_DETAILS_NOTE>"Connect algorithm details"]:::note
|
|
24
|
+
click CONNECT_DETAILS_NOTE "#picopyn.asynchronous.Pool.connect" "Connect algorithm details"
|
|
25
|
+
CONNECT_DETAILS_NOTE -.-> CONNECT
|
|
26
|
+
|
|
27
|
+
RECONCILE_NOTE>"(discovery mode only):<br>pool membership changes<br>in the background:<br>connections are opened/closed<br>to match topology changes"]:::note
|
|
28
|
+
click RECONCILE_NOTE "#picopyn.asynchronous.Pool.topology" "Reconcile algorithm details"
|
|
29
|
+
RECONCILE_NOTE -.-> POOL
|
|
30
|
+
|
|
31
|
+
CYCLE_NOTE>"repeated for each<br>database operation"]:::note
|
|
32
|
+
CYCLE_NOTE -.-> UsePool
|
|
33
|
+
|
|
34
|
+
subgraph UsePool["Pool usage"]
|
|
35
|
+
direction TB
|
|
36
|
+
ACQUIRE["pool.acquire()"] --> USE["Execute queries<br>· pool.fetchrow()<br>· pool.fetch()<br>· pool.execute()"] --> RELEASE["pool.release(conn)"]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
ACQUIRE <-. delete .-> POOL
|
|
40
|
+
RELEASE <-. write .-> POOL
|
|
41
|
+
CLOSE <-. delete .-> POOL
|
|
42
|
+
UsePool --> CLOSE
|
|
43
|
+
CLOSE --> DONE
|
|
44
|
+
```
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
---
|
|
3
|
+
title: Query metadata -- pool initialization in cache meta context
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
flowchart TD
|
|
7
|
+
classDef neutral fill:#f5f5f5,stroke:#9e9e9e,color:#424242
|
|
8
|
+
classDef note fill:#fffde7,stroke:#f9a825,stroke-dasharray:4,color:#555
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
INIT(["Pool init"]):::neutral
|
|
12
|
+
|
|
13
|
+
INIT --> CACHE_EMPTY["Create empty query meta cache"] --> CONNECT["Fill the pool with ordinary<br>connections to picodata"] --> Pool1
|
|
14
|
+
|
|
15
|
+
NOTE_CONN>"connection options require<br>statement invalidation signal only"]:::note
|
|
16
|
+
NOTE_CONN -.-> CONNECT
|
|
17
|
+
|
|
18
|
+
CONNECT_DETAILS_NOTE>"Connect algorithm details"]:::note
|
|
19
|
+
click CONNECT_DETAILS_NOTE "#picopyn.asynchronous.Pool.connect" "Connect algorithm details"
|
|
20
|
+
CONNECT_DETAILS_NOTE -.-> CONNECT
|
|
21
|
+
|
|
22
|
+
subgraph Pool1["Pool"]
|
|
23
|
+
direction TB
|
|
24
|
+
|
|
25
|
+
CONN11["conn to inst1"] --> PICO11[(Picodata)]
|
|
26
|
+
CONN12["..."] --> PICO12[(Picodata)]
|
|
27
|
+
CONN1N["conn to instN"] --> PICO1N[(Picodata)]
|
|
28
|
+
|
|
29
|
+
CACHE1[/"Query meta cache"/]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Pool1 --> CONNECT_SERV["create service connection<br>for meta listener"]
|
|
33
|
+
|
|
34
|
+
NOTE_CONN_SERV>"connection options require statement invalidation signal<br>and distribution key metadata"]:::note
|
|
35
|
+
NOTE_CONN_SERV -.-> CONNECT_SERV
|
|
36
|
+
|
|
37
|
+
CONNECT_SERV --> LIST["add picodata notice listener"]
|
|
38
|
+
|
|
39
|
+
LIST --> Pool
|
|
40
|
+
|
|
41
|
+
subgraph Pool["Pool"]
|
|
42
|
+
direction TB
|
|
43
|
+
|
|
44
|
+
CONN1["conn1 for<br>user usage"] --> PICO1[(Picodata1)]
|
|
45
|
+
CONN2["..."] --> PICO2[(...)]
|
|
46
|
+
CONNN["connN for<br>user usage"] --> PICON[(PicodataN)]
|
|
47
|
+
|
|
48
|
+
PIC(["can be any of"]) -.- CONNSERV["service connection with Notice listener"]
|
|
49
|
+
PICO1 -.- PIC
|
|
50
|
+
PICO2 -.- PIC
|
|
51
|
+
PICON -.- PIC
|
|
52
|
+
|
|
53
|
+
CACHE[/"Query meta cache"/]
|
|
54
|
+
end
|
|
55
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
---
|
|
3
|
+
title: Query metadata -- usage lookup
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
flowchart TD
|
|
7
|
+
|
|
8
|
+
classDef neutral fill:#f5f5f5,stroke:#9e9e9e,color:#424242
|
|
9
|
+
classDef note fill:#fffde7,stroke:#f9a825,stroke-dasharray:4,color:#555
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
QUERY(["parametrized query"]):::neutral --> CALL(["Pool.execute"]) --> HASH
|
|
13
|
+
|
|
14
|
+
HASH["hash the query text"] --> SEARCH_CACHE{"cached meta by hash?"}
|
|
15
|
+
|
|
16
|
+
CACHE1[/"Pool query meta cache"/] -. read .- SEARCH_CACHE
|
|
17
|
+
SEARCH_CACHE -->|yes| USE_CACHE["metadata found in cache"]
|
|
18
|
+
SEARCH_CACHE -->|no| CACHE_MISS["no metadata yet --<br>schedule background prepare"] --> ACQUIRE_CONN
|
|
19
|
+
|
|
20
|
+
subgraph Routing["BUCKET ROUTING"]
|
|
21
|
+
direction TB
|
|
22
|
+
|
|
23
|
+
CALC["calculate `bucket_id`<br>from dk_meta"]
|
|
24
|
+
|
|
25
|
+
CALC --> MAP_CONN["find bucket master<br>for this tier"]
|
|
26
|
+
|
|
27
|
+
MAP_CONN -->|routed| CALL_THE_CONN["run the query on<br>the chosen connection"]
|
|
28
|
+
MAP_CONN -->|cannot route| ACQUIRE_CONN
|
|
29
|
+
|
|
30
|
+
CALL_THE_CONN --> RESULT(["give result to the user"]):::neutral
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
USE_CACHE --> CALC
|
|
34
|
+
|
|
35
|
+
subgraph Fallback["FALLBACK"]
|
|
36
|
+
direction TB
|
|
37
|
+
|
|
38
|
+
ACQUIRE_CONN["acquire pool connection<br>via the configured balance strategy"]
|
|
39
|
+
|
|
40
|
+
ACQUIRE_CONN --> CALL_CONN["run the query on<br>the chosen connection"]
|
|
41
|
+
|
|
42
|
+
CALL_CONN --> RESULT2(["give result to the user"]):::neutral
|
|
43
|
+
end
|
|
44
|
+
```
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
---
|
|
3
|
+
title: A node outside the pool died
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
flowchart TD
|
|
7
|
+
|
|
8
|
+
classDef offline fill:#ffebee,stroke:#f44336,color:#b71c1c
|
|
9
|
+
classDef online fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
|
|
10
|
+
classDef neutral fill:#f5f5f5,stroke:#9e9e9e,color:#424242
|
|
11
|
+
|
|
12
|
+
classDef note fill:#fffde7,stroke:#f9a825,stroke-dasharray:4,color:#555
|
|
13
|
+
|
|
14
|
+
subgraph before["Before"]
|
|
15
|
+
subgraph Picodata_before["Picodata"]
|
|
16
|
+
p1[("ins 1")]:::online
|
|
17
|
+
p2[("ins 2")]:::online
|
|
18
|
+
p3[("ins 3")]:::online
|
|
19
|
+
p4[("ins 4")]:::online
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
subgraph Pool_before["Pool: max_size=3"]
|
|
23
|
+
c1["conn"] --> p1
|
|
24
|
+
c2["conn"] --> p2
|
|
25
|
+
c3["conn"] --> p3
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
EVENT1[/"Event<br>Instance 4 dead"/]:::neutral -.-> REFILL{{"reconcile pool"}}
|
|
29
|
+
before -.-> REFILL
|
|
30
|
+
|
|
31
|
+
NOTE>"Node outside the pool died<br>=> pool stays unchanged"]:::note
|
|
32
|
+
NOTE-.->REFILL
|
|
33
|
+
|
|
34
|
+
REFILL --> after
|
|
35
|
+
|
|
36
|
+
subgraph after["After"]
|
|
37
|
+
subgraph Picodata_after["Picodata"]
|
|
38
|
+
p21[("ins 1")]:::online
|
|
39
|
+
p22[("ins 2")]:::online
|
|
40
|
+
p23[("ins 3")]:::online
|
|
41
|
+
p24[("ins 4")]:::offline
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
subgraph Pool_after["Pool: max_size=3"]
|
|
45
|
+
c21["conn"] --> p21
|
|
46
|
+
c22["conn"] --> p22
|
|
47
|
+
c23["conn"] --> p23
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
```
|