Skip to content
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
1 change: 1 addition & 0 deletions apps/content/docs/helpers/publisher.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ export class PublisherDO extends PublisherDurableObject {
super(ctx, env, {
resume: {
retentionSeconds: 60 * 2, // Retain events for 2 minutes to support resume
cleanupIntervalSeconds: 12 * 60 * 60, // Interval for inactivity checks; if inactive, the DO is cleaned up (default: 12 hours)
},
})
}
Expand Down
8 changes: 4 additions & 4 deletions packages/publisher-durable-object/src/resume-storage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,22 +293,22 @@ describe('resumeStorage', () => {
expect(ctx.storage.setAlarm).toHaveBeenCalledOnce()
})

it('uses custom inactiveDataRetentionTime for alarm scheduling', async () => {
it('uses custom cleanupIntervalSeconds for alarm scheduling', async () => {
const ctx = createDurableObjectState()
const storage = new ResumeStorage(ctx, {
retentionSeconds: 60,
inactiveDataRetentionTime: 120,
cleanupIntervalSeconds: 120,
})

await storage.store(JSON.stringify({ data: 'test' }))

// Alarm should be scheduled at retentionSeconds + inactiveDataRetentionTime
// Alarm should be scheduled at cleanupIntervalSeconds
expect(ctx.storage.setAlarm).toHaveBeenCalledWith(
expect.any(Number),
)

const alarmTime = ctx.storage.setAlarm.mock.calls[0]![0]
const expectedDelay = (60 + 120) * 1000
const expectedDelay = 120 * 1000
expect(alarmTime).toBeGreaterThanOrEqual(Date.now() + expectedDelay - 1000)
expect(alarmTime).toBeLessThanOrEqual(Date.now() + expectedDelay + 1000)
})
Expand Down
17 changes: 9 additions & 8 deletions packages/publisher-durable-object/src/resume-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ export interface ResumeStorageOptions {
retentionSeconds?: number

/**
* How long (in seconds) of inactivity before auto-deleting the durable object's data.
* Inactivity means no active WebSocket connections and no events within the retention period.
* Interval (in seconds) between cleanup checks for the Durable Object.
*
* The alarm is scheduled at `retentionSeconds + inactiveDataRetentionTime`.
* At each interval, verify whether the Durable Object is inactive
* (no active WebSocket connections and no stored events). If inactive, all
* data is deleted to free resources; otherwise, another check is scheduled.
*
* @default 6 * 60 * 60 (6 hours)
* @default 12 * 60 * 60 (12 hours)
*/
inactiveDataRetentionTime?: number
cleanupIntervalSeconds?: number

/**
* Prefix for the resume storage table schema.
Expand All @@ -38,7 +39,7 @@ export interface ResumeStorageOptions {

export class ResumeStorage {
private readonly retentionSeconds: number
private readonly inactiveDataRetentionTime: number
private readonly cleanupIntervalSeconds: number
private readonly schemaPrefix: string

private isInitedSchema = false
Expand All @@ -54,7 +55,7 @@ export class ResumeStorage {
options: ResumeStorageOptions = {},
) {
this.retentionSeconds = options.retentionSeconds ?? 0
this.inactiveDataRetentionTime = options.inactiveDataRetentionTime ?? 6 * 60 * 60
this.cleanupIntervalSeconds = options.cleanupIntervalSeconds ?? 12 * 60 * 60
this.schemaPrefix = options.schemaPrefix ?? 'orpc:publisher:resume:'
}

Expand Down Expand Up @@ -230,6 +231,6 @@ export class ResumeStorage {
}

private scheduleAlarm(): Promise<void> {
return this.ctx.storage.setAlarm(Date.now() + (this.retentionSeconds + this.inactiveDataRetentionTime) * 1000)
return this.ctx.storage.setAlarm(Date.now() + this.cleanupIntervalSeconds * 1000)
}
}
Loading