mirror of
https://github.com/hcengineering/platform.git
synced 2026-10-01 05:55:09 +02:00
Fix one second counters
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"changes": [
|
||||
{
|
||||
"packageName": "@hcengineering/server-core",
|
||||
"comment": "Fix one second counters",
|
||||
"type": "patch"
|
||||
}
|
||||
],
|
||||
"packageName": "@hcengineering/server-core"
|
||||
}
|
||||
@@ -84,6 +84,14 @@ describe('OneSecondCountersImpl', () => {
|
||||
expect(entries).toEqual([['op', 0]])
|
||||
})
|
||||
|
||||
it('focused: counter returns to 0 after operation finishes', async () => {
|
||||
// Ensure explicitly that counter returns to 0 after the operation completes
|
||||
await counters.withCounter('focus', 3, async () => 'ok')
|
||||
const entries = Array.from(counters.entries()).sort((a, b) => a[0].localeCompare(b[0]))
|
||||
// 'focus' counter should be 0
|
||||
expect(entries.find((e) => e[0] === 'focus')).toEqual(['focus', 0])
|
||||
})
|
||||
|
||||
it('should decrement counter on operation error', async () => {
|
||||
const operation = jest.fn(async () => {
|
||||
throw new Error('Operation failed')
|
||||
@@ -198,6 +206,20 @@ describe('OneSecondCountersImpl', () => {
|
||||
expect(entries).toEqual([['op', 0]])
|
||||
})
|
||||
|
||||
it('focused: counter returns to 0 after timeout', async () => {
|
||||
// Start an operation that never resolves
|
||||
void counters.withCounter('timen', 4, async (): Promise<void> => {
|
||||
// never resolves
|
||||
})
|
||||
// Counter should be incremented
|
||||
expect(Array.from(counters.entries()).find((e) => e[0] === 'timen')?.[1]).toBe(4)
|
||||
// Fast-forward past the timeout and run check
|
||||
jest.advanceTimersByTime(61 * 1000)
|
||||
counters.check()
|
||||
// After timeout the counter for 'timen' should be back to 0
|
||||
expect(Array.from(counters.entries()).find((e) => e[0] === 'timen')).toEqual(['timen', 0])
|
||||
})
|
||||
|
||||
it('should clean up multiple expired timeouts', async () => {
|
||||
// Create multiple operations
|
||||
void counters.withCounter('op1', 1, async (): Promise<void> => {})
|
||||
|
||||
@@ -431,7 +431,8 @@ export async function calcHashHash (ctx: MeasureContext, domain: Domain, adapter
|
||||
|
||||
export class OneSecondCountersImpl implements OneSecondCounters {
|
||||
private readonly counters = new Map<string, number>()
|
||||
private counterTimeouts: [number, () => void][] = []
|
||||
private readonly counterTimeouts = new Map<number, [number, () => void]>()
|
||||
ids: number = 0
|
||||
|
||||
add (counter: string, count: number): void {
|
||||
this.counters.set(counter, (this.counters.get(counter) ?? 0) + count)
|
||||
@@ -440,7 +441,8 @@ export class OneSecondCountersImpl implements OneSecondCounters {
|
||||
async withCounter<T>(counter: string, count: number, op: () => Promise<T>): Promise<T> {
|
||||
this.add(counter, count)
|
||||
let cleared = false
|
||||
this.counterTimeouts.push([
|
||||
const id = ++this.ids
|
||||
this.counterTimeouts.set(id, [
|
||||
platformNow() + 60 * 1000, // One minute timeout
|
||||
() => {
|
||||
if (!cleared) {
|
||||
@@ -455,6 +457,7 @@ export class OneSecondCountersImpl implements OneSecondCounters {
|
||||
if (!cleared) {
|
||||
this.add(counter, -count)
|
||||
}
|
||||
cleared = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,11 +468,11 @@ export class OneSecondCountersImpl implements OneSecondCounters {
|
||||
check (): void {
|
||||
// Check for timeouts
|
||||
const now = platformNow()
|
||||
for (const [timeout, cb] of this.counterTimeouts) {
|
||||
for (const [k, [timeout, cb]] of [...this.counterTimeouts.entries()]) {
|
||||
if (timeout < now) {
|
||||
cb()
|
||||
this.counterTimeouts.delete(k)
|
||||
}
|
||||
}
|
||||
this.counterTimeouts = this.counterTimeouts.filter((it) => it[0] >= now)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user