stabilize live api memory and add an options pipeline explainer #8
1 changed files with 42 additions and 8 deletions
|
|
@ -614,6 +614,9 @@ export type LiveStateStatsSnapshot = {
|
||||||
const isLiveStateConfig = (value: GenericLiveLimits | LiveStateConfig): value is LiveStateConfig =>
|
const isLiveStateConfig = (value: GenericLiveLimits | LiveStateConfig): value is LiveStateConfig =>
|
||||||
"limits" in value;
|
"limits" in value;
|
||||||
|
|
||||||
|
const isRedisClientClosedError = (error: unknown): boolean =>
|
||||||
|
error instanceof Error && error.message.toLowerCase().includes("client is closed");
|
||||||
|
|
||||||
const clampConfiguredLimit = (channel: LiveGenericChannel, value: number): number =>
|
const clampConfiguredLimit = (channel: LiveGenericChannel, value: number): number =>
|
||||||
Math.max(MIN_GENERIC_LIMIT, Math.min(LIVE_GENERIC_LIMIT_CAPS[channel], Math.floor(value)));
|
Math.max(MIN_GENERIC_LIMIT, Math.min(LIVE_GENERIC_LIMIT_CAPS[channel], Math.floor(value)));
|
||||||
|
|
||||||
|
|
@ -656,6 +659,7 @@ export class LiveStateManager {
|
||||||
private readonly overlayAccess = new Map<string, number>();
|
private readonly overlayAccess = new Map<string, number>();
|
||||||
private readonly pendingRedisWrites = new Map<string, BufferedRedisWrite>();
|
private readonly pendingRedisWrites = new Map<string, BufferedRedisWrite>();
|
||||||
private readonly redisFlushTimer: ReturnType<typeof setInterval> | null;
|
private readonly redisFlushTimer: ReturnType<typeof setInterval> | null;
|
||||||
|
private redisFlushInFlight: Promise<void> | null = null;
|
||||||
private readonly stats = {
|
private readonly stats = {
|
||||||
genericHydrateFromRedis: 0,
|
genericHydrateFromRedis: 0,
|
||||||
genericHydrateFromClickHouse: 0,
|
genericHydrateFromClickHouse: 0,
|
||||||
|
|
@ -723,6 +727,22 @@ export class LiveStateManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
async flushRedisWrites(): Promise<void> {
|
async flushRedisWrites(): Promise<void> {
|
||||||
|
if (this.redisFlushInFlight) {
|
||||||
|
return this.redisFlushInFlight;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.redisFlushInFlight = this.flushRedisWritesInternal();
|
||||||
|
try {
|
||||||
|
await this.redisFlushInFlight;
|
||||||
|
} finally {
|
||||||
|
this.redisFlushInFlight = null;
|
||||||
|
if (this.pendingRedisWrites.size > 0 && this.redis?.isOpen) {
|
||||||
|
void this.flushRedisWrites();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async flushRedisWritesInternal(): Promise<void> {
|
||||||
if (!this.redis?.isOpen) {
|
if (!this.redis?.isOpen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -732,20 +752,34 @@ export class LiveStateManager {
|
||||||
|
|
||||||
for (const write of writes) {
|
for (const write of writes) {
|
||||||
if (write.mode === "rewrite") {
|
if (write.mode === "rewrite") {
|
||||||
await this.persistList(write.listKey, write.cursorField, write.items, write.limit, write.cursor);
|
try {
|
||||||
|
await this.persistList(write.listKey, write.cursorField, write.items, write.limit, write.cursor);
|
||||||
|
} catch (error) {
|
||||||
|
if (isRedisClientClosedError(error)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
this.stats.redisFlushItems += write.items.length;
|
this.stats.redisFlushItems += write.items.length;
|
||||||
this.stats.redisFlushPayloadBytes += write.items.reduce(
|
this.stats.redisFlushPayloadBytes += write.items.reduce(
|
||||||
(total, item) => total + JSON.stringify(item).length,
|
(total, item) => total + JSON.stringify(item).length,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await this.persistListAppend(
|
try {
|
||||||
write.listKey,
|
await this.persistListAppend(
|
||||||
write.cursorField,
|
write.listKey,
|
||||||
write.payloads,
|
write.cursorField,
|
||||||
write.limit,
|
write.payloads,
|
||||||
write.cursor
|
write.limit,
|
||||||
);
|
write.cursor
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
if (isRedisClientClosedError(error)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
this.stats.redisFlushItems += write.payloads.length;
|
this.stats.redisFlushItems += write.payloads.length;
|
||||||
this.stats.redisFlushPayloadBytes += write.payloads.reduce((total, payload) => total + payload.length, 0);
|
this.stats.redisFlushPayloadBytes += write.payloads.reduce((total, payload) => total + payload.length, 0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue