Commit 3a86c294 3a86c294702648355efd1ecd7ab296dce68cbe4b by 沈秋雨

refactor(connections): 实现数据库连接池和连接管理机制

- 新增 MySQL 和 PostgreSQL 连接池类,支持按需创建和自动回收连接
- 增加连接池全局实例及其获取函数,实现连接复用
- 修改原有连接获取函数,改用连接池管理连接
- 添加 refresh_conn 函数用于检测和刷新断开的连接
- 在 runner 模块中批处理循环前调用 refresh_conn 保证长时间任务连接可用
- 用 close_all_pools 统一关闭所有连接池连接,替代原单独关闭调用
- 提升长期运行任务的数据库连接稳定性与效率
1 parent cdf0d9e2
1 import time
1 import pymysql 2 import pymysql
2 import pymysql.cursors 3 import pymysql.cursors
3 import pg8000 4 import pg8000
...@@ -5,6 +6,10 @@ import oss2 ...@@ -5,6 +6,10 @@ import oss2
5 from .config import SOURCE_DB, HK_SONGS_DB, CRAWLER_DB, OSS_CONFIG, OSS_CONNECTION_POOL_SIZE 6 from .config import SOURCE_DB, HK_SONGS_DB, CRAWLER_DB, OSS_CONFIG, OSS_CONNECTION_POOL_SIZE
6 7
7 8
9 CONN_POOL_SIZE = 4
10 CONN_RECYCLE_SECONDS = 600 # 连接最长存活 10 分钟,超时自动换新
11
12
8 def _strip_nul(value): 13 def _strip_nul(value):
9 """PostgreSQL text/json 参数不接受 NUL 字符,递归清理全部绑定参数。""" 14 """PostgreSQL text/json 参数不接受 NUL 字符,递归清理全部绑定参数。"""
10 if isinstance(value, str): 15 if isinstance(value, str):
...@@ -57,27 +62,272 @@ class _NulSafePgConnection: ...@@ -57,27 +62,272 @@ class _NulSafePgConnection:
57 return getattr(self._connection, name) 62 return getattr(self._connection, name)
58 63
59 64
60 def get_source_conn() -> pymysql.Connection: 65 class _MySqlConnPool:
61 return pymysql.connect(**SOURCE_DB, cursorclass=pymysql.cursors.DictCursor) 66 """MySQL 连接池:按需创建连接,定期回收,获取时自动重连。"""
62 67
68 def __init__(self, connect_kwargs, size=CONN_POOL_SIZE, recycle=CONN_RECYCLE_SECONDS):
69 self._connect_kwargs = connect_kwargs
70 self._size = size
71 self._recycle = recycle
72 self._pool = []
73 self._created_at = []
63 74
64 def get_spider_conn() -> pymysql.Connection: 75 def _new_conn(self):
76 conn = pymysql.connect(**self._connect_kwargs, cursorclass=pymysql.cursors.DictCursor)
77 self._pool.append(conn)
78 self._created_at.append(time.monotonic())
79 return conn
80
81 def get(self) -> pymysql.Connection:
82 for i in range(len(self._pool)):
83 conn = self._pool[i]
84 try:
85 conn.ping(reconnect=False)
86 except Exception:
87 pass
88 else:
89 if time.monotonic() - self._created_at[i] < self._recycle:
90 return conn
91 # 连接超时回收
92 try:
93 conn.close()
94 except Exception:
95 pass
96 self._pool.pop(i)
97 self._created_at.pop(i)
98 return self._new_conn()
99 # ping 失败,尝试重连
100 try:
101 conn.ping(reconnect=True)
102 self._created_at[i] = time.monotonic()
103 return conn
104 except Exception:
105 try:
106 conn.close()
107 except Exception:
108 pass
109 self._pool.pop(i)
110 self._created_at.pop(i)
111 return self._new_conn()
112 return self._new_conn()
113
114 def check(self, conn) -> pymysql.Connection:
115 """确保连接可用;如断开则自动替换为新连接。"""
116 try:
117 conn.ping(reconnect=False)
118 except Exception:
119 pass
120 else:
121 if id(conn) in {id(c) for c in self._pool}:
122 return conn
123 try:
124 conn.ping(reconnect=True)
125 return conn
126 except Exception:
127 pass
128 try:
129 idx = None
130 for i, c in enumerate(self._pool):
131 if id(c) == id(conn):
132 idx = i
133 break
134 if idx is not None:
135 try:
136 conn.close()
137 except Exception:
138 pass
139 self._pool.pop(idx)
140 self._created_at.pop(idx)
141 except Exception:
142 pass
143 return self._new_conn()
144
145 def close_all(self):
146 for conn in self._pool:
147 try:
148 conn.close()
149 except Exception:
150 pass
151 self._pool.clear()
152 self._created_at.clear()
153
154
155 class _PgConnPool:
156 """PostgreSQL 连接池:按需创建连接,定期回收,获取时自动重连。"""
157
158 def __init__(self, connect_kwargs, size=CONN_POOL_SIZE, recycle=CONN_RECYCLE_SECONDS):
159 self._connect_kwargs = connect_kwargs
160 self._size = size
161 self._recycle = recycle
162 self._pool = []
163 self._created_at = []
164
165 def _new_conn(self):
166 conn = pg8000.connect(**self._connect_kwargs)
167 conn.run("SET TimeZone = 'Asia/Shanghai'")
168 wrapped = _NulSafePgConnection(conn)
169 self._pool.append(conn)
170 self._created_at.append(time.monotonic())
171 return wrapped
172
173 def get(self) -> _NulSafePgConnection:
174 for i in range(len(self._pool)):
175 conn = self._pool[i]
176 try:
177 conn.run("SELECT 1")
178 except Exception:
179 try:
180 conn.close()
181 except Exception:
182 pass
183 self._pool.pop(i)
184 self._created_at.pop(i)
185 return self._new_conn()
186 if time.monotonic() - self._created_at[i] < self._recycle:
187 return _NulSafePgConnection(conn)
188 # 回收
189 try:
190 conn.close()
191 except Exception:
192 pass
193 self._pool.pop(i)
194 self._created_at.pop(i)
195 return self._new_conn()
196 return self._new_conn()
197
198 def check(self, conn) -> _NulSafePgConnection:
199 """确保连接可用;如断开则自动替换为新连接。"""
200 try:
201 conn.run("SELECT 1")
202 except Exception:
203 pass
204 else:
205 raw = conn._connection if hasattr(conn, '_connection') else conn
206 if id(raw) in {id(c) for c in self._pool}:
207 return conn
208 # 创建新连接替换当前连接
209 try:
210 idx = None
211 for i, c in enumerate(self._pool):
212 if id(c) == id(raw):
213 idx = i
214 break
215 if idx is not None:
216 try:
217 raw.close()
218 except Exception:
219 pass
220 self._pool.pop(idx)
221 self._created_at.pop(idx)
222 except Exception:
223 pass
224 return self._new_conn()
225
226 def close_all(self):
227 for conn in self._pool:
228 try:
229 conn.close()
230 except Exception:
231 pass
232 self._pool.clear()
233 self._created_at.clear()
234
235
236 # ── 全局连接池实例 ──
237
238 _source_pool = None
239 _hk_songs_pool = None
240 _spider_pool = None
241 _pg_pool = None
242
243
244 def _get_source_pool() -> _MySqlConnPool:
245 global _source_pool
246 if _source_pool is None:
247 _source_pool = _MySqlConnPool(SOURCE_DB)
248 return _source_pool
249
250
251 def _get_hk_songs_pool() -> _MySqlConnPool:
252 global _hk_songs_pool
253 if _hk_songs_pool is None:
254 _hk_songs_pool = _MySqlConnPool(HK_SONGS_DB)
255 return _hk_songs_pool
256
257
258 def _get_spider_pool() -> _MySqlConnPool:
259 global _spider_pool
260 if _spider_pool is None:
65 cfg = SOURCE_DB.copy() 261 cfg = SOURCE_DB.copy()
66 cfg['database'] = 'hikoon-data-spider' 262 cfg['database'] = 'hikoon-data-spider'
67 return pymysql.connect(**cfg, cursorclass=pymysql.cursors.DictCursor) 263 _spider_pool = _MySqlConnPool(cfg)
264 return _spider_pool
265
266
267 def _get_pg_pool() -> _PgConnPool:
268 global _pg_pool
269 if _pg_pool is None:
270 _pg_pool = _PgConnPool(CRAWLER_DB)
271 return _pg_pool
272
273
274 def get_source_conn() -> pymysql.Connection:
275 return _get_source_pool().get()
276
277
278 def get_spider_conn() -> pymysql.Connection:
279 return _get_spider_pool().get()
68 280
69 281
70 def get_hk_songs_conn() -> pymysql.Connection: 282 def get_hk_songs_conn() -> pymysql.Connection:
71 return pymysql.connect(**HK_SONGS_DB, cursorclass=pymysql.cursors.DictCursor) 283 return _get_hk_songs_pool().get()
72 284
73 285
74 def get_pg_conn() -> _NulSafePgConnection: 286 def get_pg_conn() -> _NulSafePgConnection:
75 conn = pg8000.connect(**CRAWLER_DB) 287 return _get_pg_pool().get()
76 conn.run("SET TimeZone = 'Asia/Shanghai'")
77 return _NulSafePgConnection(conn)
78 288
79 289
80 def get_oss_bucket() -> oss2.Bucket: 290 def get_oss_bucket() -> oss2.Bucket:
81 oss2.defaults.connection_pool_size = OSS_CONNECTION_POOL_SIZE 291 oss2.defaults.connection_pool_size = OSS_CONNECTION_POOL_SIZE
82 auth = oss2.Auth(OSS_CONFIG['access_key_id'], OSS_CONFIG['access_key_secret']) 292 auth = oss2.Auth(OSS_CONFIG['access_key_id'], OSS_CONFIG['access_key_secret'])
83 return oss2.Bucket(auth, OSS_CONFIG['endpoint'], OSS_CONFIG['bucket_name']) 293 return oss2.Bucket(auth, OSS_CONFIG['endpoint'], OSS_CONFIG['bucket_name'])
294
295
296 def close_all_pools():
297 """关闭所有连接池中的连接。应在 ETL 退出时调用。"""
298 for pool in (_source_pool, _hk_songs_pool, _spider_pool):
299 if pool is not None:
300 pool.close_all()
301 if _pg_pool is not None:
302 _pg_pool.close_all()
303
304
305 def refresh_conn(conn, pool_type: str):
306 """验证连接健康度,断开时返回新连接,正常时原样返回。
307
308 用法:在批次循环开头调用,如 ``conn = refresh_conn(conn, 'pg')``。
309 pool_type 取 'source' | 'hk_songs' | 'spider' | 'pg'。
310 """
311 try:
312 if pool_type == 'pg':
313 conn.run("SELECT 1")
314 return conn
315 # MySQL
316 conn.ping(reconnect=False)
317 return conn
318 except Exception:
319 pass
320
321 pools = {
322 'source': _get_source_pool,
323 'hk_songs': _get_hk_songs_pool,
324 'spider': _get_spider_pool,
325 'pg': _get_pg_pool,
326 }
327 pool = pools.get(pool_type)
328 if pool is None:
329 raise ValueError(f"unknown pool_type: {pool_type}")
330
331 if pool_type == 'pg':
332 return pool().check(conn)
333 return pool().check(conn)
......
...@@ -5,7 +5,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed ...@@ -5,7 +5,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
5 from tqdm import tqdm 5 from tqdm import tqdm
6 6
7 from .config import PLATFORM_QQ, PLATFORM_KUGOU, PLATFORM_NETEASE, BATCH_SIZE, BACKFILL_BATCH_SIZE, OSS_CONFIG 7 from .config import PLATFORM_QQ, PLATFORM_KUGOU, PLATFORM_NETEASE, BATCH_SIZE, BACKFILL_BATCH_SIZE, OSS_CONFIG
8 from .connections import get_hk_songs_conn, get_source_conn, get_spider_conn, get_pg_conn, get_oss_bucket 8 from .connections import get_hk_songs_conn, get_source_conn, get_spider_conn, get_pg_conn, get_oss_bucket, refresh_conn, close_all_pools
9 from .reader import ( 9 from .reader import (
10 iter_hk_songs_batches, 10 iter_hk_songs_batches,
11 iter_hk_songs_records2_batches, 11 iter_hk_songs_records2_batches,
...@@ -1185,6 +1185,11 @@ def initialize_yinyan_song_records(platforms: list[str], max_batches: int | None ...@@ -1185,6 +1185,11 @@ def initialize_yinyan_song_records(platforms: list[str], max_batches: int | None
1185 skipped_batches = 0 1185 skipped_batches = 0
1186 try: 1186 try:
1187 for i, batch in enumerate(tqdm(iter_hk_songs_batches(hk_conn, BATCH_SIZE), desc='init-yinyan')): 1187 for i, batch in enumerate(tqdm(iter_hk_songs_batches(hk_conn, BATCH_SIZE), desc='init-yinyan')):
1188 # 长任务连接可能断开,每批次开始前刷新
1189 hk_conn = refresh_conn(hk_conn, 'hk_songs')
1190 src_conn = refresh_conn(src_conn, 'source')
1191 pg_conn = refresh_conn(pg_conn, 'pg')
1192
1188 if max_batches is not None and i >= max_batches: 1193 if max_batches is not None and i >= max_batches:
1189 break 1194 break
1190 # 过滤掉已存在的 song_id 1195 # 过滤掉已存在的 song_id
...@@ -1236,9 +1241,7 @@ def initialize_yinyan_song_records(platforms: list[str], max_batches: int | None ...@@ -1236,9 +1241,7 @@ def initialize_yinyan_song_records(platforms: list[str], max_batches: int | None
1236 hk_conn.commit() 1241 hk_conn.commit()
1237 log.info("Init: marked %d unmatched songs as deleted", len(unmatched_ids)) 1242 log.info("Init: marked %d unmatched songs as deleted", len(unmatched_ids))
1238 finally: 1243 finally:
1239 hk_conn.close() 1244 close_all_pools()
1240 src_conn.close()
1241 pg_conn.close()
1242 1245
1243 log.info("Initialized yinyan_song_records candidates=%d, skipped_batches=%d", total, skipped_batches) 1246 log.info("Initialized yinyan_song_records candidates=%d, skipped_batches=%d", total, skipped_batches)
1244 1247
...@@ -1254,6 +1257,11 @@ def initialize_yinyan_song_records2(platforms: list[str], max_batches: int | Non ...@@ -1254,6 +1257,11 @@ def initialize_yinyan_song_records2(platforms: list[str], max_batches: int | Non
1254 for index, batch in enumerate( 1257 for index, batch in enumerate(
1255 tqdm(iter_hk_songs_records2_batches(hk_conn, BATCH_SIZE), desc='init-yinyan-records2') 1258 tqdm(iter_hk_songs_records2_batches(hk_conn, BATCH_SIZE), desc='init-yinyan-records2')
1256 ): 1259 ):
1260 # 长任务连接可能断开,每批次开始前刷新
1261 hk_conn = refresh_conn(hk_conn, 'hk_songs')
1262 src_conn = refresh_conn(src_conn, 'source')
1263 pg_conn = refresh_conn(pg_conn, 'pg')
1264
1257 if max_batches is not None and index >= max_batches: 1265 if max_batches is not None and index >= max_batches:
1258 break 1266 break
1259 1267
...@@ -1281,9 +1289,7 @@ def initialize_yinyan_song_records2(platforms: list[str], max_batches: int | Non ...@@ -1281,9 +1289,7 @@ def initialize_yinyan_song_records2(platforms: list[str], max_batches: int | Non
1281 deleted = delete_yinyan_song_records2_existing_relations(pg_cur) 1289 deleted = delete_yinyan_song_records2_existing_relations(pg_cur)
1282 pg_conn.commit() 1290 pg_conn.commit()
1283 finally: 1291 finally:
1284 hk_conn.close() 1292 close_all_pools()
1285 src_conn.close()
1286 pg_conn.close()
1287 1293
1288 log.info( 1294 log.info(
1289 "Initialized yinyan_song_records2 candidates=%d, removed_existing_records1_relations=%d", 1295 "Initialized yinyan_song_records2 candidates=%d, removed_existing_records1_relations=%d",
...@@ -1302,6 +1308,10 @@ def backfill_yinyan_record_platforms(max_batches: int | None = None) -> None: ...@@ -1302,6 +1308,10 @@ def backfill_yinyan_record_platforms(max_batches: int | None = None) -> None:
1302 batch_index = 0 1308 batch_index = 0
1303 pbar = tqdm(desc='backfill-yinyan-platforms') 1309 pbar = tqdm(desc='backfill-yinyan-platforms')
1304 while max_batches is None or batch_index < max_batches: 1310 while max_batches is None or batch_index < max_batches:
1311 # 长任务连接可能断开,每批次开始前刷新
1312 src_conn = refresh_conn(src_conn, 'source')
1313 pg_conn = refresh_conn(pg_conn, 'pg')
1314
1305 with pg_conn.cursor() as pg_cur: 1315 with pg_conn.cursor() as pg_cur:
1306 rows = fetch_yinyan_records_missing_platform(pg_cur, BACKFILL_BATCH_SIZE) 1316 rows = fetch_yinyan_records_missing_platform(pg_cur, BACKFILL_BATCH_SIZE)
1307 if not rows: 1317 if not rows:
...@@ -1334,8 +1344,7 @@ def backfill_yinyan_record_platforms(max_batches: int | None = None) -> None: ...@@ -1334,8 +1344,7 @@ def backfill_yinyan_record_platforms(max_batches: int | None = None) -> None:
1334 pbar.update(1) 1344 pbar.update(1)
1335 pbar.close() 1345 pbar.close()
1336 finally: 1346 finally:
1337 src_conn.close() 1347 close_all_pools()
1338 pg_conn.close()
1339 1348
1340 log.info("Backfilled yinyan_song_records platform rows=%d, skipped=%d", total, skipped) 1349 log.info("Backfilled yinyan_song_records platform rows=%d, skipped=%d", total, skipped)
1341 1350
...@@ -1345,6 +1354,10 @@ def _backfill_qq_singers(spider_conn, pg_conn, bucket, base_url, max_batches): ...@@ -1345,6 +1354,10 @@ def _backfill_qq_singers(spider_conn, pg_conn, bucket, base_url, max_batches):
1345 total_ok = total_skip = 0 1354 total_ok = total_skip = 0
1346 pbar = tqdm(desc='backfill-qq-singers') 1355 pbar = tqdm(desc='backfill-qq-singers')
1347 while max_batches is None or batch_index < max_batches: 1356 while max_batches is None or batch_index < max_batches:
1357 # 长任务连接可能断开,每批次开始前刷新
1358 spider_conn = refresh_conn(spider_conn, 'spider')
1359 pg_conn = refresh_conn(pg_conn, 'pg')
1360
1348 with pg_conn.cursor() as pg_cur: 1361 with pg_conn.cursor() as pg_cur:
1349 rows = fetch_qq_songs_missing_singers(pg_cur, BATCH_SIZE) 1362 rows = fetch_qq_songs_missing_singers(pg_cur, BATCH_SIZE)
1350 if not rows: 1363 if not rows:
...@@ -1421,6 +1434,10 @@ def _backfill_kugou_singers(spider_conn, pg_conn, bucket, base_url, max_batches) ...@@ -1421,6 +1434,10 @@ def _backfill_kugou_singers(spider_conn, pg_conn, bucket, base_url, max_batches)
1421 total_ok = total_skip = 0 1434 total_ok = total_skip = 0
1422 pbar = tqdm(desc='backfill-kugou-singers') 1435 pbar = tqdm(desc='backfill-kugou-singers')
1423 while max_batches is None or batch_index < max_batches: 1436 while max_batches is None or batch_index < max_batches:
1437 # 长任务连接可能断开,每批次开始前刷新
1438 spider_conn = refresh_conn(spider_conn, 'spider')
1439 pg_conn = refresh_conn(pg_conn, 'pg')
1440
1424 with pg_conn.cursor() as pg_cur: 1441 with pg_conn.cursor() as pg_cur:
1425 rows = fetch_kugou_songs_missing_singers(pg_cur, BATCH_SIZE) 1442 rows = fetch_kugou_songs_missing_singers(pg_cur, BATCH_SIZE)
1426 if not rows: 1443 if not rows:
...@@ -1491,6 +1508,10 @@ def _backfill_netease_singers(spider_conn, pg_conn, bucket, base_url, max_batche ...@@ -1491,6 +1508,10 @@ def _backfill_netease_singers(spider_conn, pg_conn, bucket, base_url, max_batche
1491 total_ok = total_skip = 0 1508 total_ok = total_skip = 0
1492 pbar = tqdm(desc='backfill-netease-singers') 1509 pbar = tqdm(desc='backfill-netease-singers')
1493 while max_batches is None or batch_index < max_batches: 1510 while max_batches is None or batch_index < max_batches:
1511 # 长任务连接可能断开,每批次开始前刷新
1512 spider_conn = refresh_conn(spider_conn, 'spider')
1513 pg_conn = refresh_conn(pg_conn, 'pg')
1514
1494 with pg_conn.cursor() as pg_cur: 1515 with pg_conn.cursor() as pg_cur:
1495 rows = fetch_netease_songs_missing_singers(pg_cur, BATCH_SIZE) 1516 rows = fetch_netease_songs_missing_singers(pg_cur, BATCH_SIZE)
1496 if not rows: 1517 if not rows:
...@@ -1569,8 +1590,7 @@ def backfill_empty_singers(platforms: list[str], max_batches: int | None = None) ...@@ -1569,8 +1590,7 @@ def backfill_empty_singers(platforms: list[str], max_batches: int | None = None)
1569 if PLATFORM_NETEASE in platforms: 1590 if PLATFORM_NETEASE in platforms:
1570 _backfill_netease_singers(spider_conn, pg_conn, bucket, base_url, max_batches) 1591 _backfill_netease_singers(spider_conn, pg_conn, bucket, base_url, max_batches)
1571 finally: 1592 finally:
1572 spider_conn.close() 1593 close_all_pools()
1573 pg_conn.close()
1574 1594
1575 1595
1576 def _is_target_oss_url(url: str | None, base_url: str) -> bool: 1596 def _is_target_oss_url(url: str | None, base_url: str) -> bool:
...@@ -1589,6 +1609,12 @@ def backfill_qq_invalid_covers(max_batches: int | None = None) -> None: ...@@ -1589,6 +1609,12 @@ def backfill_qq_invalid_covers(max_batches: int | None = None) -> None:
1589 pbar = tqdm(desc='backfill-qq-invalid-covers') 1609 pbar = tqdm(desc='backfill-qq-invalid-covers')
1590 try: 1610 try:
1591 while max_batches is None or batch_index < max_batches: 1611 while max_batches is None or batch_index < max_batches:
1612 # 长任务连接可能断开,每批次开始前刷新
1613 hk_conn = refresh_conn(hk_conn, 'hk_songs')
1614 src_conn = refresh_conn(src_conn, 'source')
1615 spider_conn = refresh_conn(spider_conn, 'spider')
1616 pg_conn = refresh_conn(pg_conn, 'pg')
1617
1592 with pg_conn.cursor() as pg_cur: 1618 with pg_conn.cursor() as pg_cur:
1593 invalid_rows = fetch_qq_songs_with_invalid_covers( 1619 invalid_rows = fetch_qq_songs_with_invalid_covers(
1594 pg_cur, QQ_MISSING_ALBUM_COVER, BATCH_SIZE, 1620 pg_cur, QQ_MISSING_ALBUM_COVER, BATCH_SIZE,
...@@ -1674,10 +1700,7 @@ def backfill_qq_invalid_covers(max_batches: int | None = None) -> None: ...@@ -1674,10 +1700,7 @@ def backfill_qq_invalid_covers(max_batches: int | None = None) -> None:
1674 pbar.update(1) 1700 pbar.update(1)
1675 finally: 1701 finally:
1676 pbar.close() 1702 pbar.close()
1677 hk_conn.close() 1703 close_all_pools()
1678 src_conn.close()
1679 spider_conn.close()
1680 pg_conn.close()
1681 log.info('QQ invalid cover backfill: replaced=%d filtered=%d retry_later=%d', replaced, filtered, retry_later) 1704 log.info('QQ invalid cover backfill: replaced=%d filtered=%d retry_later=%d', replaced, filtered, retry_later)
1682 1705
1683 1706
...@@ -1698,6 +1721,12 @@ def run( ...@@ -1698,6 +1721,12 @@ def run(
1698 batch_index = 0 1721 batch_index = 0
1699 pbar = tqdm(desc='batches') 1722 pbar = tqdm(desc='batches')
1700 while max_batches is None or batch_index < max_batches: 1723 while max_batches is None or batch_index < max_batches:
1724 # 长任务连接可能断开,每批次开始前刷新
1725 hk_conn = refresh_conn(hk_conn, 'hk_songs')
1726 src_conn = refresh_conn(src_conn, 'source')
1727 spider_conn = refresh_conn(spider_conn, 'spider')
1728 pg_conn = refresh_conn(pg_conn, 'pg')
1729
1701 with pg_conn.cursor() as pg_cur: 1730 with pg_conn.cursor() as pg_cur:
1702 pending_records = fetch_pending_yinyan_song_records(pg_cur, BATCH_SIZE) 1731 pending_records = fetch_pending_yinyan_song_records(pg_cur, BATCH_SIZE)
1703 if not pending_records: 1732 if not pending_records:
...@@ -1880,10 +1909,7 @@ def run( ...@@ -1880,10 +1909,7 @@ def run(
1880 pbar.close() 1909 pbar.close()
1881 1910
1882 finally: 1911 finally:
1883 hk_conn.close() 1912 close_all_pools()
1884 src_conn.close()
1885 spider_conn.close()
1886 pg_conn.close()
1887 1913
1888 log.info("Done. OK=%d ERR=%d", total_ok, total_err) 1914 log.info("Done. OK=%d ERR=%d", total_ok, total_err)
1889 if imported: 1915 if imported:
......