meerschaum 2.9.5__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerschaum/__init__.py +5 -2
- meerschaum/_internal/__init__.py +1 -0
- meerschaum/_internal/arguments/_parse_arguments.py +4 -4
- meerschaum/_internal/arguments/_parser.py +33 -4
- meerschaum/_internal/cli/__init__.py +6 -0
- meerschaum/_internal/cli/daemons.py +103 -0
- meerschaum/_internal/cli/entry.py +220 -0
- meerschaum/_internal/cli/workers.py +435 -0
- meerschaum/_internal/docs/index.py +48 -2
- meerschaum/_internal/entry.py +50 -14
- meerschaum/_internal/shell/Shell.py +121 -29
- meerschaum/_internal/shell/__init__.py +4 -1
- meerschaum/_internal/static.py +359 -0
- meerschaum/_internal/term/TermPageHandler.py +1 -2
- meerschaum/_internal/term/__init__.py +40 -6
- meerschaum/_internal/term/tools.py +33 -8
- meerschaum/actions/__init__.py +6 -4
- meerschaum/actions/api.py +53 -13
- meerschaum/actions/attach.py +1 -0
- meerschaum/actions/bootstrap.py +8 -8
- meerschaum/actions/delete.py +4 -2
- meerschaum/actions/edit.py +171 -25
- meerschaum/actions/login.py +8 -8
- meerschaum/actions/register.py +143 -6
- meerschaum/actions/reload.py +22 -5
- meerschaum/actions/restart.py +14 -0
- meerschaum/actions/show.py +184 -31
- meerschaum/actions/start.py +166 -17
- meerschaum/actions/stop.py +38 -2
- meerschaum/actions/sync.py +7 -2
- meerschaum/actions/tag.py +9 -8
- meerschaum/actions/verify.py +5 -8
- meerschaum/api/__init__.py +45 -15
- meerschaum/api/_events.py +46 -4
- meerschaum/api/_oauth2.py +162 -9
- meerschaum/api/_tokens.py +102 -0
- meerschaum/api/dash/__init__.py +0 -3
- meerschaum/api/dash/callbacks/__init__.py +1 -0
- meerschaum/api/dash/callbacks/custom.py +4 -3
- meerschaum/api/dash/callbacks/dashboard.py +198 -118
- meerschaum/api/dash/callbacks/jobs.py +14 -7
- meerschaum/api/dash/callbacks/login.py +10 -1
- meerschaum/api/dash/callbacks/pipes.py +194 -14
- meerschaum/api/dash/callbacks/plugins.py +0 -1
- meerschaum/api/dash/callbacks/register.py +10 -3
- meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
- meerschaum/api/dash/callbacks/tokens.py +389 -0
- meerschaum/api/dash/components.py +36 -15
- meerschaum/api/dash/jobs.py +1 -1
- meerschaum/api/dash/keys.py +35 -93
- meerschaum/api/dash/pages/__init__.py +2 -1
- meerschaum/api/dash/pages/dashboard.py +1 -20
- meerschaum/api/dash/pages/{job.py → jobs.py} +10 -7
- meerschaum/api/dash/pages/login.py +2 -2
- meerschaum/api/dash/pages/pipes.py +16 -5
- meerschaum/api/dash/pages/settings/password_reset.py +1 -1
- meerschaum/api/dash/pages/tokens.py +53 -0
- meerschaum/api/dash/pipes.py +382 -95
- meerschaum/api/dash/sessions.py +12 -0
- meerschaum/api/dash/tokens.py +603 -0
- meerschaum/api/dash/websockets.py +1 -1
- meerschaum/api/dash/webterm.py +18 -6
- meerschaum/api/models/__init__.py +23 -3
- meerschaum/api/models/_actions.py +22 -0
- meerschaum/api/models/_pipes.py +91 -7
- meerschaum/api/models/_tokens.py +81 -0
- meerschaum/api/resources/static/js/terminado.js +3 -0
- meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
- meerschaum/api/resources/templates/termpage.html +13 -0
- meerschaum/api/routes/__init__.py +1 -0
- meerschaum/api/routes/_actions.py +3 -4
- meerschaum/api/routes/_connectors.py +3 -7
- meerschaum/api/routes/_jobs.py +26 -35
- meerschaum/api/routes/_login.py +120 -15
- meerschaum/api/routes/_misc.py +5 -10
- meerschaum/api/routes/_pipes.py +178 -143
- meerschaum/api/routes/_plugins.py +38 -28
- meerschaum/api/routes/_tokens.py +236 -0
- meerschaum/api/routes/_users.py +47 -35
- meerschaum/api/routes/_version.py +3 -3
- meerschaum/api/routes/_webterm.py +3 -3
- meerschaum/config/__init__.py +100 -30
- meerschaum/config/_default.py +132 -64
- meerschaum/config/_edit.py +38 -32
- meerschaum/config/_formatting.py +2 -0
- meerschaum/config/_patch.py +10 -8
- meerschaum/config/_paths.py +133 -13
- meerschaum/config/_read_config.py +87 -36
- meerschaum/config/_sync.py +6 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/environment.py +262 -0
- meerschaum/config/stack/__init__.py +37 -15
- meerschaum/config/static.py +18 -0
- meerschaum/connectors/_Connector.py +11 -6
- meerschaum/connectors/__init__.py +41 -22
- meerschaum/connectors/api/_APIConnector.py +34 -6
- meerschaum/connectors/api/_actions.py +2 -2
- meerschaum/connectors/api/_jobs.py +12 -1
- meerschaum/connectors/api/_login.py +33 -7
- meerschaum/connectors/api/_misc.py +2 -2
- meerschaum/connectors/api/_pipes.py +23 -32
- meerschaum/connectors/api/_plugins.py +2 -2
- meerschaum/connectors/api/_request.py +1 -1
- meerschaum/connectors/api/_tokens.py +146 -0
- meerschaum/connectors/api/_users.py +70 -58
- meerschaum/connectors/instance/_InstanceConnector.py +83 -0
- meerschaum/connectors/instance/__init__.py +10 -0
- meerschaum/connectors/instance/_pipes.py +442 -0
- meerschaum/connectors/instance/_plugins.py +159 -0
- meerschaum/connectors/instance/_tokens.py +317 -0
- meerschaum/connectors/instance/_users.py +188 -0
- meerschaum/connectors/parse.py +5 -2
- meerschaum/connectors/sql/_SQLConnector.py +22 -5
- meerschaum/connectors/sql/_cli.py +12 -11
- meerschaum/connectors/sql/_create_engine.py +12 -168
- meerschaum/connectors/sql/_fetch.py +2 -18
- meerschaum/connectors/sql/_pipes.py +295 -278
- meerschaum/connectors/sql/_plugins.py +29 -0
- meerschaum/connectors/sql/_sql.py +46 -21
- meerschaum/connectors/sql/_users.py +36 -2
- meerschaum/connectors/sql/tables/__init__.py +254 -122
- meerschaum/connectors/valkey/_ValkeyConnector.py +5 -7
- meerschaum/connectors/valkey/_pipes.py +60 -31
- meerschaum/connectors/valkey/_plugins.py +2 -26
- meerschaum/core/Pipe/__init__.py +115 -85
- meerschaum/core/Pipe/_attributes.py +425 -124
- meerschaum/core/Pipe/_bootstrap.py +54 -24
- meerschaum/core/Pipe/_cache.py +555 -0
- meerschaum/core/Pipe/_clear.py +0 -11
- meerschaum/core/Pipe/_data.py +96 -68
- meerschaum/core/Pipe/_deduplicate.py +0 -13
- meerschaum/core/Pipe/_delete.py +12 -21
- meerschaum/core/Pipe/_drop.py +11 -23
- meerschaum/core/Pipe/_dtypes.py +49 -19
- meerschaum/core/Pipe/_edit.py +14 -4
- meerschaum/core/Pipe/_fetch.py +1 -1
- meerschaum/core/Pipe/_index.py +8 -14
- meerschaum/core/Pipe/_show.py +5 -5
- meerschaum/core/Pipe/_sync.py +123 -204
- meerschaum/core/Pipe/_verify.py +4 -4
- meerschaum/{plugins → core/Plugin}/_Plugin.py +16 -12
- meerschaum/core/Plugin/__init__.py +1 -1
- meerschaum/core/Token/_Token.py +220 -0
- meerschaum/core/Token/__init__.py +12 -0
- meerschaum/core/User/_User.py +35 -10
- meerschaum/core/User/__init__.py +9 -1
- meerschaum/core/__init__.py +1 -0
- meerschaum/jobs/_Executor.py +88 -4
- meerschaum/jobs/_Job.py +149 -38
- meerschaum/jobs/__init__.py +3 -2
- meerschaum/jobs/systemd.py +8 -3
- meerschaum/models/__init__.py +35 -0
- meerschaum/models/pipes.py +247 -0
- meerschaum/models/tokens.py +38 -0
- meerschaum/models/users.py +26 -0
- meerschaum/plugins/__init__.py +301 -88
- meerschaum/plugins/bootstrap.py +510 -4
- meerschaum/utils/_get_pipes.py +97 -30
- meerschaum/utils/daemon/Daemon.py +199 -43
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
- meerschaum/utils/daemon/RotatingFile.py +63 -36
- meerschaum/utils/daemon/StdinFile.py +53 -13
- meerschaum/utils/daemon/__init__.py +47 -6
- meerschaum/utils/daemon/_names.py +6 -3
- meerschaum/utils/dataframe.py +479 -81
- meerschaum/utils/debug.py +49 -19
- meerschaum/utils/dtypes/__init__.py +476 -34
- meerschaum/utils/dtypes/sql.py +369 -29
- meerschaum/utils/formatting/__init__.py +5 -2
- meerschaum/utils/formatting/_jobs.py +1 -1
- meerschaum/utils/formatting/_pipes.py +52 -50
- meerschaum/utils/formatting/_pprint.py +1 -0
- meerschaum/utils/formatting/_shell.py +44 -18
- meerschaum/utils/misc.py +268 -186
- meerschaum/utils/packages/__init__.py +25 -40
- meerschaum/utils/packages/_packages.py +42 -34
- meerschaum/utils/pipes.py +213 -0
- meerschaum/utils/process.py +2 -2
- meerschaum/utils/prompt.py +175 -144
- meerschaum/utils/schedule.py +2 -1
- meerschaum/utils/sql.py +134 -47
- meerschaum/utils/threading.py +42 -0
- meerschaum/utils/typing.py +1 -4
- meerschaum/utils/venv/_Venv.py +2 -2
- meerschaum/utils/venv/__init__.py +7 -7
- meerschaum/utils/warnings.py +19 -13
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/METADATA +94 -96
- meerschaum-3.0.0.dist-info/RECORD +289 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/WHEEL +1 -1
- meerschaum-3.0.0.dist-info/licenses/NOTICE +2 -0
- meerschaum/api/models/_interfaces.py +0 -15
- meerschaum/api/models/_locations.py +0 -15
- meerschaum/api/models/_metrics.py +0 -15
- meerschaum/config/_environment.py +0 -145
- meerschaum/config/static/__init__.py +0 -186
- meerschaum-2.9.5.dist-info/RECORD +0 -263
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/zip-safe +0 -0
@@ -7,19 +7,25 @@ Define SQLAlchemy tables
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
+
|
11
|
+
import pickle
|
12
|
+
import threading
|
13
|
+
import meerschaum as mrsm
|
10
14
|
from meerschaum.utils.typing import Optional, Dict, Union, InstanceConnector, List
|
11
|
-
from meerschaum.utils.warnings import error, warn
|
15
|
+
from meerschaum.utils.warnings import error, warn, dprint
|
12
16
|
|
13
17
|
### store a tables dict for each connector
|
14
18
|
connector_tables = {}
|
19
|
+
_tables_locks = {}
|
15
20
|
|
16
21
|
_sequence_flavors = {'duckdb', 'oracle'}
|
17
22
|
_skip_index_names_flavors = {'mssql',}
|
18
23
|
|
19
24
|
def get_tables(
|
20
25
|
mrsm_instance: Optional[Union[str, InstanceConnector]] = None,
|
21
|
-
create: bool =
|
22
|
-
|
26
|
+
create: Optional[bool] = None,
|
27
|
+
refresh: bool = False,
|
28
|
+
debug: bool = False,
|
23
29
|
) -> Union[Dict[str, 'sqlalchemy.Table'], bool]:
|
24
30
|
"""
|
25
31
|
Create tables on the database and return the `sqlalchemy` tables.
|
@@ -29,10 +35,13 @@ def get_tables(
|
|
29
35
|
mrsm_instance: Optional[Union[str, InstanceConnector]], default None
|
30
36
|
The connector on which the tables reside.
|
31
37
|
|
32
|
-
create: bool, default
|
38
|
+
create: Optional[bool], default None
|
33
39
|
If `True`, create the tables if they don't exist.
|
34
40
|
|
35
|
-
|
41
|
+
refresh: bool, default False
|
42
|
+
If `True`, invalidate and rebuild any cache.
|
43
|
+
|
44
|
+
debug: bool, default False
|
36
45
|
Verbosity Toggle.
|
37
46
|
|
38
47
|
Returns
|
@@ -42,7 +51,6 @@ def get_tables(
|
|
42
51
|
|
43
52
|
"""
|
44
53
|
from meerschaum.utils.debug import dprint
|
45
|
-
from meerschaum.utils.formatting import pprint
|
46
54
|
from meerschaum.connectors.parse import parse_instance_keys
|
47
55
|
from meerschaum.utils.packages import attempt_import
|
48
56
|
from meerschaum.utils.sql import json_flavors
|
@@ -54,7 +62,7 @@ def get_tables(
|
|
54
62
|
lazy=False,
|
55
63
|
)
|
56
64
|
if not sqlalchemy:
|
57
|
-
error(
|
65
|
+
error("Failed to import sqlalchemy. Is sqlalchemy installed?")
|
58
66
|
|
59
67
|
if mrsm_instance is None:
|
60
68
|
conn = get_connector(debug=debug)
|
@@ -63,149 +71,209 @@ def get_tables(
|
|
63
71
|
else: ### NOTE: mrsm_instance MUST BE a SQL Connector for this to work!
|
64
72
|
conn = mrsm_instance
|
65
73
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
74
|
+
cache_expired = refresh or (
|
75
|
+
(
|
76
|
+
_check_create_cache(conn, debug=debug)
|
77
|
+
if conn.flavor != 'sqlite'
|
78
|
+
else True
|
79
|
+
)
|
80
|
+
if conn.type == 'sql'
|
81
|
+
else False
|
82
|
+
)
|
83
|
+
create = create or cache_expired
|
72
84
|
|
73
85
|
### Skip if the connector is not a SQL connector.
|
74
86
|
if getattr(conn, 'type', None) != 'sql':
|
75
87
|
return {}
|
76
88
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
89
|
+
conn_key = str(conn)
|
90
|
+
|
91
|
+
if refresh:
|
92
|
+
_ = connector_tables.pop(conn_key, None)
|
93
|
+
|
94
|
+
if conn_key in connector_tables:
|
95
|
+
return connector_tables[conn_key]
|
96
|
+
|
97
|
+
fasteners = attempt_import('fasteners')
|
98
|
+
pickle_path = conn.get_metadata_cache_path(kind='pkl')
|
99
|
+
lock_path = pickle_path.with_suffix('.lock')
|
100
|
+
lock = fasteners.InterProcessLock(lock_path)
|
101
|
+
|
102
|
+
with lock:
|
103
|
+
if not cache_expired and pickle_path.exists():
|
104
|
+
try:
|
105
|
+
with open(pickle_path, 'rb') as f:
|
106
|
+
metadata = pickle.load(f)
|
107
|
+
metadata.bind = conn.engine
|
108
|
+
tables = {tbl.name.replace('mrsm_', ''): tbl for tbl in metadata.tables.values()}
|
109
|
+
connector_tables[conn_key] = tables
|
110
|
+
return tables
|
111
|
+
except Exception as e:
|
112
|
+
warn(f"Failed to load metadata from cache, rebuilding: {e}")
|
113
|
+
|
114
|
+
if conn_key not in _tables_locks:
|
115
|
+
_tables_locks[conn_key] = threading.Lock()
|
116
|
+
|
117
|
+
with _tables_locks[conn_key]:
|
118
|
+
if conn_key not in connector_tables:
|
119
|
+
if debug:
|
120
|
+
dprint(f"Building in-memory instance tables for '{conn}'.")
|
121
|
+
|
122
|
+
id_type = sqlalchemy.Integer
|
123
|
+
if conn.flavor in json_flavors:
|
124
|
+
from sqlalchemy.dialects.postgresql import JSONB
|
125
|
+
params_type = JSONB
|
126
|
+
else:
|
127
|
+
params_type = sqlalchemy.types.Text
|
128
|
+
id_names = ('user_id', 'plugin_id', 'pipe_id')
|
129
|
+
sequences = {
|
130
|
+
k: sqlalchemy.Sequence(k + '_seq')
|
131
|
+
for k in id_names
|
132
|
+
}
|
133
|
+
id_col_args = { k: [k, id_type] for k in id_names }
|
134
|
+
id_col_kw = { k: {'primary_key': True} for k in id_names }
|
135
|
+
index_names = conn.flavor not in _skip_index_names_flavors
|
136
|
+
|
137
|
+
if conn.flavor in _sequence_flavors:
|
138
|
+
for k, args in id_col_args.items():
|
139
|
+
args.append(sequences[k])
|
140
|
+
for k, kw in id_col_kw.items():
|
141
|
+
kw.update({'server_default': sequences[k].next_value()})
|
142
|
+
|
143
|
+
_tables = {
|
144
|
+
'users': sqlalchemy.Table(
|
145
|
+
'mrsm_users',
|
146
|
+
conn.metadata,
|
147
|
+
sqlalchemy.Column(
|
148
|
+
*id_col_args['user_id'],
|
149
|
+
**id_col_kw['user_id'],
|
150
|
+
),
|
151
|
+
sqlalchemy.Column(
|
152
|
+
'username',
|
153
|
+
sqlalchemy.String(256),
|
154
|
+
index = index_names,
|
155
|
+
nullable = False,
|
156
|
+
),
|
157
|
+
sqlalchemy.Column('password_hash', sqlalchemy.String(1024)),
|
158
|
+
sqlalchemy.Column('email', sqlalchemy.String(256)),
|
159
|
+
sqlalchemy.Column('user_type', sqlalchemy.String(256)),
|
160
|
+
sqlalchemy.Column('attributes', params_type),
|
161
|
+
extend_existing = True,
|
108
162
|
),
|
109
|
-
sqlalchemy.
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
163
|
+
'plugins': sqlalchemy.Table(
|
164
|
+
*([
|
165
|
+
'mrsm_plugins',
|
166
|
+
conn.metadata,
|
167
|
+
sqlalchemy.Column(
|
168
|
+
*id_col_args['plugin_id'],
|
169
|
+
**id_col_kw['plugin_id'],
|
170
|
+
),
|
171
|
+
sqlalchemy.Column(
|
172
|
+
'plugin_name', sqlalchemy.String(256), index=index_names, nullable=False,
|
173
|
+
),
|
174
|
+
sqlalchemy.Column('user_id', sqlalchemy.Integer, nullable=False),
|
175
|
+
sqlalchemy.Column('version', sqlalchemy.String(256)),
|
176
|
+
sqlalchemy.Column('attributes', params_type),
|
177
|
+
] + ([
|
178
|
+
sqlalchemy.ForeignKeyConstraint(['user_id'], ['mrsm_users.user_id']),
|
179
|
+
] if conn.flavor != 'duckdb' else [])),
|
180
|
+
extend_existing = True,
|
114
181
|
),
|
115
|
-
|
116
|
-
|
117
|
-
sqlalchemy.Column('user_type', sqlalchemy.String(256)),
|
118
|
-
sqlalchemy.Column('attributes', params_type),
|
119
|
-
extend_existing = True,
|
120
|
-
),
|
121
|
-
'plugins': sqlalchemy.Table(
|
122
|
-
*([
|
123
|
-
'mrsm_plugins',
|
182
|
+
'temp_tables': sqlalchemy.Table(
|
183
|
+
'mrsm_temp_tables',
|
124
184
|
conn.metadata,
|
125
185
|
sqlalchemy.Column(
|
126
|
-
|
127
|
-
|
186
|
+
'date_created',
|
187
|
+
sqlalchemy.DateTime,
|
188
|
+
index = True,
|
189
|
+
nullable = False,
|
128
190
|
),
|
129
191
|
sqlalchemy.Column(
|
130
|
-
'
|
192
|
+
'table',
|
193
|
+
sqlalchemy.String(256),
|
194
|
+
index = index_names,
|
195
|
+
nullable = False,
|
131
196
|
),
|
132
|
-
sqlalchemy.Column(
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
197
|
+
sqlalchemy.Column(
|
198
|
+
'ready_to_drop',
|
199
|
+
sqlalchemy.DateTime,
|
200
|
+
index = False,
|
201
|
+
nullable = True,
|
202
|
+
),
|
203
|
+
extend_existing = True,
|
204
|
+
),
|
205
|
+
}
|
206
|
+
|
207
|
+
pipes_parameters_col = sqlalchemy.Column("parameters", params_type)
|
208
|
+
pipes_table_args = [
|
209
|
+
"mrsm_pipes",
|
142
210
|
conn.metadata,
|
143
211
|
sqlalchemy.Column(
|
144
|
-
'
|
145
|
-
|
146
|
-
|
212
|
+
*id_col_args['pipe_id'],
|
213
|
+
**id_col_kw['pipe_id'],
|
214
|
+
),
|
215
|
+
sqlalchemy.Column(
|
216
|
+
"connector_keys",
|
217
|
+
sqlalchemy.String(256),
|
218
|
+
index = index_names,
|
147
219
|
nullable = False,
|
148
220
|
),
|
149
221
|
sqlalchemy.Column(
|
150
|
-
|
222
|
+
"metric_key",
|
151
223
|
sqlalchemy.String(256),
|
152
224
|
index = index_names,
|
153
225
|
nullable = False,
|
154
226
|
),
|
155
227
|
sqlalchemy.Column(
|
156
|
-
|
157
|
-
sqlalchemy.
|
158
|
-
index =
|
228
|
+
"location_key",
|
229
|
+
sqlalchemy.String(256),
|
230
|
+
index = index_names,
|
159
231
|
nullable = True,
|
160
232
|
),
|
233
|
+
pipes_parameters_col,
|
234
|
+
]
|
235
|
+
if conn.flavor in json_flavors:
|
236
|
+
pipes_table_args.append(
|
237
|
+
sqlalchemy.Index(
|
238
|
+
'ix_mrsm_pipes_parameters_tags',
|
239
|
+
pipes_parameters_col['tags'],
|
240
|
+
postgresql_using='gin'
|
241
|
+
)
|
242
|
+
)
|
243
|
+
_tables['pipes'] = sqlalchemy.Table(
|
244
|
+
*pipes_table_args,
|
161
245
|
extend_existing = True,
|
162
|
-
),
|
163
|
-
}
|
164
|
-
|
165
|
-
_tables['pipes'] = sqlalchemy.Table(
|
166
|
-
"mrsm_pipes",
|
167
|
-
conn.metadata,
|
168
|
-
sqlalchemy.Column(
|
169
|
-
*id_col_args['pipe_id'],
|
170
|
-
**id_col_kw['pipe_id'],
|
171
|
-
),
|
172
|
-
sqlalchemy.Column(
|
173
|
-
"connector_keys",
|
174
|
-
sqlalchemy.String(256),
|
175
|
-
index = index_names,
|
176
|
-
nullable = False,
|
177
|
-
),
|
178
|
-
sqlalchemy.Column(
|
179
|
-
"metric_key",
|
180
|
-
sqlalchemy.String(256),
|
181
|
-
index = index_names,
|
182
|
-
nullable = False,
|
183
|
-
),
|
184
|
-
sqlalchemy.Column(
|
185
|
-
"location_key",
|
186
|
-
sqlalchemy.String(256),
|
187
|
-
index = index_names,
|
188
|
-
nullable = True,
|
189
|
-
),
|
190
|
-
sqlalchemy.Column("parameters", params_type),
|
191
|
-
extend_existing = True,
|
192
|
-
)
|
193
|
-
|
194
|
-
### store the table dict for reuse (per connector)
|
195
|
-
connector_tables[conn] = _tables
|
196
|
-
if create:
|
197
|
-
create_schemas(
|
198
|
-
conn,
|
199
|
-
schemas = [conn.internal_schema],
|
200
|
-
debug = debug,
|
201
246
|
)
|
202
|
-
create_tables(conn, tables=_tables)
|
203
247
|
|
204
|
-
|
248
|
+
### store the table dict for reuse (per connector)
|
249
|
+
connector_tables[conn_key] = _tables
|
250
|
+
|
251
|
+
if debug:
|
252
|
+
dprint(f"Built in-memory tables for '{conn}'.")
|
253
|
+
|
254
|
+
if create:
|
255
|
+
if debug:
|
256
|
+
dprint(f"Creating tables for connector '{conn}'.")
|
257
|
+
|
258
|
+
create_schemas(
|
259
|
+
conn,
|
260
|
+
schemas = [conn.internal_schema],
|
261
|
+
debug = debug,
|
262
|
+
)
|
263
|
+
create_tables(conn, tables=_tables)
|
264
|
+
|
265
|
+
_write_create_cache(mrsm.get_connector(str(mrsm_instance)), debug=debug)
|
266
|
+
|
267
|
+
if conn.flavor != 'sqlite':
|
268
|
+
with open(pickle_path, 'wb') as f:
|
269
|
+
pickle.dump(conn.metadata, f)
|
270
|
+
|
271
|
+
connector_tables[conn_key] = _tables
|
272
|
+
return connector_tables[conn_key]
|
205
273
|
|
206
274
|
|
207
275
|
def create_tables(
|
208
|
-
conn:
|
276
|
+
conn: mrsm.connectors.SQLConnector,
|
209
277
|
tables: Optional[Dict[str, 'sqlalchemy.Table']] = None,
|
210
278
|
) -> bool:
|
211
279
|
"""
|
@@ -224,14 +292,13 @@ def create_tables(
|
|
224
292
|
|
225
293
|
|
226
294
|
def create_schemas(
|
227
|
-
conn:
|
295
|
+
conn: mrsm.connectors.SQLConnector,
|
228
296
|
schemas: List[str],
|
229
297
|
debug: bool = False,
|
230
298
|
) -> bool:
|
231
299
|
"""
|
232
300
|
Create the internal Meerschaum schema on the database.
|
233
301
|
"""
|
234
|
-
from meerschaum.config.static import STATIC_CONFIG
|
235
302
|
from meerschaum.utils.packages import attempt_import
|
236
303
|
from meerschaum.utils.sql import sql_item_name, NO_SCHEMA_FLAVORS, SKIP_IF_EXISTS_FLAVORS
|
237
304
|
if conn.flavor in NO_SCHEMA_FLAVORS:
|
@@ -257,3 +324,68 @@ def create_schemas(
|
|
257
324
|
except Exception as e:
|
258
325
|
warn(f"Failed to create internal schema '{schema}':\n{e}")
|
259
326
|
return all(successes.values())
|
327
|
+
|
328
|
+
|
329
|
+
def _check_create_cache(connector: mrsm.connectors.SQLConnector, debug: bool = False) -> bool:
|
330
|
+
"""
|
331
|
+
Return `True` if the metadata cache is missing or expired.
|
332
|
+
"""
|
333
|
+
import json
|
334
|
+
from datetime import datetime, timedelta
|
335
|
+
from meerschaum.utils.dtypes import get_current_timestamp
|
336
|
+
|
337
|
+
if connector.type != 'sql':
|
338
|
+
return False
|
339
|
+
|
340
|
+
path = connector.get_metadata_cache_path()
|
341
|
+
if not path.exists():
|
342
|
+
if debug:
|
343
|
+
dprint(f"Metadata cache doesn't exist for '{connector}'.")
|
344
|
+
return True
|
345
|
+
|
346
|
+
try:
|
347
|
+
with open(path, 'r', encoding='utf-8') as f:
|
348
|
+
metadata = json.load(f)
|
349
|
+
except Exception:
|
350
|
+
return True
|
351
|
+
|
352
|
+
created_str = metadata.get('created', None)
|
353
|
+
if not created_str:
|
354
|
+
return True
|
355
|
+
|
356
|
+
now = get_current_timestamp()
|
357
|
+
created = datetime.fromisoformat(created_str)
|
358
|
+
|
359
|
+
delta = now - created
|
360
|
+
threshold_minutes = (
|
361
|
+
mrsm.get_config('system', 'connectors', 'sql', 'instance', 'create_metadata_cache_minutes')
|
362
|
+
)
|
363
|
+
threshold_delta = timedelta(minutes=threshold_minutes)
|
364
|
+
if delta >= threshold_delta:
|
365
|
+
if debug:
|
366
|
+
dprint(f"Metadata cache expired for '{connector}'.")
|
367
|
+
return True
|
368
|
+
|
369
|
+
if debug:
|
370
|
+
dprint(f"Using cached metadata for '{connector}'.")
|
371
|
+
|
372
|
+
return False
|
373
|
+
|
374
|
+
|
375
|
+
def _write_create_cache(connector: mrsm.connectors.SQLConnector, debug: bool = False):
|
376
|
+
"""
|
377
|
+
Write the current timestamp to the cache file.
|
378
|
+
"""
|
379
|
+
if connector.type != 'sql':
|
380
|
+
return
|
381
|
+
|
382
|
+
import json
|
383
|
+
from meerschaum.utils.dtypes import get_current_timestamp, json_serialize_value
|
384
|
+
|
385
|
+
if debug:
|
386
|
+
dprint(f"Writing metadata cache for '{connector}'.")
|
387
|
+
|
388
|
+
path = connector.get_metadata_cache_path()
|
389
|
+
now = get_current_timestamp()
|
390
|
+
with open(path, 'w+', encoding='utf-8') as f:
|
391
|
+
json.dump({'created': now}, f, default=json_serialize_value)
|
@@ -10,19 +10,18 @@ import json
|
|
10
10
|
from datetime import datetime, timezone
|
11
11
|
|
12
12
|
import meerschaum as mrsm
|
13
|
-
from meerschaum.connectors import
|
13
|
+
from meerschaum.connectors import InstanceConnector, make_connector
|
14
14
|
from meerschaum.utils.typing import List, Dict, Any, Optional, Union
|
15
15
|
from meerschaum.utils.warnings import dprint
|
16
16
|
|
17
17
|
|
18
18
|
@make_connector
|
19
|
-
class ValkeyConnector(
|
19
|
+
class ValkeyConnector(InstanceConnector):
|
20
20
|
"""
|
21
21
|
Manage a Valkey instance.
|
22
22
|
|
23
23
|
Build a `ValkeyConnector` from connection attributes or a URI string.
|
24
24
|
"""
|
25
|
-
IS_INSTANCE: bool = True
|
26
25
|
REQUIRED_ATTRIBUTES: List[str] = ['host']
|
27
26
|
OPTIONAL_ATTRIBUTES: List[str] = [
|
28
27
|
'port', 'username', 'password', 'db', 'socket_timeout',
|
@@ -80,7 +79,6 @@ class ValkeyConnector(Connector):
|
|
80
79
|
get_plugin_user_id,
|
81
80
|
get_plugin_username,
|
82
81
|
get_plugin_attributes,
|
83
|
-
get_plugins,
|
84
82
|
delete_plugin,
|
85
83
|
)
|
86
84
|
|
@@ -142,13 +140,13 @@ class ValkeyConnector(Connector):
|
|
142
140
|
|
143
141
|
return uri
|
144
142
|
|
145
|
-
def set(self, key: str, value: Any, **kwargs: Any) ->
|
143
|
+
def set(self, key: str, value: Any, **kwargs: Any) -> bool:
|
146
144
|
"""
|
147
145
|
Set the `key` to `value`.
|
148
146
|
"""
|
149
147
|
return self.client.set(key, value, **kwargs)
|
150
148
|
|
151
|
-
def get(self, key: str) -> Union[str, None]:
|
149
|
+
def get(self, key: str, decode: bool = True) -> Union[str, None]:
|
152
150
|
"""
|
153
151
|
Get the value for `key`.
|
154
152
|
"""
|
@@ -156,7 +154,7 @@ class ValkeyConnector(Connector):
|
|
156
154
|
if val is None:
|
157
155
|
return None
|
158
156
|
|
159
|
-
return val.decode('utf-8')
|
157
|
+
return val.decode('utf-8') if decode else val
|
160
158
|
|
161
159
|
def test_connection(self) -> bool:
|
162
160
|
"""
|