|
35 | 35 | from common.core.config import settings |
36 | 36 | import sqlglot |
37 | 37 | from sqlglot import expressions as exp |
38 | | -from sqlalchemy.pool import NullPool |
39 | 38 | from pyhive import hive |
40 | 39 |
|
41 | 40 | try: |
@@ -144,37 +143,49 @@ def get_engine(ds: CoreDatasource, timeout: int = 0) -> Engine: |
144 | 143 | if timeout > 0: |
145 | 144 | conf.timeout = timeout |
146 | 145 |
|
| 146 | + db_config = { |
| 147 | + 'pool_size': conf.poolSize if conf.poolSize else 5, |
| 148 | + 'max_overflow': 20, |
| 149 | + 'pool_recycle': 3600 |
| 150 | + } |
| 151 | + |
147 | 152 | if equals_ignore_case(ds.type, "pg"): |
148 | 153 | if conf.dbSchema is not None and conf.dbSchema != "": |
149 | 154 | engine = create_engine(get_uri(ds), |
150 | 155 | connect_args={"options": f"-c search_path={urllib.parse.quote(conf.dbSchema)}", |
151 | | - "connect_timeout": conf.timeout}, poolclass=NullPool) |
| 156 | + "connect_timeout": conf.timeout}, **db_config) |
152 | 157 | else: |
153 | | - engine = create_engine(get_uri(ds), connect_args={"connect_timeout": conf.timeout}, poolclass=NullPool) |
| 158 | + engine = create_engine(get_uri(ds), connect_args={"connect_timeout": conf.timeout}, **db_config) |
154 | 159 | elif equals_ignore_case(ds.type, 'sqlServer'): |
155 | 160 | engine = create_engine('mssql+pymssql://', creator=lambda: get_origin_connect(ds.type, conf), |
156 | | - poolclass=NullPool) |
| 161 | + **db_config) |
157 | 162 | elif equals_ignore_case(ds.type, 'oracle'): |
158 | | - engine = create_engine(get_uri(ds), poolclass=NullPool) |
| 163 | + engine = create_engine(get_uri(ds), **db_config) |
159 | 164 | elif equals_ignore_case(ds.type, 'mysql'): # mysql |
160 | 165 | ssl_mode = {"require": True} if conf.ssl else None |
161 | 166 | engine = create_engine(get_uri(ds), connect_args={"connect_timeout": conf.timeout, "ssl": ssl_mode}, |
162 | | - poolclass=NullPool) |
| 167 | + **db_config) |
163 | 168 | else: # ck |
164 | | - engine = create_engine(get_uri(ds), connect_args={"connect_timeout": conf.timeout}, poolclass=NullPool) |
| 169 | + engine = create_engine(get_uri(ds), connect_args={"connect_timeout": conf.timeout}, **db_config) |
165 | 170 | return engine |
166 | 171 |
|
167 | 172 |
|
| 173 | +def get_connection(ds: CoreDatasource | AssistantOutDsSchema, db_config): |
| 174 | + pass |
| 175 | + |
| 176 | + |
168 | 177 | def get_session(ds: CoreDatasource | AssistantOutDsSchema): |
169 | 178 | # engine = get_engine(ds) if isinstance(ds, CoreDatasource) else get_ds_engine(ds) |
170 | 179 | if isinstance(ds, AssistantOutDsSchema): |
171 | 180 | out_conf = get_out_ds_conf(ds, 30) |
172 | 181 | ds.configuration = out_conf |
173 | 182 |
|
174 | | - engine = get_engine(ds) |
175 | | - session_maker = sessionmaker(bind=engine) |
176 | | - session = session_maker() |
177 | | - return session |
| 183 | + # engine = get_engine(ds) |
| 184 | + # session_maker = sessionmaker(bind=engine) |
| 185 | + |
| 186 | + # get session from pool |
| 187 | + session = pool_manager.get_pool(ds=ds, **{}) |
| 188 | + return session() |
178 | 189 |
|
179 | 190 |
|
180 | 191 | def check_connection(trans: Optional[Trans], ds: CoreDatasource | AssistantOutDsSchema, is_raise: bool = False): |
@@ -992,3 +1003,53 @@ def checkParams(extraParams: str, illegalParams: List[str]): |
992 | 1003 | k, v = kv.split('=') |
993 | 1004 | if k in illegalParams: |
994 | 1005 | raise HTTPException(status_code=500, detail=f'Illegal Parameter: {k}') |
| 1006 | + |
| 1007 | + |
| 1008 | +import threading |
| 1009 | +from collections import OrderedDict |
| 1010 | + |
| 1011 | + |
| 1012 | +class ConnectionPoolManager: |
| 1013 | + def __init__(self, max_pools=500): |
| 1014 | + """ |
| 1015 | + init |
| 1016 | + :param max_pools: max pool |
| 1017 | + """ |
| 1018 | + self.max_pools = max_pools |
| 1019 | + self._pools = OrderedDict() # 使用有序字典实现 LRU |
| 1020 | + self._lock = threading.Lock() # 保证多线程安全 |
| 1021 | + |
| 1022 | + def get_pool(self, ds: CoreDatasource | AssistantOutDsSchema, **db_config): |
| 1023 | + """ |
| 1024 | + get connection pool(lazy load + LRU update) |
| 1025 | + """ |
| 1026 | + with self._lock: |
| 1027 | + if ds.id: |
| 1028 | + # 1. 如果连接池已存在,将其移动到字典末尾(标记为最近使用) |
| 1029 | + if ds.id in self._pools: |
| 1030 | + self._pools.move_to_end(ds.id) |
| 1031 | + print(f"[LRU] return: {ds.id}") |
| 1032 | + return self._pools[ds.id] |
| 1033 | + |
| 1034 | + # 2. 如果连接池不存在,检查是否达到上限,若达到则淘汰最久未使用的(字典头部) |
| 1035 | + if len(self._pools) >= self.max_pools: |
| 1036 | + oldest_id, oldest_pool = self._pools.popitem(last=False) |
| 1037 | + oldest_pool.close() # 安全关闭被驱逐的连接池 |
| 1038 | + print(f"[LRU] remove oldest: {oldest_id}") |
| 1039 | + |
| 1040 | + # 3. 创建新连接池并放入字典末尾 |
| 1041 | + engine = get_engine(ds) |
| 1042 | + new_pool = sessionmaker(bind=engine) |
| 1043 | + self._pools[ds.id] = new_pool |
| 1044 | + print(f"[LRU] create: {ds.id}") |
| 1045 | + return new_pool |
| 1046 | + |
| 1047 | + def close_all(self): |
| 1048 | + """stop""" |
| 1049 | + with self._lock: |
| 1050 | + for pool in self._pools.values(): |
| 1051 | + pool.close() |
| 1052 | + self._pools.clear() |
| 1053 | + |
| 1054 | + |
| 1055 | +pool_manager = ConnectionPoolManager(max_pools=500) |
0 commit comments