Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 Fix missing URL context #3323

Merged
merged 1 commit into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 32 additions & 7 deletions packages/core/src/tools/valueHistory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,41 @@ describe('valueHistory', () => {
expect(valueHistory.find(0 as RelativeTime)).toBeUndefined()
})

it('should clear old values', () => {
const originalTime = performance.now() as RelativeTime
valueHistory.add('foo', originalTime).close(addDuration(originalTime, 10 as Duration))
clock.tick(10)
describe('clearing old values', () => {
it('should clear old values after expiration', () => {
const originalTime = performance.now() as RelativeTime
valueHistory.add('foo', originalTime).close(addDuration(originalTime, 10 as Duration))
clock.tick(10)

expect(valueHistory.find(originalTime)).toBeDefined()
expect(valueHistory.find(originalTime)).toBeDefined()

clock.tick(EXPIRE_DELAY + CLEAR_OLD_VALUES_INTERVAL)
clock.tick(EXPIRE_DELAY + CLEAR_OLD_VALUES_INTERVAL)

expect(valueHistory.find(originalTime)).toBeUndefined()
expect(valueHistory.find(originalTime)).toBeUndefined()
})

it('should clear multiple histories at the same time to avoid race condition', () => {
const delay = 5
const originalTime = performance.now() as RelativeTime

const valueHistory1 = createValueHistory({ expireDelay: EXPIRE_DELAY })
clock.tick(delay)
const valueHistory2 = createValueHistory({ expireDelay: EXPIRE_DELAY })

valueHistory1.add('foo', originalTime).close(addDuration(originalTime, 10 as Duration))
valueHistory2.add('bar', originalTime).close(addDuration(originalTime, 10 as Duration))

expect(valueHistory1.find(originalTime)).toBeDefined()
expect(valueHistory2.find(originalTime)).toBeDefined()

clock.tick(EXPIRE_DELAY + CLEAR_OLD_VALUES_INTERVAL - delay)

expect(valueHistory1.find(originalTime)).toBeUndefined()
expect(valueHistory2.find(originalTime)).toBeUndefined()

valueHistory1.stop()
valueHistory2.stop()
})
})

it('should limit the number of entries', () => {
Expand Down
23 changes: 20 additions & 3 deletions packages/core/src/tools/valueHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ export interface ValueHistory<Value> {
getDeletedEntries: () => RelativeTime[]
}

let cleanupHistoriesInterval: TimeoutId | null = null

const cleanupTasks: Set<() => void> = new Set()

function cleanupHistories() {
cleanupTasks.forEach((task) => task())
}

export function createValueHistory<Value>({
expireDelay,
maxEntries,
Expand All @@ -43,9 +51,12 @@ export function createValueHistory<Value>({
}): ValueHistory<Value> {
let entries: Array<ValueHistoryEntry<Value>> = []
const deletedEntries: RelativeTime[] = []
const clearOldValuesInterval: TimeoutId = setInterval(() => clearOldValues(), CLEAR_OLD_VALUES_INTERVAL)

function clearOldValues() {
if (!cleanupHistoriesInterval) {
cleanupHistoriesInterval = setInterval(() => cleanupHistories(), CLEAR_OLD_VALUES_INTERVAL)
}

const clearExpiredValues = () => {
const oldTimeThreshold = relativeNow() - expireDelay
while (entries.length > 0 && entries[entries.length - 1].endTime < oldTimeThreshold) {
const entry = entries.pop()
Expand All @@ -55,6 +66,8 @@ export function createValueHistory<Value>({
}
}

cleanupTasks.add(clearExpiredValues)

/**
* Add a value to the history associated with a start time. Returns a reference to this newly
* added entry that can be removed or closed.
Expand Down Expand Up @@ -147,7 +160,11 @@ export function createValueHistory<Value>({
* Stop internal garbage collection of past entries.
*/
function stop() {
clearInterval(clearOldValuesInterval)
cleanupTasks.delete(clearExpiredValues)
if (cleanupTasks.size === 0 && cleanupHistoriesInterval) {
clearInterval(cleanupHistoriesInterval)
cleanupHistoriesInterval = null
}
}

return { add, find, closeActive, findAll, reset, stop, getAllEntries, getDeletedEntries }
Expand Down