picopyn 1.0.0__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.
Files changed (78) hide show
  1. {picopyn-1.0.0/picopyn.egg-info → picopyn-2.0.0.dev1}/PKG-INFO +30 -25
  2. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/README.md +22 -18
  3. picopyn-2.0.0.dev1/docs/async.md +6 -0
  4. picopyn-2.0.0.dev1/docs/async_internal.md +18 -0
  5. picopyn-2.0.0.dev1/docs/bucket.md +7 -0
  6. picopyn-2.0.0.dev1/docs/changelog.md +3 -0
  7. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/docs/contributing.md +2 -2
  8. picopyn-2.0.0.dev1/docs/diagrams/_pool_init.md +64 -0
  9. picopyn-2.0.0.dev1/docs/diagrams/_pool_init_discovery_disabled.md +46 -0
  10. picopyn-2.0.0.dev1/docs/diagrams/_pool_init_discovery_enabled.md +55 -0
  11. picopyn-2.0.0.dev1/docs/diagrams/_pool_lifecycle.md +44 -0
  12. picopyn-2.0.0.dev1/docs/diagrams/_query_metadata_pool_init.md +55 -0
  13. picopyn-2.0.0.dev1/docs/diagrams/_query_metadata_usage_lookup.md +44 -0
  14. picopyn-2.0.0.dev1/docs/diagrams/_reconcile_node_outside_pool_died.md +50 -0
  15. picopyn-2.0.0.dev1/docs/diagrams/_reconcile_pool_with_topology.md +46 -0
  16. picopyn-2.0.0.dev1/docs/diagrams/_reconcile_pooled_node_died_size_eq.md +50 -0
  17. picopyn-2.0.0.dev1/docs/diagrams/_reconcile_pooled_node_died_size_gt.md +54 -0
  18. picopyn-2.0.0.dev1/docs/diagrams/_reconcile_pooled_node_died_size_lt.md +48 -0
  19. picopyn-2.0.0.dev1/docs/diagrams/_request_if_missing.md +27 -0
  20. picopyn-2.0.0.dev1/docs/diagrams/_topology_apply_bucket_event.md +20 -0
  21. picopyn-2.0.0.dev1/docs/diagrams/_topology_apply_bucket_replicaset_event.md +52 -0
  22. picopyn-2.0.0.dev1/docs/diagrams/_topology_components.md +30 -0
  23. picopyn-2.0.0.dev1/docs/diagrams/_topology_refresh.md +52 -0
  24. picopyn-2.0.0.dev1/docs/diagrams/_topology_tracking.md +36 -0
  25. picopyn-2.0.0.dev1/docs/index.md +73 -0
  26. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/docs/installation.md +1 -1
  27. picopyn-2.0.0.dev1/docs/query_metadata.md +8 -0
  28. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/asynchronous/client.py +8 -0
  29. picopyn-2.0.0.dev1/picopyn/asynchronous/connection.py +358 -0
  30. picopyn-2.0.0.dev1/picopyn/asynchronous/metadata_service.py +177 -0
  31. picopyn-2.0.0.dev1/picopyn/asynchronous/pool.py +1262 -0
  32. picopyn-2.0.0.dev1/picopyn/asynchronous/topology_tracker.py +408 -0
  33. picopyn-2.0.0.dev1/picopyn/bucket.py +256 -0
  34. picopyn-2.0.0.dev1/picopyn/query_metadata.py +227 -0
  35. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/synchronous/__init__.py +2 -0
  36. picopyn-2.0.0.dev1/picopyn/synchronous/client.py +168 -0
  37. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/synchronous/connection.py +79 -48
  38. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/synchronous/pool.py +199 -21
  39. picopyn-2.0.0.dev1/picopyn/topology.py +422 -0
  40. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/utils/__init__.py +17 -3
  41. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/utils/discovery.py +45 -1
  42. picopyn-2.0.0.dev1/picopyn/utils/queries.py +72 -0
  43. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/utils/topology.py +67 -3
  44. {picopyn-1.0.0 → picopyn-2.0.0.dev1/picopyn.egg-info}/PKG-INFO +30 -25
  45. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn.egg-info/SOURCES.txt +25 -0
  46. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn.egg-info/requires.txt +4 -3
  47. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/pyproject.toml +10 -9
  48. picopyn-1.0.0/docs/async.md +0 -9
  49. picopyn-1.0.0/docs/index.md +0 -70
  50. picopyn-1.0.0/picopyn/asynchronous/connection.py +0 -149
  51. picopyn-1.0.0/picopyn/asynchronous/pool.py +0 -817
  52. picopyn-1.0.0/picopyn/asynchronous/topology_tracker.py +0 -178
  53. picopyn-1.0.0/picopyn/topology.py +0 -220
  54. picopyn-1.0.0/picopyn/utils/queries.py +0 -40
  55. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/AUTHORS.md +0 -0
  56. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/LICENSE +0 -0
  57. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/MANIFEST.in +0 -0
  58. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/benchmark/README.md +0 -0
  59. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/benchmark/bench.py +0 -0
  60. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/benchmark/examples/10k_rows_1_thread_1_batch/overall_speed_time.png +0 -0
  61. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/benchmark/examples/10k_rows_1_thread_1_batch/threads_speed_time.png +0 -0
  62. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/benchmark/examples/1m_rows_10_threads_100_batch/errors_cumulative.png +0 -0
  63. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/benchmark/examples/1m_rows_10_threads_100_batch/overall_speed_time.png +0 -0
  64. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/benchmark/examples/1m_rows_10_threads_100_batch/threads_speed_time.png +0 -0
  65. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/benchmark/generate_data_to_csv.py +0 -0
  66. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/docs/explain.md +0 -0
  67. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/docs/sync.md +0 -0
  68. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/docs/topology.md +0 -0
  69. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/__init__.py +0 -0
  70. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/asynchronous/__init__.py +0 -0
  71. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/explain.py +0 -0
  72. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/ssl.py +0 -0
  73. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/synchronous/cursor.py +0 -0
  74. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/synchronous/errors.py +0 -0
  75. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn/utils/dsn.py +0 -0
  76. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn.egg-info/dependency_links.txt +0 -0
  77. {picopyn-1.0.0 → picopyn-2.0.0.dev1}/picopyn.egg-info/top_level.txt +0 -0
  78. {picopyn-1.0.0 → 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: 1.0.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 Кoltsov <dkoltsov@picodata.io>, Daniil Gorlyakov <d.gorlyakov@picodata.io>
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/solution/picopyn
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 :: 3 - Alpha
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==4.12.2
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.0; extra == "docs"
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
@@ -49,16 +50,22 @@ Dynamic: license-file
49
50
  **Picopyn** is a Python package for working with the distributed [Picodata](https://picodata.io/en/) database.
50
51
 
51
52
  **Version compatibility**:
52
- | Picodata version | Status |
53
- | ---------------- | ------ |
54
- | >=25.4.x, < 26.2.x | ✅ Fully supported |
55
- | 26.2.x | ⚠️ Not yet tested |
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 |
56
60
 
57
61
  ## Features
58
62
  * Connection pooling with configurable pool size
59
63
  * Optional automatic node discovery
60
64
  * Pluggable load-balancing strategies
61
- * Provides both asynchronous API (based on [asyncpg](https://github.com/MagicStack/asyncpg)) and synchronous [DPAPI-compatible](https://peps.python.org/pep-0249/) API (based on [psycopg](https://www.psycopg.org/))
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/)
62
69
 
63
70
  ## Navigation
64
71
  * [Installation](#installation)
@@ -79,7 +86,7 @@ pip install picopyn
79
86
  Or from source:
80
87
 
81
88
  ```sh
82
- git clone https://git.picodata.io/solution/picopyn.git
89
+ git clone https://git.picodata.io/core/drivers/picopyn.git
83
90
  cd picopyn
84
91
  pip install -e .
85
92
  ```
@@ -118,20 +125,18 @@ Sync:
118
125
  from picopyn.synchronous import connect
119
126
 
120
127
  # create and connect to the picodata cluster
121
- conn = connect("postgresql://admin:pass@localhost:5432")
122
- cur = conn.cursor()
128
+ with connect("postgresql://admin:pass@localhost:5432") as conn:
129
+ cur = conn.cursor()
123
130
 
124
- # execute DDL operations
125
- cur.execute('''
126
- CREATE TABLE "warehouse" (id INTEGER NOT NULL, item TEXT NOT NULL, PRIMARY KEY (id)) USING memtx DISTRIBUTED BY (id) OPTION (TIMEOUT = 3.0);
127
- ''')
128
-
129
- # execute DML/DQL operations
130
- cur.execute('INSERT INTO "warehouse" VALUES (%s, %s)', (1, "test"))
131
- cur.execute('SELECT * FROM "warehouse"')
132
- print(cur.fetchall())
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
+ ''')
133
135
 
134
- conn.close()
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())
135
140
  ```
136
141
 
137
142
  ## Development
@@ -7,16 +7,22 @@
7
7
  **Picopyn** is a Python package for working with the distributed [Picodata](https://picodata.io/en/) database.
8
8
 
9
9
  **Version compatibility**:
10
- | Picodata version | Status |
11
- | ---------------- | ------ |
12
- | >=25.4.x, < 26.2.x | ✅ Fully supported |
13
- | 26.2.x | ⚠️ Not yet tested |
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 |
14
17
 
15
18
  ## Features
16
19
  * Connection pooling with configurable pool size
17
20
  * Optional automatic node discovery
18
21
  * Pluggable load-balancing strategies
19
- * Provides both asynchronous API (based on [asyncpg](https://github.com/MagicStack/asyncpg)) and synchronous [DPAPI-compatible](https://peps.python.org/pep-0249/) API (based on [psycopg](https://www.psycopg.org/))
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/)
20
26
 
21
27
  ## Navigation
22
28
  * [Installation](#installation)
@@ -37,7 +43,7 @@ pip install picopyn
37
43
  Or from source:
38
44
 
39
45
  ```sh
40
- git clone https://git.picodata.io/solution/picopyn.git
46
+ git clone https://git.picodata.io/core/drivers/picopyn.git
41
47
  cd picopyn
42
48
  pip install -e .
43
49
  ```
@@ -76,20 +82,18 @@ Sync:
76
82
  from picopyn.synchronous import connect
77
83
 
78
84
  # create and connect to the picodata cluster
79
- conn = connect("postgresql://admin:pass@localhost:5432")
80
- cur = conn.cursor()
85
+ with connect("postgresql://admin:pass@localhost:5432") as conn:
86
+ cur = conn.cursor()
81
87
 
82
- # execute DDL operations
83
- cur.execute('''
84
- CREATE TABLE "warehouse" (id INTEGER NOT NULL, item TEXT NOT NULL, PRIMARY KEY (id)) USING memtx DISTRIBUTED BY (id) OPTION (TIMEOUT = 3.0);
85
- ''')
86
-
87
- # execute DML/DQL operations
88
- cur.execute('INSERT INTO "warehouse" VALUES (%s, %s)', (1, "test"))
89
- cur.execute('SELECT * FROM "warehouse"')
90
- print(cur.fetchall())
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
+ ''')
91
92
 
92
- conn.close()
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())
93
97
  ```
94
98
 
95
99
  ## Development
@@ -0,0 +1,6 @@
1
+ # Async
2
+
3
+ ::: picopyn.asynchronous
4
+ options:
5
+ filters:
6
+ - "!^_"
@@ -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
+ # Bucket ID
2
+
3
+ ::: picopyn.bucket
4
+ options:
5
+ filters:
6
+ - "!^_"
7
+ - "^__init__$"
@@ -0,0 +1,3 @@
1
+ # Changelog
2
+
3
+ --8<-- "CHANGELOG.md"
@@ -2,6 +2,6 @@
2
2
 
3
3
  Contributions are welcome! The project is hosted on GitLab:
4
4
 
5
- **[git.picodata.io/solution/picopyn](https://git.picodata.io/solution/picopyn)**
5
+ **[git.picodata.io/core/drivers/picopyn](https://git.picodata.io/core/drivers/picopyn)**
6
6
 
7
- To get started with the development environment, running tests, and code style requirements, see the [Development](https://git.picodata.io/solution/picopyn#development) section in the project README.
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
+ ```
@@ -0,0 +1,46 @@
1
+ ```mermaid
2
+ ---
3
+ title: Reconcile pool according to topology
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
+ classDef error fill:#ffebee,stroke:#f44336,color:#b71c1c
11
+
12
+ subgraph Pool["Pool"]
13
+ Settings[/"forbidden tiers=[...]<br>max size=N"/]
14
+ FreeConns["free conns=[...]"]
15
+ UsedConns["used conns=[...]"]
16
+ Topology[/"Topology"/]
17
+ end
18
+
19
+ TOPOLOGY_APPLY["Apply change to topology"] -.-> Topology
20
+
21
+ EVENT_REMOVE[/"Event<br>delete instance A_uuid"/]:::neutral -.-> TOPOLOGY_APPLY
22
+ EVENT_REPLACE[/"Event<br>replace instance B_uuid"/]:::neutral -.-> TOPOLOGY_APPLY
23
+
24
+ TOPOLOGY_DETAILS_NOTE>"Topology details"]:::note
25
+ click TOPOLOGY_DETAILS_NOTE "../topology/#picopyn.topology.Topology" "Topology details"
26
+ TOPOLOGY_DETAILS_NOTE -.-> TOPOLOGY_APPLY
27
+
28
+ TOPOLOGY_APPLY --> GET_LIST["build candidate_list from Topology:<br>- uuids with allowed tiers<br>- online uuids"]
29
+ GET_LIST --> REMOVE["close all connections whose<br>uuid not in candidate_list"] --> CHECK_SIZE_START
30
+
31
+ REMOVE -.-> FreeConns
32
+ REMOVE -.-> UsedConns
33
+
34
+ CHECK_SIZE_START{"current pool size < max size?"}
35
+ CHECK_SIZE_START -->|no| POOL_OK(["Pool okay<br>(exit)"]):::success
36
+ CHECK_SIZE_START -->|yes| MIN["choose uuid from candidate_list<br>with min connections count"]
37
+
38
+ MIN -->|got candidate| ADD_CONN["try to connect to candidate<br>and add connection to pool..."]
39
+ MIN -->|no candidate| NO_CANDIDATE2(["pool is temporarily not<br>full until next event<br>(exit)"]):::error
40
+
41
+ ADD_CONN -->|connection failed| EXCLUDE["exclude the candidate uuid<br/>from list in this loop"] --> CHECK_SIZE_LOOP
42
+ ADD_CONN-->|connected| CHECK_SIZE_LOOP{"current size < max size?"}
43
+
44
+ CHECK_SIZE_LOOP -->|no| POOL_OK2(["Pool okay<br>(exit)"]):::success
45
+ CHECK_SIZE_LOOP -->|yes| MIN
46
+ ```