mirror of
https://fast.feibisi.com/https://github.com/parcelvoy/platform.git
synced 2025-09-10 13:56:39 +08:00
feat: add asynchronous chunker for faster ingest
This commit is contained in:
parent
d0577b1636
commit
a976e9bd3a
2 changed files with 52 additions and 3 deletions
|
@ -12,7 +12,7 @@ import { PageParams } from '../core/searchParams'
|
||||||
import { allLists } from '../lists/ListService'
|
import { allLists } from '../lists/ListService'
|
||||||
import { allTemplates, duplicateTemplate, screenshotHtml, templateInUserLocale, validateTemplates } from '../render/TemplateService'
|
import { allTemplates, duplicateTemplate, screenshotHtml, templateInUserLocale, validateTemplates } from '../render/TemplateService'
|
||||||
import { getSubscription, getUserSubscriptionState } from '../subscriptions/SubscriptionService'
|
import { getSubscription, getUserSubscriptionState } from '../subscriptions/SubscriptionService'
|
||||||
import { chunk, Chunker, pick, shallowEqual } from '../utilities'
|
import { AsyncChunker, chunk, pick, shallowEqual } from '../utilities'
|
||||||
import { getProvider } from '../providers/ProviderRepository'
|
import { getProvider } from '../providers/ProviderRepository'
|
||||||
import { createTagSubquery, getTags, setTags } from '../tags/TagService'
|
import { createTagSubquery, getTags, setTags } from '../tags/TagService'
|
||||||
import { getProject } from '../projects/ProjectService'
|
import { getProject } from '../projects/ProjectService'
|
||||||
|
@ -308,7 +308,7 @@ export const generateSendList = async (campaign: SentCampaign) => {
|
||||||
http_headers_progress_interval_ms: '110000', // 110 seconds
|
http_headers_progress_interval_ms: '110000', // 110 seconds
|
||||||
})
|
})
|
||||||
|
|
||||||
const chunker = new Chunker<CampaignSendParams>(async items => {
|
const chunker = new AsyncChunker<CampaignSendParams>(async items => {
|
||||||
await App.main.db.transaction(async (trx) => {
|
await App.main.db.transaction(async (trx) => {
|
||||||
await CampaignSend.query(trx)
|
await CampaignSend.query(trx)
|
||||||
.insert(items)
|
.insert(items)
|
||||||
|
@ -331,6 +331,8 @@ export const generateSendList = async (campaign: SentCampaign) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Most of the work will happen here since ClickHouse should return
|
||||||
|
// records fairly quickly
|
||||||
await chunker.flush()
|
await chunker.flush()
|
||||||
|
|
||||||
logger.info({ campaignId: campaign.id, elapsed: Date.now() - now }, 'campaign:generate:progress:finished')
|
logger.info({ campaignId: campaign.id, elapsed: Date.now() - now }, 'campaign:generate:progress:finished')
|
||||||
|
|
|
@ -279,7 +279,7 @@ export class Chunker<T> {
|
||||||
#items: T[] = []
|
#items: T[] = []
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private callback: (batch: T[]) => Promise<void>,
|
private callback: ChunkCallback<T>,
|
||||||
private size: number,
|
private size: number,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@ -300,6 +300,53 @@ export class Chunker<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class AsyncChunker<T> {
|
||||||
|
|
||||||
|
private buffer: T[] = []
|
||||||
|
private isProcessing = false
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly callback: ChunkCallback<T>,
|
||||||
|
private readonly batchSize: number,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
add(item: T): void {
|
||||||
|
this.buffer.push(item)
|
||||||
|
|
||||||
|
if (this.buffer.length >= this.batchSize) {
|
||||||
|
this.triggerProcessing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private triggerProcessing(): void {
|
||||||
|
if (this.isProcessing) return
|
||||||
|
|
||||||
|
this.isProcessing = true
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
|
const batch = this.buffer.splice(0, this.batchSize)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.callback(batch)
|
||||||
|
} finally {
|
||||||
|
this.isProcessing = false
|
||||||
|
|
||||||
|
if (this.buffer.length >= this.batchSize) {
|
||||||
|
this.triggerProcessing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
async flush(): Promise<void> {
|
||||||
|
this.isProcessing = true
|
||||||
|
while (this.buffer.length > 0) {
|
||||||
|
const batch = this.buffer.splice(0, this.batchSize)
|
||||||
|
await this.callback(batch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function visit<T>(item: T, children: (item: T) => undefined | T[], callback: (item: T) => void) {
|
export function visit<T>(item: T, children: (item: T) => undefined | T[], callback: (item: T) => void) {
|
||||||
callback(item)
|
callback(item)
|
||||||
const items = children(item)
|
const items = children(item)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue