userver
C++ Async Framework
Toggle main menu visibility
Loading...
Searching...
No Matches
control.py
1
import
dataclasses
2
import
logging
3
import
pathlib
4
5
import
clickhouse_driver
6
7
from
.
import
classes
8
9
logger = logging.getLogger(__name__)
10
11
12
@dataclasses.dataclass(frozen=True)
13
class
ClickhouseQuery
:
14
body: str
15
source: str
16
path: str |
None
17
18
19
class
ConnectionCache
:
20
def
__init__(self, conn_info: classes.ConnectionInfo):
21
self.
_conn_info
= conn_info
22
self.
_cache
: dict[str, clickhouse_driver.Client] = {}
23
self.
_master_connection
=
None
24
25
def
get_master_connection(self):
26
return
self.
get_connection
(
'default'
)
27
28
def
get_connection(self, dbname: str) -> clickhouse_driver.Client:
29
if
dbname
not
in
self.
_cache
:
30
self.
_cache
[dbname] = self.
_create_connection
(dbname)
31
return
self.
_cache
[dbname]
32
33
def
get_connection_info(self, dbname: str) ->
classes.ConnectionInfo
:
34
return
self.
_conn_info
.replace(dbname=dbname)
35
36
def
_create_connection(self, dbname: str):
37
return
self.
_connect
(self.
get_connection_info
(dbname))
38
39
def
_connect(self, conn_info: classes.ConnectionInfo):
40
return
clickhouse_driver.Client(
41
host=conn_info.host,
42
port=conn_info.tcp_port,
43
database=conn_info.dbname,
44
)
45
46
47
class
DatabasesState
:
48
_migrations_run: set[tuple[str, pathlib.Path]]
49
_initialized: set[str]
50
51
def
__init__(self, connections: ConnectionCache, verbose: bool =
False
):
52
self.
_connections
= connections
53
self.
_verbose
= verbose
54
self.
_migrations_run
= set()
55
self.
_initialized
= set()
56
57
def
get_connection(
58
self,
59
dbname: str,
60
create_db: bool =
True
,
61
) -> clickhouse_driver.Client:
62
if
dbname
not
in
self.
_initialized
:
63
if
create_db:
64
self.
_init_db
(dbname)
65
self.
_initialized
.add(dbname)
66
return
self.
_connections
.get_connection(dbname)
67
68
def
run_migration(self, dbname: str, path: pathlib.Path):
69
key = dbname, path
70
if
key
in
self.
_migrations_run
:
71
return
72
logger.debug(
73
'Running clickhouse-sql script %s against database %s'
,
74
path,
75
dbname,
76
)
77
conn = self.
get_connection
(dbname)
78
conn.execute(path.read_text())
79
self.
_migrations_run
.add(key)
80
81
def
_init_db(self, dbname: str):
82
conn = self.
_connections
.get_master_connection()
83
conn.execute(f
'DROP DATABASE IF EXISTS `{dbname}`'
)
84
conn.execute(f
'CREATE DATABASE `{dbname}`'
)
85
86
87
class
Control
:
88
def
__init__(
89
self,
90
databases: classes.DatabasesDict,
91
state: DatabasesState,
92
):
93
self.
_databases
= databases
94
self.
_state
= state
95
96
def
get_connections(self):
97
return
{
98
alias: self.
_state
.get_connection(dbconfig.dbname)
99
for
alias, dbconfig
in
self.
_databases
.items()
100
}
101
102
def
run_migrations(self):
103
for
dbconfig
in
self.
_databases
.values():
104
self.
_run_database_migrations
(dbconfig)
105
106
def
_run_database_migrations(self, dbconfig: classes.DatabaseConfig):
107
for
path
in
dbconfig.migrations:
108
self.
_state
.run_migration(dbconfig.dbname, path)
109
110
111
def
_get_db_tables_list(connection: clickhouse_driver.Client):
112
return
connection.execute(
'SHOW TABLES'
)
113
114
115
def
apply_queries(
116
connection: clickhouse_driver.Client,
117
queries: list[ClickhouseQuery],
118
):
119
tables = _get_db_tables_list(connection)
120
if
tables:
121
for
(table,)
in
tables:
122
connection.execute(f
'TRUNCATE TABLE `{table}`'
)
123
124
for
query
in
queries:
125
try
:
126
connection.execute(query.body)
127
except
Exception
as
exc:
128
error_message = (
129
f
'ClickHouse apply query error\nQuery from {query.source}\n'
130
)
131
if
query.path:
132
error_message += f
'File path: {query.path}\n'
133
error_message +=
'\n'
+ str(exc)
134
raise
RuntimeError(error_message)
en
testsuite
databases
clickhouse
control.py
Generated on
for userver by
Doxygen
1.17.0