velocity-python 0.2.5__py3-none-any.whl → 0.2.6__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.
- velocity/__init__.py +1 -1
- velocity/db/core/async_support.py +3 -13
- velocity/db/core/transaction.py +74 -18
- {velocity_python-0.2.5.dist-info → velocity_python-0.2.6.dist-info}/METADATA +1 -1
- {velocity_python-0.2.5.dist-info → velocity_python-0.2.6.dist-info}/RECORD +9 -9
- {velocity_python-0.2.5.dist-info → velocity_python-0.2.6.dist-info}/WHEEL +1 -1
- {velocity_python-0.2.5.dist-info → velocity_python-0.2.6.dist-info}/entry_points.txt +0 -0
- {velocity_python-0.2.5.dist-info → velocity_python-0.2.6.dist-info}/licenses/LICENSE +0 -0
- {velocity_python-0.2.5.dist-info → velocity_python-0.2.6.dist-info}/top_level.txt +0 -0
velocity/__init__.py
CHANGED
|
@@ -528,19 +528,9 @@ class AsyncTransaction:
|
|
|
528
528
|
|
|
529
529
|
# R12 — Slow query logging
|
|
530
530
|
if _SLOW_QUERY_MS and elapsed_ms > _SLOW_QUERY_MS:
|
|
531
|
-
from velocity.db.core.transaction import
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
_logger.warning(
|
|
535
|
-
"Slow async query: %.1f ms sql=%s",
|
|
536
|
-
elapsed_ms,
|
|
537
|
-
sql_preview,
|
|
538
|
-
extra={
|
|
539
|
-
"query_duration_ms": round(elapsed_ms, 1),
|
|
540
|
-
"sql_preview": sql_preview,
|
|
541
|
-
},
|
|
542
|
-
stack_info=True,
|
|
543
|
-
)
|
|
531
|
+
from velocity.db.core.transaction import _slow_query_log
|
|
532
|
+
|
|
533
|
+
_slow_query_log(_logger, sql, elapsed_ms)
|
|
544
534
|
|
|
545
535
|
# R14 — N+1 detection / R33 — raw-write cache invalidation
|
|
546
536
|
from velocity.db.core.transaction import _classify_sql, _extract_table_name
|
velocity/db/core/transaction.py
CHANGED
|
@@ -30,6 +30,15 @@ _DEFAULT_QUERY_CACHE_SIZE = int(os.environ.get("VELOCITY_QUERY_CACHE_SIZE", "100
|
|
|
30
30
|
# Slow-query threshold in milliseconds (0 = disabled).
|
|
31
31
|
_SLOW_QUERY_MS = int(os.environ.get("VELOCITY_SLOW_QUERY_MS", "500"))
|
|
32
32
|
_SLOW_QUERY_SQL_CHARS = int(os.environ.get("VELOCITY_SLOW_QUERY_SQL_CHARS", "4000"))
|
|
33
|
+
# The slow-query line is for people: this much SQL, the caller's file:line,
|
|
34
|
+
# no traceback unless VELOCITY_SLOW_QUERY_STACK=1 asks for one. The full
|
|
35
|
+
# preview (VELOCITY_SLOW_QUERY_SQL_CHARS) still rides on the record's `extra`.
|
|
36
|
+
_SLOW_QUERY_LINE_CHARS = 200
|
|
37
|
+
_SLOW_QUERY_STACK = os.environ.get("VELOCITY_SLOW_QUERY_STACK", "").strip().lower() in (
|
|
38
|
+
"1",
|
|
39
|
+
"true",
|
|
40
|
+
"yes",
|
|
41
|
+
)
|
|
33
42
|
|
|
34
43
|
# N+1 detection: warn when the same table is SELECTed more than this many
|
|
35
44
|
# times within a single transaction. 0 = disabled. Only active when the
|
|
@@ -174,16 +183,79 @@ def _written_tables_from_sql(sql):
|
|
|
174
183
|
return tags or None
|
|
175
184
|
|
|
176
185
|
|
|
186
|
+
# a run of three or more bind placeholders: "IN (%s, %s, %s, …)"
|
|
187
|
+
_PLACEHOLDER_RUN_RE = re.compile(r"(%s|\?)(?:\s*,\s*\1){2,}")
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _collapse_placeholders(text):
|
|
191
|
+
"""``%s, %s, … (812 of them)`` -> ``%s × 812``."""
|
|
192
|
+
|
|
193
|
+
def collapse(match):
|
|
194
|
+
token = match.group(1)
|
|
195
|
+
return f"{token} × {match.group(0).count(token)}"
|
|
196
|
+
|
|
197
|
+
return _PLACEHOLDER_RUN_RE.sub(collapse, text)
|
|
198
|
+
|
|
199
|
+
|
|
177
200
|
def _summarize_sql(sql, limit=None):
|
|
178
201
|
if not sql:
|
|
179
202
|
return None
|
|
180
|
-
compact = " ".join(str(sql).split())
|
|
203
|
+
compact = _collapse_placeholders(" ".join(str(sql).split()))
|
|
181
204
|
max_chars = _SLOW_QUERY_SQL_CHARS if limit is None else limit
|
|
182
205
|
if max_chars and len(compact) > max_chars:
|
|
183
206
|
return compact[:max_chars] + "... [truncated]"
|
|
184
207
|
return compact
|
|
185
208
|
|
|
186
209
|
|
|
210
|
+
_VELOCITY_DIR = os.path.dirname(
|
|
211
|
+
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _caller_location():
|
|
216
|
+
"""``file.py:line`` of the first stack frame outside velocity (the code
|
|
217
|
+
that ran the query), or ``None``."""
|
|
218
|
+
import traceback
|
|
219
|
+
|
|
220
|
+
for frame in reversed(traceback.extract_stack()[:-1]):
|
|
221
|
+
filename = os.path.abspath(frame.filename)
|
|
222
|
+
if filename.startswith(_VELOCITY_DIR + os.sep) or "<frozen" in frame.filename:
|
|
223
|
+
continue
|
|
224
|
+
return f"{os.path.basename(frame.filename)}:{frame.lineno}"
|
|
225
|
+
return None
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _slow_query_log(logger, sql, elapsed_ms, *, op=None, table=None):
|
|
229
|
+
"""One readable line for a slow query: what, on which table, how long,
|
|
230
|
+
from where, and the start of the SQL (placeholders collapsed). The fields
|
|
231
|
+
for log tooling go in ``extra``; a traceback only with
|
|
232
|
+
VELOCITY_SLOW_QUERY_STACK=1."""
|
|
233
|
+
op = op or _classify_sql(sql)
|
|
234
|
+
table = table if table is not None else _extract_table_name(sql)
|
|
235
|
+
preview = _summarize_sql(sql)
|
|
236
|
+
line = _summarize_sql(sql, limit=_SLOW_QUERY_LINE_CHARS)
|
|
237
|
+
caller = _caller_location()
|
|
238
|
+
took = (
|
|
239
|
+
f"{elapsed_ms / 1000:.1f} s" if elapsed_ms >= 1000 else f"{elapsed_ms:.0f} ms"
|
|
240
|
+
)
|
|
241
|
+
logger.warning(
|
|
242
|
+
"Slow %s%s: %s%s — %s",
|
|
243
|
+
op or "query",
|
|
244
|
+
f" on {table}" if table else "",
|
|
245
|
+
took,
|
|
246
|
+
f" at {caller}" if caller else "",
|
|
247
|
+
line,
|
|
248
|
+
extra={
|
|
249
|
+
"query_duration_ms": round(elapsed_ms, 1),
|
|
250
|
+
"table_name": table,
|
|
251
|
+
"operation": op,
|
|
252
|
+
"sql_preview": preview,
|
|
253
|
+
"caller": caller,
|
|
254
|
+
},
|
|
255
|
+
stack_info=_SLOW_QUERY_STACK,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
187
259
|
def _is_prepare_kw_type_error(error):
|
|
188
260
|
"""True for the TypeError a DB-API driver raises when ``execute()`` has
|
|
189
261
|
no ``prepare`` keyword (``unexpected keyword argument 'prepare'``)."""
|
|
@@ -650,23 +722,7 @@ class Transaction:
|
|
|
650
722
|
|
|
651
723
|
# R12 — Slow query logging.
|
|
652
724
|
if _SLOW_QUERY_MS and elapsed_ms > _SLOW_QUERY_MS:
|
|
653
|
-
|
|
654
|
-
tbl = _extract_table_name(sql)
|
|
655
|
-
sql_preview = _summarize_sql(sql)
|
|
656
|
-
_logger.warning(
|
|
657
|
-
"Slow query (%s): %.1f ms table=%s sql=%s",
|
|
658
|
-
op,
|
|
659
|
-
elapsed_ms,
|
|
660
|
-
tbl,
|
|
661
|
-
sql_preview,
|
|
662
|
-
extra={
|
|
663
|
-
"query_duration_ms": round(elapsed_ms, 1),
|
|
664
|
-
"table_name": tbl,
|
|
665
|
-
"operation": op,
|
|
666
|
-
"sql_preview": sql_preview,
|
|
667
|
-
},
|
|
668
|
-
stack_info=True,
|
|
669
|
-
)
|
|
725
|
+
_slow_query_log(_logger, sql, elapsed_ms)
|
|
670
726
|
|
|
671
727
|
# R14 — N+1 detection (only when debug=True).
|
|
672
728
|
if debug and _N_PLUS_1_THRESHOLD:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
velocity/__init__.py,sha256=
|
|
1
|
+
velocity/__init__.py,sha256=zI7efmr4RDHUvu7Mw3sLtHToFQqy55c8QIuqnJD8ZRo,362
|
|
2
2
|
velocity/cache.py,sha256=pthvw7gDVEXRzkTcQtA5YVf4Q1IxAamWsba4UReT0RE,8933
|
|
3
3
|
velocity/logging.py,sha256=MAsZap1cDSdOBNRc_9ezQxup5dHOS0ARxU9NI_-UK3E,4037
|
|
4
4
|
velocity/aws/__init__.py,sha256=XJJ79gnPlhg3aJij713PB8-vjQOt-BkqxYeGCBOxAu8,3460
|
|
@@ -86,7 +86,7 @@ velocity/db/migrations.py,sha256=ff4kpSrgiBnpdsZ6CHj279_ALf0FE3fy7h2flfUW7A8,222
|
|
|
86
86
|
velocity/db/tunnel.py,sha256=F01GuxRCxtPg7MbG1KYWmQsAVjefzH1jyXDVNCpCJMM,2316
|
|
87
87
|
velocity/db/utils.py,sha256=p-LzHAIexSfMtlr5jGJZR6y0AALCxA347p_tQadEyFw,8973
|
|
88
88
|
velocity/db/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
89
|
-
velocity/db/core/async_support.py,sha256=
|
|
89
|
+
velocity/db/core/async_support.py,sha256=M3Fq-V1sfj9pyeTz8eHXcYp22ThZbISF5-L095T6R8A,26160
|
|
90
90
|
velocity/db/core/column.py,sha256=6RA1Xpk1JY1j4g4yFNWli1C8uZEUyA4NgLmT0oC-VeA,4859
|
|
91
91
|
velocity/db/core/database.py,sha256=6vf0sX8Xt8ynCq7oM09hSFAl_3zsTXv-otOemT0KKGg,3530
|
|
92
92
|
velocity/db/core/decorators.py,sha256=9kkFV3wtjobIbftfiJIZ3q6cNFg7cVE936cmmuWFvCI,19325
|
|
@@ -97,7 +97,7 @@ velocity/db/core/result.py,sha256=Ij0YtKKiUjX2mfG0Kkz8VA0n-X08xyz-vGyYjHoCDwY,15
|
|
|
97
97
|
velocity/db/core/row.py,sha256=A2U5cRW03wFOf15jDr_LUCJe0pNW6RYddG-PK3vYqX4,18777
|
|
98
98
|
velocity/db/core/sequence.py,sha256=dap4Ip0m3htgWV9eCF6ajt-SImnPJJGb8pb8kZVVY4Y,4064
|
|
99
99
|
velocity/db/core/table.py,sha256=DVwCwOaiq-z-dgnazHPwgItx97Sce4SAaFZJGdpklB0,126292
|
|
100
|
-
velocity/db/core/transaction.py,sha256=
|
|
100
|
+
velocity/db/core/transaction.py,sha256=wqmysXnVJvh9vaLlDF4hCux55h21rt7LHzHd6jgEXak,55462
|
|
101
101
|
velocity/db/core/view.py,sha256=lrT1V-NlRioCSfk5pmdXGdCEafdP-t1Pl_EalIrX_cc,5286
|
|
102
102
|
velocity/db/servers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
103
103
|
velocity/db/servers/tablehelper.py,sha256=Is47kUFvFo2kPi7eTe7zj09XM9c7bLNHflr5YlOKBfg,48368
|
|
@@ -223,9 +223,9 @@ velocity/payment/stripe_adapter.py,sha256=6EmVBZZchg2s3Teya0eQGA_8pvSK_teizr036U
|
|
|
223
223
|
velocity/payment/stripe_mirror.py,sha256=6GKJk_PjAgySqFEuxbcuV423OfuGXupVTiGu9QwS5W8,12320
|
|
224
224
|
velocity/testing/__init__.py,sha256=WtYMDHgCLxOq6mlemcOYV5Y9HqF_2QQgSfuAjFRg69Q,731
|
|
225
225
|
velocity/testing/fake_db.py,sha256=phgdulk5owf3noUDpEfI7yFsVcKxRGZ82f2BJPYnaiU,53226
|
|
226
|
-
velocity_python-0.2.
|
|
227
|
-
velocity_python-0.2.
|
|
228
|
-
velocity_python-0.2.
|
|
229
|
-
velocity_python-0.2.
|
|
230
|
-
velocity_python-0.2.
|
|
231
|
-
velocity_python-0.2.
|
|
226
|
+
velocity_python-0.2.6.dist-info/licenses/LICENSE,sha256=aoN245GG8s9oRUU89KNiGTU4_4OtnNmVi4hQeChg6rM,1076
|
|
227
|
+
velocity_python-0.2.6.dist-info/METADATA,sha256=un8yKry0URcUzfnJhcmA7PolBjxeQvmk2WH7Ag0KHo0,17782
|
|
228
|
+
velocity_python-0.2.6.dist-info/WHEEL,sha256=a-udL2m4V_6Yn8b-uKzWb12RjRxwwMo6ohYFM24tpaY,91
|
|
229
|
+
velocity_python-0.2.6.dist-info/entry_points.txt,sha256=nXUKJHkOGQCSPoagdmMNd_qycrle1LVU-GhYknLGM1M,56
|
|
230
|
+
velocity_python-0.2.6.dist-info/top_level.txt,sha256=JW2vJPmodgdgSz7H6yoZvnxF8S3fTMIv-YJWCT1sNW0,9
|
|
231
|
+
velocity_python-0.2.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|