Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5db9674490 |
+30
-6
@@ -2567,14 +2567,25 @@ async def get_logs(
|
||||
if account_id is not None:
|
||||
await get_owned_account(db, user, account_id)
|
||||
limit = max(1, min(int(limit or 50), 500))
|
||||
stmt = (
|
||||
# Fetch only primary keys while MySQL performs the cross-account sort.
|
||||
# Selecting the full ORM row here includes MEDIUMTEXT/TEXT columns, which
|
||||
# makes MySQL 5.7 materialize a huge on-disk temporary table for non-admin
|
||||
# users. Under polling load that grew ibtmp1 by tens of gigabytes.
|
||||
id_stmt = (
|
||||
logs_for_user(user, account_id)
|
||||
.with_only_columns(MessageLog.id)
|
||||
.order_by(MessageLog.created_at.desc())
|
||||
.offset(max(0, int(offset or 0)))
|
||||
.limit(limit)
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
return result.scalars().all()
|
||||
ordered_ids = list((await db.execute(id_stmt)).scalars().all())
|
||||
if not ordered_ids:
|
||||
return []
|
||||
rows = (
|
||||
await db.execute(select(MessageLog).where(MessageLog.id.in_(ordered_ids)))
|
||||
).scalars().all()
|
||||
rows_by_id = {row.id: row for row in rows}
|
||||
return [rows_by_id[row_id] for row_id in ordered_ids if row_id in rows_by_id]
|
||||
|
||||
|
||||
@app.get("/api/received-messages", response_model=List[ReceivedMessageLogResponse])
|
||||
@@ -2588,13 +2599,26 @@ async def get_received_messages(
|
||||
if account_id is not None:
|
||||
await get_owned_account(db, user, account_id)
|
||||
limit = max(1, min(int(limit or 100), 500))
|
||||
stmt = (
|
||||
# Keep the global sort narrow for the same reason as /api/logs. raw_content
|
||||
# can be large and must only be loaded after LIMIT has selected the IDs.
|
||||
id_stmt = (
|
||||
received_logs_for_user(user, account_id)
|
||||
.with_only_columns(ReceivedMessageLog.id)
|
||||
.order_by(ReceivedMessageLog.created_at.desc())
|
||||
.limit(limit)
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
return result.scalars().all()
|
||||
ordered_ids = list((await db.execute(id_stmt)).scalars().all())
|
||||
if not ordered_ids:
|
||||
return []
|
||||
rows = (
|
||||
await db.execute(
|
||||
select(ReceivedMessageLog).where(
|
||||
ReceivedMessageLog.id.in_(ordered_ids)
|
||||
)
|
||||
)
|
||||
).scalars().all()
|
||||
rows_by_id = {row.id: row for row in rows}
|
||||
return [rows_by_id[row_id] for row_id in ordered_ids if row_id in rows_by_id]
|
||||
|
||||
|
||||
@app.get("/api/system-logs", response_model=List[SystemLogResponse])
|
||||
|
||||
@@ -126,6 +126,12 @@ def migrate_accounts_table(conn) -> None:
|
||||
"douyin_uid",
|
||||
{"default": "ALTER TABLE accounts ADD COLUMN douyin_uid VARCHAR(64)"},
|
||||
)
|
||||
add_index_if_missing(
|
||||
conn,
|
||||
"accounts",
|
||||
"ix_accounts_owner_id",
|
||||
("owner_id",),
|
||||
)
|
||||
|
||||
|
||||
def migrate_account_videos_table(conn) -> None:
|
||||
|
||||
@@ -39,9 +39,23 @@ const LOGS_PAGE_SIZE = 20
|
||||
const hasMoreLogs = ref(true)
|
||||
const loadingMore = ref(false)
|
||||
let pollTimer = null
|
||||
let pollStopped = false
|
||||
// 上次日志数据的签名;轮询时数据没变化就跳过赋值,避免无谓的重算与重渲染
|
||||
let lastLogsSignature = ''
|
||||
|
||||
// Use a self-scheduling timeout instead of setInterval. A slow request must
|
||||
// finish before the next poll is scheduled, otherwise overlapping requests can
|
||||
// exhaust the database pool and amplify one slow query into dozens.
|
||||
const scheduleLogPoll = () => {
|
||||
if (pollStopped) return
|
||||
pollTimer = setTimeout(async () => {
|
||||
if (!document.hidden) {
|
||||
await fetchLogs(true)
|
||||
}
|
||||
scheduleLogPoll()
|
||||
}, POLL_INTERVAL_MS)
|
||||
}
|
||||
|
||||
const dedupeMessages = (messages) => {
|
||||
const map = new Map()
|
||||
for (const item of messages) {
|
||||
@@ -593,10 +607,8 @@ onMounted(async () => {
|
||||
}
|
||||
await fetchAccounts()
|
||||
await fetchLogs()
|
||||
pollTimer = setInterval(() => {
|
||||
if (document.hidden) return
|
||||
fetchLogs(true)
|
||||
}, POLL_INTERVAL_MS)
|
||||
pollStopped = false
|
||||
scheduleLogPoll()
|
||||
})
|
||||
|
||||
watch(
|
||||
@@ -613,8 +625,9 @@ watch(
|
||||
)
|
||||
|
||||
onUnmounted(() => {
|
||||
pollStopped = true
|
||||
if (pollTimer) {
|
||||
clearInterval(pollTimer)
|
||||
clearTimeout(pollTimer)
|
||||
pollTimer = null
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user