picopyn 2.0.0.dev1__tar.gz → 3.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.
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/AUTHORS.md +2 -1
- {picopyn-2.0.0.dev1/picopyn.egg-info → picopyn-3.0.0}/PKG-INFO +13 -13
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/README.md +12 -12
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/benchmark/bench.py +18 -15
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_pool_init.md +1 -1
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_pool_init_discovery_disabled.md +2 -2
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_pool_init_discovery_enabled.md +3 -3
- picopyn-2.0.0.dev1/docs/diagrams/_pool_lifecycle.md → picopyn-3.0.0/docs/diagrams/_pool_lifecycle_async.md +7 -7
- picopyn-3.0.0/docs/diagrams/_pool_lifecycle_sync.md +44 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_query_metadata_pool_init.md +10 -9
- picopyn-3.0.0/docs/diagrams/_query_metadata_usage_lookup.md +37 -0
- picopyn-3.0.0/docs/diagrams/_rebalance_pool_after_reconcile.md +50 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_reconcile_pool_with_topology.md +12 -4
- picopyn-3.0.0/docs/diagrams/_reconcile_rebalance.md +45 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_request_if_missing.md +0 -2
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_topology_components.md +2 -2
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_topology_refresh.md +1 -1
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_topology_tracking.md +2 -2
- picopyn-3.0.0/docs/explain.md +21 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/index.md +18 -11
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/installation.md +1 -1
- picopyn-3.0.0/docs/sharding.md +7 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/sync.md +4 -4
- picopyn-3.0.0/docs/sync_internal.md +18 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/__init__.py +3 -0
- picopyn-3.0.0/picopyn/asynchronous/__init__.py +9 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/asynchronous/connection.py +45 -9
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/asynchronous/metadata_service.py +40 -8
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/asynchronous/pool.py +731 -193
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/asynchronous/topology_tracker.py +17 -72
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/bucket.py +116 -11
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/explain.py +159 -1
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/query_metadata.py +62 -37
- picopyn-3.0.0/picopyn/sharding.py +291 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/synchronous/__init__.py +8 -3
- picopyn-3.0.0/picopyn/synchronous/connection.py +402 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/synchronous/cursor.py +34 -0
- picopyn-3.0.0/picopyn/synchronous/metadata_service.py +272 -0
- picopyn-3.0.0/picopyn/synchronous/pool.py +1756 -0
- picopyn-3.0.0/picopyn/synchronous/topology_tracker.py +475 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/topology.py +103 -39
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/utils/__init__.py +38 -5
- picopyn-3.0.0/picopyn/utils/balance.py +270 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/utils/discovery.py +59 -3
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/utils/dsn.py +28 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/utils/queries.py +34 -10
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/utils/topology.py +102 -10
- {picopyn-2.0.0.dev1 → picopyn-3.0.0/picopyn.egg-info}/PKG-INFO +13 -13
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn.egg-info/SOURCES.txt +10 -3
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/pyproject.toml +6 -3
- picopyn-2.0.0.dev1/docs/diagrams/_query_metadata_usage_lookup.md +0 -44
- picopyn-2.0.0.dev1/docs/explain.md +0 -11
- picopyn-2.0.0.dev1/picopyn/asynchronous/__init__.py +0 -11
- picopyn-2.0.0.dev1/picopyn/asynchronous/client.py +0 -180
- picopyn-2.0.0.dev1/picopyn/synchronous/client.py +0 -168
- picopyn-2.0.0.dev1/picopyn/synchronous/connection.py +0 -205
- picopyn-2.0.0.dev1/picopyn/synchronous/pool.py +0 -652
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/LICENSE +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/MANIFEST.in +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/benchmark/README.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/benchmark/examples/10k_rows_1_thread_1_batch/overall_speed_time.png +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/benchmark/examples/10k_rows_1_thread_1_batch/threads_speed_time.png +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/benchmark/examples/1m_rows_10_threads_100_batch/errors_cumulative.png +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/benchmark/examples/1m_rows_10_threads_100_batch/overall_speed_time.png +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/benchmark/examples/1m_rows_10_threads_100_batch/threads_speed_time.png +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/benchmark/generate_data_to_csv.py +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/async.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/async_internal.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/bucket.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/changelog.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/contributing.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_reconcile_node_outside_pool_died.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_reconcile_pooled_node_died_size_eq.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_reconcile_pooled_node_died_size_gt.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_reconcile_pooled_node_died_size_lt.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_topology_apply_bucket_event.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/diagrams/_topology_apply_bucket_replicaset_event.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/query_metadata.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/docs/topology.md +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/ssl.py +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn/synchronous/errors.py +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn.egg-info/dependency_links.txt +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn.egg-info/requires.txt +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/picopyn.egg-info/top_level.txt +0 -0
- {picopyn-2.0.0.dev1 → picopyn-3.0.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: picopyn
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Python connector for working with the distributed Picodata database.
|
|
5
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
|
|
@@ -55,17 +55,17 @@ Dynamic: license-file
|
|
|
55
55
|
| >=25.2.1, <25.4.4 | 0.1.1 |
|
|
56
56
|
| >=25.4.4, <25.5.1 | 0.2.0 |
|
|
57
57
|
| >=25.5.1, <26.1.x | 1.0.0 |
|
|
58
|
-
| >=26.1.1, <26.2.x | 2.0.0
|
|
58
|
+
| >=26.1.1, <26.2.x | >=2.0.0, <4.0.0 |
|
|
59
59
|
| 26.2.x | ⚠️ Unreleased yet |
|
|
60
60
|
|
|
61
61
|
## Features
|
|
62
62
|
* Connection pooling with configurable pool size
|
|
63
63
|
* Optional automatic node discovery
|
|
64
64
|
* Pluggable load-balancing strategies
|
|
65
|
+
* Shard-aware routing
|
|
66
|
+
* Automatic pool membership reconciliation, including rebalancing a full pool across the nodes
|
|
65
67
|
* Asynchronous API based on [asyncpg](https://github.com/MagicStack/asyncpg)
|
|
66
|
-
|
|
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/)
|
|
68
|
+
* Synchronous [DB-API 2.0-compatible](https://peps.python.org/pep-0249/) API based on [psycopg](https://www.psycopg.org/)
|
|
69
69
|
|
|
70
70
|
## Navigation
|
|
71
71
|
* [Installation](#installation)
|
|
@@ -97,24 +97,24 @@ Async:
|
|
|
97
97
|
|
|
98
98
|
```python
|
|
99
99
|
import asyncio
|
|
100
|
-
from picopyn.asynchronous import
|
|
100
|
+
from picopyn.asynchronous import Pool
|
|
101
101
|
|
|
102
102
|
async def main():
|
|
103
|
-
# create and
|
|
104
|
-
|
|
105
|
-
await
|
|
103
|
+
# create and open pool to the picodata cluster
|
|
104
|
+
pool = Pool(dsn="postgresql://admin:pass@localhost:5432", enable_discovery=True)
|
|
105
|
+
await pool.open()
|
|
106
106
|
|
|
107
107
|
# execute DDL operations
|
|
108
|
-
await
|
|
108
|
+
await pool.execute('''
|
|
109
109
|
CREATE TABLE "warehouse" (id INTEGER NOT NULL, item TEXT NOT NULL, PRIMARY KEY (id)) USING memtx DISTRIBUTED BY (id) OPTION (TIMEOUT = 3.0);
|
|
110
110
|
''')
|
|
111
111
|
|
|
112
112
|
# execute DML/DQL operations
|
|
113
|
-
await
|
|
114
|
-
rows = await
|
|
113
|
+
await pool.execute('INSERT INTO "warehouse" VALUES ($1::int, $2::varchar)', 1, "test")
|
|
114
|
+
rows = await pool.fetch('SELECT * FROM "warehouse"')
|
|
115
115
|
print(rows)
|
|
116
116
|
|
|
117
|
-
await
|
|
117
|
+
await pool.close()
|
|
118
118
|
|
|
119
119
|
asyncio.run(main())
|
|
120
120
|
```
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
| >=25.2.1, <25.4.4 | 0.1.1 |
|
|
13
13
|
| >=25.4.4, <25.5.1 | 0.2.0 |
|
|
14
14
|
| >=25.5.1, <26.1.x | 1.0.0 |
|
|
15
|
-
| >=26.1.1, <26.2.x | 2.0.0
|
|
15
|
+
| >=26.1.1, <26.2.x | >=2.0.0, <4.0.0 |
|
|
16
16
|
| 26.2.x | ⚠️ Unreleased yet |
|
|
17
17
|
|
|
18
18
|
## Features
|
|
19
19
|
* Connection pooling with configurable pool size
|
|
20
20
|
* Optional automatic node discovery
|
|
21
21
|
* Pluggable load-balancing strategies
|
|
22
|
+
* Shard-aware routing
|
|
23
|
+
* Automatic pool membership reconciliation, including rebalancing a full pool across the nodes
|
|
22
24
|
* Asynchronous API based on [asyncpg](https://github.com/MagicStack/asyncpg)
|
|
23
|
-
|
|
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/)
|
|
25
|
+
* Synchronous [DB-API 2.0-compatible](https://peps.python.org/pep-0249/) API based on [psycopg](https://www.psycopg.org/)
|
|
26
26
|
|
|
27
27
|
## Navigation
|
|
28
28
|
* [Installation](#installation)
|
|
@@ -54,24 +54,24 @@ Async:
|
|
|
54
54
|
|
|
55
55
|
```python
|
|
56
56
|
import asyncio
|
|
57
|
-
from picopyn.asynchronous import
|
|
57
|
+
from picopyn.asynchronous import Pool
|
|
58
58
|
|
|
59
59
|
async def main():
|
|
60
|
-
# create and
|
|
61
|
-
|
|
62
|
-
await
|
|
60
|
+
# create and open pool to the picodata cluster
|
|
61
|
+
pool = Pool(dsn="postgresql://admin:pass@localhost:5432", enable_discovery=True)
|
|
62
|
+
await pool.open()
|
|
63
63
|
|
|
64
64
|
# execute DDL operations
|
|
65
|
-
await
|
|
65
|
+
await pool.execute('''
|
|
66
66
|
CREATE TABLE "warehouse" (id INTEGER NOT NULL, item TEXT NOT NULL, PRIMARY KEY (id)) USING memtx DISTRIBUTED BY (id) OPTION (TIMEOUT = 3.0);
|
|
67
67
|
''')
|
|
68
68
|
|
|
69
69
|
# execute DML/DQL operations
|
|
70
|
-
await
|
|
71
|
-
rows = await
|
|
70
|
+
await pool.execute('INSERT INTO "warehouse" VALUES ($1::int, $2::varchar)', 1, "test")
|
|
71
|
+
rows = await pool.fetch('SELECT * FROM "warehouse"')
|
|
72
72
|
print(rows)
|
|
73
73
|
|
|
74
|
-
await
|
|
74
|
+
await pool.close()
|
|
75
75
|
|
|
76
76
|
asyncio.run(main())
|
|
77
77
|
```
|
|
@@ -8,7 +8,7 @@ from uuid import UUID
|
|
|
8
8
|
import matplotlib
|
|
9
9
|
import numpy as np
|
|
10
10
|
|
|
11
|
-
from picopyn.asynchronous import
|
|
11
|
+
from picopyn.asynchronous import Pool
|
|
12
12
|
|
|
13
13
|
matplotlib.use("Agg")
|
|
14
14
|
import os
|
|
@@ -121,17 +121,17 @@ def read_csv(input_file):
|
|
|
121
121
|
exit(1)
|
|
122
122
|
|
|
123
123
|
|
|
124
|
-
async def prepare_database(
|
|
124
|
+
async def prepare_database(pool):
|
|
125
125
|
"""
|
|
126
126
|
Prepare the database: drop and recreate the target table.
|
|
127
127
|
|
|
128
128
|
Args:
|
|
129
|
-
|
|
129
|
+
pool (Pool): Picodata connection pool.
|
|
130
130
|
"""
|
|
131
131
|
try:
|
|
132
|
-
await
|
|
133
|
-
await
|
|
134
|
-
await
|
|
132
|
+
await pool.open()
|
|
133
|
+
await pool.execute("DROP TABLE IF EXISTS test_all_types;")
|
|
134
|
+
await pool.execute(
|
|
135
135
|
"""
|
|
136
136
|
CREATE TABLE test_all_types(
|
|
137
137
|
id INTEGER PRIMARY KEY,
|
|
@@ -176,13 +176,13 @@ def build_batch_insert_sql(batch_size: int) -> str:
|
|
|
176
176
|
)
|
|
177
177
|
|
|
178
178
|
|
|
179
|
-
async def worker(thread_id,
|
|
179
|
+
async def worker(thread_id, pool, batches, durations, errors, start_time, args):
|
|
180
180
|
"""
|
|
181
181
|
Worker coroutine to perform batch inserts for assigned batches.
|
|
182
182
|
|
|
183
183
|
Args:
|
|
184
184
|
thread_id (int): Worker ID.
|
|
185
|
-
|
|
185
|
+
pool (Pool): Picodata connection pool.
|
|
186
186
|
batches (list): List of batch rows to insert.
|
|
187
187
|
durations (list): List to collect durations per batch.
|
|
188
188
|
errors (list): List to collect timestamps of failed inserts.
|
|
@@ -194,7 +194,7 @@ async def worker(thread_id, client, batches, durations, errors, start_time, args
|
|
|
194
194
|
try:
|
|
195
195
|
sql = build_batch_insert_sql(len(batch))
|
|
196
196
|
flat_args = [item for row in batch for item in row]
|
|
197
|
-
await
|
|
197
|
+
await pool.execute(sql, *flat_args)
|
|
198
198
|
except Exception as e:
|
|
199
199
|
if args.log_insert_errors:
|
|
200
200
|
print(f"[ERROR] Failed batch insert: {e}")
|
|
@@ -203,12 +203,12 @@ async def worker(thread_id, client, batches, durations, errors, start_time, args
|
|
|
203
203
|
durations[thread_id].append(end - start)
|
|
204
204
|
|
|
205
205
|
|
|
206
|
-
async def run_benchmark(
|
|
206
|
+
async def run_benchmark(pool, rows, args):
|
|
207
207
|
"""
|
|
208
208
|
Run the entire insert benchmark using multiple workers.
|
|
209
209
|
|
|
210
210
|
Args:
|
|
211
|
-
|
|
211
|
+
pool (Pool): Picodata connection pool.
|
|
212
212
|
rows (list): Parsed rows to insert.
|
|
213
213
|
args (Namespace): Parsed arguments.
|
|
214
214
|
|
|
@@ -229,7 +229,7 @@ async def run_benchmark(client, rows, args):
|
|
|
229
229
|
errors = []
|
|
230
230
|
|
|
231
231
|
tasks = [
|
|
232
|
-
worker(i,
|
|
232
|
+
worker(i, pool, batches_per_thread[i], durations, errors, start_time, args)
|
|
233
233
|
for i in range(args.threads)
|
|
234
234
|
]
|
|
235
235
|
await asyncio.gather(*tasks)
|
|
@@ -473,10 +473,13 @@ async def main():
|
|
|
473
473
|
|
|
474
474
|
rows = read_csv(args.input)
|
|
475
475
|
|
|
476
|
-
|
|
477
|
-
|
|
476
|
+
pool = Pool(dsn=args.cluster, max_size=args.threads, enable_discovery=True)
|
|
477
|
+
try:
|
|
478
|
+
await prepare_database(pool)
|
|
478
479
|
|
|
479
|
-
|
|
480
|
+
durations, errors = await run_benchmark(pool, rows, args)
|
|
481
|
+
finally:
|
|
482
|
+
await pool.close()
|
|
480
483
|
|
|
481
484
|
build_thread_graph(durations, args)
|
|
482
485
|
build_overall_graph(durations, rows, args)
|
|
@@ -16,7 +16,7 @@ ERROR([Initialization failed]):::error
|
|
|
16
16
|
CFG[/"Pool settings<br>· DSN string<br>· pool size<br>· forbidden tiers<br>· cluster discovery mode"/]
|
|
17
17
|
|
|
18
18
|
USAGE_DETAILS_NOTE>"Return to pool lifecycle details"]:::note
|
|
19
|
-
click USAGE_DETAILS_NOTE "#picopyn
|
|
19
|
+
click USAGE_DETAILS_NOTE "#picopyn.$MOD$.Pool" "Return to pool lifecycle details"
|
|
20
20
|
|
|
21
21
|
%% === Data objects ===
|
|
22
22
|
POOL[[Connection pool]]
|
|
@@ -17,7 +17,7 @@ ERROR([Initialization failed]):::error
|
|
|
17
17
|
CFG[/"Pool settings<br>· DSN string<br>· pool size<br>· forbidden tiers<br>· cluster discovery disabled"/]
|
|
18
18
|
|
|
19
19
|
GENERAL_DETAILS_NOTE>"Return to general algorithm"]:::note
|
|
20
|
-
click GENERAL_DETAILS_NOTE "#picopyn
|
|
20
|
+
click GENERAL_DETAILS_NOTE "#picopyn.$MOD$.Pool.open" "Return to general algorithm"
|
|
21
21
|
|
|
22
22
|
%% === Data objects ===
|
|
23
23
|
B_DSN[[DSN node list]]
|
|
@@ -29,7 +29,7 @@ START --> CFG --> PARSE[Parse DSN string to DSN node list] --> B_PoolFilling
|
|
|
29
29
|
PARSE <-. write .-> B_DSN
|
|
30
30
|
B_DSN <-. read .-> B_CONNECT_NODE[Connect to DSN node]
|
|
31
31
|
|
|
32
|
-
B_PoolFilling_NOTE>"iterate over DSN nodes until either:<br>- the list is empty<br>-
|
|
32
|
+
B_PoolFilling_NOTE>"iterate over DSN nodes until either:<br>- the list is empty<br>- max size connections<br>have been established"]:::note
|
|
33
33
|
B_PoolFilling_NOTE -.-> B_PoolFilling
|
|
34
34
|
subgraph B_PoolFilling["Pool filling"]
|
|
35
35
|
direction TB
|
|
@@ -15,10 +15,10 @@ START([Start]):::neutral
|
|
|
15
15
|
DONE([Pool ready]):::success
|
|
16
16
|
D_ERROR_CAND([Initialization failed]):::error
|
|
17
17
|
ERROR([Initialization failed]):::error
|
|
18
|
-
CFG[/"Pool settings<br>· DSN string<br>·
|
|
18
|
+
CFG[/"Pool settings<br>· DSN string<br>· max_size<br>· forbidden tiers<br>· cluster discovery enabled"/]
|
|
19
19
|
|
|
20
20
|
GENERAL_DETAILS_NOTE>"Return to general algorithm"]:::note
|
|
21
|
-
click GENERAL_DETAILS_NOTE "#picopyn
|
|
21
|
+
click GENERAL_DETAILS_NOTE "#picopyn.$MOD$.Pool.open" "Return to general algorithm"
|
|
22
22
|
|
|
23
23
|
%% === Data objects ===
|
|
24
24
|
D_DSN[[DSN node list]]
|
|
@@ -40,7 +40,7 @@ subgraph DiscoverNodes["Discover cluster nodes"]
|
|
|
40
40
|
end
|
|
41
41
|
DiscoverNodes --> D_PoolFilling
|
|
42
42
|
|
|
43
|
-
D_PoolFilling_NOTE>"iterate over candidate nodes<br>until either:<br>- the list is empty<br>-
|
|
43
|
+
D_PoolFilling_NOTE>"iterate over candidate nodes<br>until either:<br>- the list is empty<br>- max_size connections<br>have been established"]:::note
|
|
44
44
|
D_PoolFilling_NOTE -.-> D_PoolFilling
|
|
45
45
|
CAND <-. read .-> D_CONNECT_NODE[Connect to candidate node]
|
|
46
46
|
subgraph D_PoolFilling["Pool filling"]
|
|
@@ -17,15 +17,15 @@ DONE([Pool closed]):::neutral
|
|
|
17
17
|
|
|
18
18
|
CFG[/"Pool settings<br>· DSN<br>· pool size<br>· forbidden tiers<br>· cluster discovery mode"/]
|
|
19
19
|
|
|
20
|
-
START --> CFG --> CREATE["Pool.__init__"] --> CONNECT["pool.
|
|
20
|
+
START --> CFG --> CREATE["Pool.__init__"] --> CONNECT["pool.open()"] --> UsePool
|
|
21
21
|
CONNECT <-. write .-> POOL
|
|
22
22
|
|
|
23
|
-
CONNECT_DETAILS_NOTE>"
|
|
24
|
-
click CONNECT_DETAILS_NOTE "#picopyn
|
|
23
|
+
CONNECT_DETAILS_NOTE>"Open algorithm details"]:::note
|
|
24
|
+
click CONNECT_DETAILS_NOTE "#picopyn.$MOD$.Pool.open" "Open algorithm details"
|
|
25
25
|
CONNECT_DETAILS_NOTE -.-> CONNECT
|
|
26
26
|
|
|
27
|
-
RECONCILE_NOTE>"
|
|
28
|
-
click RECONCILE_NOTE "#picopyn
|
|
27
|
+
RECONCILE_NOTE>"pool membership changes<br>in the background:<br>connections are opened/closed<br>to match topology changes<br>(discovery mode: any cluster node,<br>bootstrap mode: DSN nodes only)"]:::note
|
|
28
|
+
click RECONCILE_NOTE "#picopyn.$MOD$.Pool.topology" "Reconcile algorithm details"
|
|
29
29
|
RECONCILE_NOTE -.-> POOL
|
|
30
30
|
|
|
31
31
|
CYCLE_NOTE>"repeated for each<br>database operation"]:::note
|
|
@@ -33,12 +33,12 @@ CYCLE_NOTE -.-> UsePool
|
|
|
33
33
|
|
|
34
34
|
subgraph UsePool["Pool usage"]
|
|
35
35
|
direction TB
|
|
36
|
-
ACQUIRE["pool.acquire()"] --> USE["Execute queries<br>·
|
|
36
|
+
ACQUIRE["pool.acquire() -> conn"] --> USE["Execute queries<br>· conn.fetchrow()<br>· conn.fetch()<br>· conn.execute()"] --> RELEASE["pool.release(conn)"]
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
ACQUIRE <-. delete .-> POOL
|
|
40
40
|
RELEASE <-. write .-> POOL
|
|
41
|
-
CLOSE <-. delete .-> POOL
|
|
41
|
+
CLOSE["pool.close()"] <-. delete .-> POOL
|
|
42
42
|
UsePool --> CLOSE
|
|
43
43
|
CLOSE --> DONE
|
|
44
44
|
```
|
|
@@ -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.open()"] --> UsePool
|
|
21
|
+
CONNECT <-. write .-> POOL
|
|
22
|
+
|
|
23
|
+
CONNECT_DETAILS_NOTE>"Open algorithm details"]:::note
|
|
24
|
+
click CONNECT_DETAILS_NOTE "#picopyn.$MOD$.Pool.open" "Open algorithm details"
|
|
25
|
+
CONNECT_DETAILS_NOTE -.-> CONNECT
|
|
26
|
+
|
|
27
|
+
RECONCILE_NOTE>"pool membership changes<br>in the background:<br>connections are opened/closed<br>to match topology changes<br>(discovery mode: any cluster node,<br>bootstrap mode: DSN nodes only)"]:::note
|
|
28
|
+
click RECONCILE_NOTE "#picopyn.$MOD$.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["with pool.connection() as conn"] --> CURSOR["conn.cursor() -> cursor"] --> USE["Execute queries<br>· cursor.fetchone()<br>· cursor.execute()<br>· ..."] --> RELEASE["leaving the with block<br>releases the connection"]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
ACQUIRE <-. delete .-> POOL
|
|
40
|
+
RELEASE <-. write .-> POOL
|
|
41
|
+
CLOSE["pool.close()"] <-. delete .-> POOL
|
|
42
|
+
UsePool --> CLOSE
|
|
43
|
+
CLOSE --> DONE
|
|
44
|
+
```
|
|
@@ -10,13 +10,13 @@ classDef note fill:#fffde7,stroke:#f9a825,stroke-dasharray:4,color:#555
|
|
|
10
10
|
|
|
11
11
|
INIT(["Pool init"]):::neutral
|
|
12
12
|
|
|
13
|
-
INIT -->
|
|
13
|
+
INIT --> CONNECT["Fill the pool with ordinary<br>connections to picodata"] --> Pool1
|
|
14
14
|
|
|
15
|
-
NOTE_CONN>"connection options require<br>statement invalidation
|
|
15
|
+
NOTE_CONN>"connection options require<br>statement invalidation<br>signal only"]:::note
|
|
16
16
|
NOTE_CONN -.-> CONNECT
|
|
17
17
|
|
|
18
|
-
CONNECT_DETAILS_NOTE>"
|
|
19
|
-
click CONNECT_DETAILS_NOTE "#picopyn
|
|
18
|
+
CONNECT_DETAILS_NOTE>"Open algorithm details"]:::note
|
|
19
|
+
click CONNECT_DETAILS_NOTE "#picopyn.$MOD$.Pool.open" "Open algorithm details"
|
|
20
20
|
CONNECT_DETAILS_NOTE -.-> CONNECT
|
|
21
21
|
|
|
22
22
|
subgraph Pool1["Pool"]
|
|
@@ -25,13 +25,14 @@ subgraph Pool1["Pool"]
|
|
|
25
25
|
CONN11["conn to inst1"] --> PICO11[(Picodata)]
|
|
26
26
|
CONN12["..."] --> PICO12[(Picodata)]
|
|
27
27
|
CONN1N["conn to instN"] --> PICO1N[(Picodata)]
|
|
28
|
-
|
|
29
|
-
CACHE1[/"Query meta cache"/]
|
|
30
28
|
end
|
|
31
29
|
|
|
32
|
-
Pool1 --> CONNECT_SERV["create service connection<br>for meta listener"]
|
|
30
|
+
Pool1 --> CONNECT_SERV["create empty query meta cache<br>and service connection<br>for meta listener"]
|
|
31
|
+
|
|
32
|
+
NOTE_DISABLED>"skipped entirely when<br>service disabled: no cache,<br>no service connection"]:::note
|
|
33
|
+
NOTE_DISABLED -.-> CONNECT_SERV
|
|
33
34
|
|
|
34
|
-
NOTE_CONN_SERV>"connection options require
|
|
35
|
+
NOTE_CONN_SERV>"connection options require<br>statement invalidation signal<br>and distribution key metadata"]:::note
|
|
35
36
|
NOTE_CONN_SERV -.-> CONNECT_SERV
|
|
36
37
|
|
|
37
38
|
CONNECT_SERV --> LIST["add picodata notice listener"]
|
|
@@ -45,7 +46,7 @@ subgraph Pool["Pool"]
|
|
|
45
46
|
CONN2["..."] --> PICO2[(...)]
|
|
46
47
|
CONNN["connN for<br>user usage"] --> PICON[(PicodataN)]
|
|
47
48
|
|
|
48
|
-
PIC(["can be any of"]) -.- CONNSERV["service connection
|
|
49
|
+
PIC(["can be any of"]) -.- CONNSERV["service connection<br>with Notice listener"]
|
|
49
50
|
PICO1 -.- PIC
|
|
50
51
|
PICO2 -.- PIC
|
|
51
52
|
PICON -.- PIC
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
---
|
|
3
|
+
title: Query metadata -- usage lookup
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
sequenceDiagram
|
|
7
|
+
participant App as Application
|
|
8
|
+
box rgba(128,128,128,0.15) Picopyn
|
|
9
|
+
participant Driver as Core
|
|
10
|
+
participant Pool as Connection pool
|
|
11
|
+
participant Meta as Service connection
|
|
12
|
+
end
|
|
13
|
+
participant Server as Picodata
|
|
14
|
+
|
|
15
|
+
App->>Driver: pool.execute(query, params)
|
|
16
|
+
|
|
17
|
+
alt First call: cache miss
|
|
18
|
+
Driver->>Pool: Any free connection
|
|
19
|
+
else Repeated call: cache hit
|
|
20
|
+
Driver->>Driver: query meta + params -> bucket_id -> node
|
|
21
|
+
Driver->>Pool: Connection to that node
|
|
22
|
+
end
|
|
23
|
+
Pool-->>Driver: Connection
|
|
24
|
+
|
|
25
|
+
Driver->>Server: Bind + Execute
|
|
26
|
+
Server-->>Driver: Result
|
|
27
|
+
Driver-->>App: Result
|
|
28
|
+
|
|
29
|
+
rect rgba(128,128,128,0.15)
|
|
30
|
+
Note over Driver,Server: In the background, only after a cache miss
|
|
31
|
+
Driver->>Meta: query
|
|
32
|
+
Meta->>Server: Parse(query)
|
|
33
|
+
Server-->>Meta: NoticeResponse
|
|
34
|
+
Meta-->>Driver: query meta
|
|
35
|
+
Driver->>Driver: LRU cache
|
|
36
|
+
end
|
|
37
|
+
```
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
---
|
|
3
|
+
title: Rebalance pool after reconcile
|
|
4
|
+
---
|
|
5
|
+
flowchart TD
|
|
6
|
+
|
|
7
|
+
classDef neutral fill:#f5f5f5,stroke:#9e9e9e,color:#424242
|
|
8
|
+
classDef note fill:#fffde7,stroke:#f9a825,stroke-dasharray:4,color:#555
|
|
9
|
+
classDef success fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
|
|
10
|
+
|
|
11
|
+
START(["START"]):::neutral --> COUNT
|
|
12
|
+
|
|
13
|
+
COUNT_DETAILS_NOTE>"connections already marked<br>to evict are not counted:<br>they are closed on<br>release and refilled elsewhere"]:::note
|
|
14
|
+
COUNT_DETAILS_NOTE -.-> COUNT
|
|
15
|
+
|
|
16
|
+
COUNT_DETAILS_NOTE2>"candidate nodes are<br>online Picodata nodes<br>from allowed tiers"]:::note
|
|
17
|
+
COUNT_DETAILS_NOTE2 -.-> COUNT
|
|
18
|
+
|
|
19
|
+
COUNT["count free and in-use pool<br>connections per candidate uuid"] --> GAP
|
|
20
|
+
|
|
21
|
+
GAP_DETAILS_NOTE>"a gap of one<br>is not an imbalance"]:::note
|
|
22
|
+
GAP_DETAILS_NOTE -.-> GAP
|
|
23
|
+
|
|
24
|
+
GAP{"gap between<br>max count and min count<br>(most overloaded and<br>most underloaded nodes)<br>>= 2?"}
|
|
25
|
+
GAP -->|no| POOL_OK(["pool is balanced<br>(exit)"]):::success
|
|
26
|
+
GAP -->|yes| FREE{"is there a free connection<br>to the overloaded uuid?"}
|
|
27
|
+
|
|
28
|
+
FREE -->|no| EVICT_USED["mark one in-use connection<br>to the overloaded uuid to evict"]
|
|
29
|
+
FREE -->|yes| MIGRATE["migrate one connection:<br>take the free one out, connect<br>to the underloaded uuid,<br>close the old one"]
|
|
30
|
+
|
|
31
|
+
EVICT_DETAILS_NOTE>"release closes it, and the next<br>refresh refills the free slot to<br>the underloaded uuid"]:::note
|
|
32
|
+
EVICT_DETAILS_NOTE -.-> EVICT_USED
|
|
33
|
+
|
|
34
|
+
EVICT_USED --> POOL_OK3(["(exit)"]):::success
|
|
35
|
+
|
|
36
|
+
MIGRATE -->|migrated| LIMIT
|
|
37
|
+
MIGRATE -->|"could not connect to<br>the underloaded uuid"| EXCLUDE["put the connection back and<br>exclude the underloaded uuid<br>from the candidate list<br>in this reconcile"]
|
|
38
|
+
EXCLUDE --> LIMIT
|
|
39
|
+
|
|
40
|
+
LIMIT{"migration limit for<br>this reconcile reached?"}
|
|
41
|
+
LIMIT -->|no| GAP
|
|
42
|
+
LIMIT -->|yes| POOL_OK4(["(exit)"]):::success
|
|
43
|
+
|
|
44
|
+
LIMIT_DETAILS_NOTE>"the limit is proportional to<br>the pool size, see<br>Pool.rebalance_pool_divisor"]:::note
|
|
45
|
+
click LIMIT_DETAILS_NOTE "#picopyn.$MOD$.Pool" "Pool.rebalance_pool_divisor"
|
|
46
|
+
LIMIT_DETAILS_NOTE -.-> LIMIT
|
|
47
|
+
|
|
48
|
+
LIMIT_DETAILS_NOTE2>"the rest is left to<br>the next refresh"]:::note
|
|
49
|
+
LIMIT_DETAILS_NOTE2 -.-> POOL_OK4
|
|
50
|
+
```
|
|
@@ -10,7 +10,6 @@ classDef success fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
|
|
|
10
10
|
classDef error fill:#ffebee,stroke:#f44336,color:#b71c1c
|
|
11
11
|
|
|
12
12
|
subgraph Pool["Pool"]
|
|
13
|
-
Settings[/"forbidden tiers=[...]<br>max size=N"/]
|
|
14
13
|
FreeConns["free conns=[...]"]
|
|
15
14
|
UsedConns["used conns=[...]"]
|
|
16
15
|
Topology[/"Topology"/]
|
|
@@ -25,14 +24,16 @@ TOPOLOGY_DETAILS_NOTE>"Topology details"]:::note
|
|
|
25
24
|
click TOPOLOGY_DETAILS_NOTE "../topology/#picopyn.topology.Topology" "Topology details"
|
|
26
25
|
TOPOLOGY_DETAILS_NOTE -.-> TOPOLOGY_APPLY
|
|
27
26
|
|
|
28
|
-
TOPOLOGY_APPLY -->
|
|
27
|
+
TOPOLOGY_APPLY --> COMPLETE{"topology has both<br>instances and replicasets?"}
|
|
28
|
+
COMPLETE -->|no| NOT_TRUSTED(["snapshot is not trustworthy:<br>pool is left as is<br>(exit)"]):::error
|
|
29
|
+
COMPLETE -->|yes| GET_LIST["build candidate_list from Topology:<br>- uuids with allowed tiers<br>- online uuids<br>- (bootstrap mode) only<br>DSN node addresses"]
|
|
29
30
|
GET_LIST --> REMOVE["close all connections whose<br>uuid not in candidate_list"] --> CHECK_SIZE_START
|
|
30
31
|
|
|
31
32
|
REMOVE -.-> FreeConns
|
|
32
33
|
REMOVE -.-> UsedConns
|
|
33
34
|
|
|
34
35
|
CHECK_SIZE_START{"current pool size < max size?"}
|
|
35
|
-
CHECK_SIZE_START -->|no|
|
|
36
|
+
CHECK_SIZE_START -->|no| REBALANCE
|
|
36
37
|
CHECK_SIZE_START -->|yes| MIN["choose uuid from candidate_list<br>with min connections count"]
|
|
37
38
|
|
|
38
39
|
MIN -->|got candidate| ADD_CONN["try to connect to candidate<br>and add connection to pool..."]
|
|
@@ -41,6 +42,13 @@ MIN -->|no candidate| NO_CANDIDATE2(["pool is temporarily not<br>full until next
|
|
|
41
42
|
ADD_CONN -->|connection failed| EXCLUDE["exclude the candidate uuid<br/>from list in this loop"] --> CHECK_SIZE_LOOP
|
|
42
43
|
ADD_CONN-->|connected| CHECK_SIZE_LOOP{"current size < max size?"}
|
|
43
44
|
|
|
44
|
-
CHECK_SIZE_LOOP -->|no|
|
|
45
|
+
CHECK_SIZE_LOOP -->|no| REBALANCE
|
|
45
46
|
CHECK_SIZE_LOOP -->|yes| MIN
|
|
47
|
+
|
|
48
|
+
REBALANCE["rebalance the pool:<br>move connections from overloaded<br>nodes to underloaded ones"]
|
|
49
|
+
REBALANCE --> POOL_OK(["Pool okay<br>(exit)"]):::success
|
|
50
|
+
|
|
51
|
+
REBALANCE_DETAILS_NOTE>"Rebalance details"]:::note
|
|
52
|
+
click REBALANCE_DETAILS_NOTE "#picopyn.$MOD$.Pool.topology--rebalance" "Rebalance details"
|
|
53
|
+
REBALANCE_DETAILS_NOTE -.-> REBALANCE
|
|
46
54
|
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
```mermaid
|
|
2
|
+
---
|
|
3
|
+
title: (Rebalance) a pooled node returned to Online (pool is already full)
|
|
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")]:::offline
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
subgraph Pool_before["Pool: max_size=6"]
|
|
22
|
+
c1["conn x3"] --> p1
|
|
23
|
+
c2["conn x3"] --> p2
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
EVENT1[/"Event<br>Instance 3 back Online"/]:::neutral -.-> REBALANCE{{"reconcile pool"}}
|
|
27
|
+
before -.-> REBALANCE
|
|
28
|
+
NOTE>"Pool is full, so refill<br>does nothing.<br>Initial connection counts:<br>[3/3/0], gap=3<br>=> migrate: [2/3/1], gap=2<br>=> migrate: [2/2/2], gap=0<br>Pool rebalanced in 2 refreshes"]:::note
|
|
29
|
+
NOTE -.-> REBALANCE
|
|
30
|
+
REBALANCE --> after
|
|
31
|
+
|
|
32
|
+
subgraph after["After (2 refreshes)"]
|
|
33
|
+
subgraph Picodata_after["Picodata"]
|
|
34
|
+
p21[("ins 1")]:::online
|
|
35
|
+
p22[("ins 2")]:::online
|
|
36
|
+
p23[("ins 3")]:::online
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
subgraph Pool_after["Pool: max_size=6"]
|
|
40
|
+
c21["conn x2"] --> p21
|
|
41
|
+
c22["conn x2"] --> p22
|
|
42
|
+
c23["conn x2"] --> p23
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
```
|
|
@@ -9,10 +9,8 @@ classDef neutral fill:#f5f5f5,stroke:#9e9e9e,color:#424242
|
|
|
9
9
|
classDef note fill:#fffde7,stroke:#f9a825,stroke-dasharray:4,color:#555
|
|
10
10
|
|
|
11
11
|
QUERY(["parametrized query"]):::neutral
|
|
12
|
-
HASH(["hash of the<br>query text"]):::neutral
|
|
13
12
|
|
|
14
13
|
QUERY --> PARSE_QUERY
|
|
15
|
-
HASH --> PARSE_QUERY
|
|
16
14
|
PARSE_QUERY["ask Picodata to parse query"] -. Parse .-> PICO[("Picodata")] -. Notice message .-> GET
|
|
17
15
|
|
|
18
16
|
GET["receive the notice"] --> PARSE_MSG{"is this query metadata?"}
|
|
@@ -20,10 +20,10 @@ flowchart TB
|
|
|
20
20
|
click TOPOLOGY_DETAILS_NOTE "../topology/#picopyn.topology.Topology" "See topology details"
|
|
21
21
|
|
|
22
22
|
TRACKER_DETAILS_NOTE>"See topology tracker details"]:::note -.-> TRACKER
|
|
23
|
-
click TRACKER_DETAILS_NOTE "#picopyn
|
|
23
|
+
click TRACKER_DETAILS_NOTE "#picopyn.$MOD$.topology_tracker.TopologyTracker" "Topology tracker details"
|
|
24
24
|
|
|
25
25
|
SOURCE_DETAILS_NOTE>"See topology source details"]:::note -.-> SOURCE
|
|
26
|
-
click SOURCE_DETAILS_NOTE "#picopyn
|
|
26
|
+
click SOURCE_DETAILS_NOTE "#picopyn.$MOD$.topology_tracker.PollingTopologySource" "Topology source details"
|
|
27
27
|
|
|
28
28
|
NOTE>"today: events are built by driver<br>polled Picodata's system<br>tables/functions.<br>planned: Picodata itself<br>is the source, pushing<br>events directly"]:::note
|
|
29
29
|
NOTE -.-> SOURCE
|
|
@@ -44,7 +44,7 @@ BUCKETS_DETAILS_NOTE>"Bucket event details"]:::note
|
|
|
44
44
|
click BUCKETS_DETAILS_NOTE "#picopyn.topology.Topology.apply_bucket" "Bucket event details"
|
|
45
45
|
BUCKETS_DETAILS_NOTE -.-> BUCKET_APPLY
|
|
46
46
|
|
|
47
|
-
NOTE>"_pico_bucket(tier) may not exist<br>on older Picodata -- failure
|
|
47
|
+
NOTE>"_pico_bucket(tier) may not exist<br>on older Picodata -- failure<br>is logged and skipped per tier,<br>not fatal"]:::note
|
|
48
48
|
NOTE -.-> BUCKET_FETCH
|
|
49
49
|
BUCKET_APPLY <-. write .-> TOPOLOGY4[/Topology/]
|
|
50
50
|
|
|
@@ -12,8 +12,8 @@ click ADR_DETAILS_NOTE "https://git.picodata.io/core/picodata/-/blob/master/doc/
|
|
|
12
12
|
|
|
13
13
|
TOPOLOGY[[Topology<br>·instances<br>·replicasets<br>·buckets<br>·tiers]]
|
|
14
14
|
|
|
15
|
-
CONNECT["Pool.
|
|
16
|
-
INITAL_DETAILS_NOTE>"Picodata doesn't send it yet,<br>but it's planned: Picodata will
|
|
15
|
+
CONNECT["Pool.open()"] --> CFG[/"Topology tracker settings<br>· update interval"/] --> START["start topology tracker"] --> INITIAL["initial refresh<br>(sync, imitate ReadyForQuery)"] --> WAIT
|
|
16
|
+
INITAL_DETAILS_NOTE>"Picodata doesn't send it yet,<br>but it's planned: Picodata will<br>send ReadyForQuery once the<br>initial snapshot is fully<br>delivered -- meaning our<br>topology view is consistent"]:::note
|
|
17
17
|
|
|
18
18
|
INITAL_DETAILS_NOTE -.-> INITIAL
|
|
19
19
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Explain
|
|
2
|
+
|
|
3
|
+
::: picopyn.explain
|
|
4
|
+
options:
|
|
5
|
+
show_root_heading: false
|
|
6
|
+
show_root_toc_entry: false
|
|
7
|
+
heading_level: 1
|
|
8
|
+
members: []
|
|
9
|
+
|
|
10
|
+
## API Reference
|
|
11
|
+
|
|
12
|
+
::: picopyn.explain
|
|
13
|
+
options:
|
|
14
|
+
show_root_heading: false
|
|
15
|
+
show_root_toc_entry: false
|
|
16
|
+
heading_level: 3
|
|
17
|
+
members_order: source
|
|
18
|
+
extra:
|
|
19
|
+
hide_docstring: true
|
|
20
|
+
filters:
|
|
21
|
+
- "!^_"
|