Use jest.useFakeTimers
This commit is contained in:
parent
bd89633e61
commit
fb72b77ac1
|
@ -101,20 +101,20 @@ function getLockName(opts: LockOptions) {
|
|||
return name
|
||||
}
|
||||
|
||||
export const autoExtendPollingMs = Duration.fromSeconds(10).toMs()
|
||||
|
||||
export async function doWithLock<T>(
|
||||
opts: LockOptions,
|
||||
task: () => Promise<T>
|
||||
): Promise<RedlockExecution<T>> {
|
||||
const redlock = await getClient(opts.type, opts.customOptions)
|
||||
let lock: Redlock.Lock | undefined
|
||||
let timeout
|
||||
let timeout: NodeJS.Timeout | undefined
|
||||
try {
|
||||
const name = getLockName(opts)
|
||||
|
||||
const ttl =
|
||||
opts.type === LockType.AUTO_EXTEND
|
||||
? Duration.fromSeconds(10).toMs()
|
||||
: opts.ttl
|
||||
opts.type === LockType.AUTO_EXTEND ? autoExtendPollingMs : opts.ttl
|
||||
|
||||
// create the lock
|
||||
lock = await redlock.lock(name, ttl)
|
||||
|
@ -123,21 +123,9 @@ export async function doWithLock<T>(
|
|||
// We keep extending the lock while the task is running
|
||||
const extendInIntervals = (): void => {
|
||||
timeout = setTimeout(async () => {
|
||||
let isExpired = false
|
||||
try {
|
||||
lock = await lock!.extend(ttl)
|
||||
} catch (err: any) {
|
||||
isExpired = err.message.includes("Cannot extend lock on resource")
|
||||
if (isExpired) {
|
||||
console.error("The lock expired", { name })
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
lock = await lock!.extend(ttl, () => opts.onExtend && opts.onExtend())
|
||||
|
||||
if (!isExpired) {
|
||||
extendInIntervals()
|
||||
}
|
||||
}, ttl / 2)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import { LockName, LockType, LockOptions } from "@budibase/types"
|
||||
import tk from "timekeeper"
|
||||
import { doWithLock } from "../redlockImpl"
|
||||
import { autoExtendPollingMs, doWithLock } from "../redlockImpl"
|
||||
import { DBTestConfiguration, generator } from "../../../tests"
|
||||
|
||||
tk.reset()
|
||||
|
||||
describe("redlockImpl", () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers()
|
||||
})
|
||||
|
||||
describe("doWithLock", () => {
|
||||
const config = new DBTestConfiguration()
|
||||
const lockTtl = 50
|
||||
const lockTtl = autoExtendPollingMs
|
||||
|
||||
function runLockWithExecutionTime({
|
||||
opts,
|
||||
|
@ -21,7 +22,10 @@ describe("redlockImpl", () => {
|
|||
}) {
|
||||
return config.doInTenant(() =>
|
||||
doWithLock(opts, async () => {
|
||||
await new Promise<void>(r => setTimeout(() => r(), executionTimeMs))
|
||||
const interval = lockTtl / 10
|
||||
for (let i = executionTimeMs; i > 0; i -= interval) {
|
||||
await jest.advanceTimersByTimeAsync(interval)
|
||||
}
|
||||
return task()
|
||||
})
|
||||
)
|
||||
|
@ -33,7 +37,7 @@ describe("redlockImpl", () => {
|
|||
const expectedResult = generator.guid()
|
||||
const mockTask = jest.fn().mockResolvedValue(expectedResult)
|
||||
|
||||
const opts = {
|
||||
const opts: LockOptions = {
|
||||
name: LockName.PERSIST_WRITETHROUGH,
|
||||
type: lockType,
|
||||
ttl: lockTtl,
|
||||
|
@ -54,22 +58,24 @@ describe("redlockImpl", () => {
|
|||
it("should extend when type is autoextend", async () => {
|
||||
const expectedResult = generator.guid()
|
||||
const mockTask = jest.fn().mockResolvedValue(expectedResult)
|
||||
const mockOnExtend = jest.fn()
|
||||
|
||||
const opts = {
|
||||
const opts: LockOptions = {
|
||||
name: LockName.PERSIST_WRITETHROUGH,
|
||||
type: LockType.AUTO_EXTEND,
|
||||
ttl: lockTtl,
|
||||
onExtend: mockOnExtend,
|
||||
}
|
||||
|
||||
const result = await runLockWithExecutionTime({
|
||||
opts,
|
||||
task: mockTask,
|
||||
executionTimeMs: lockTtl * 3,
|
||||
executionTimeMs: lockTtl * 2.5,
|
||||
})
|
||||
|
||||
expect(result.executed).toBe(true)
|
||||
expect(result.executed && result.result).toBe(expectedResult)
|
||||
expect(mockTask).toHaveBeenCalledTimes(1)
|
||||
expect(mockOnExtend).toHaveBeenCalledTimes(5)
|
||||
})
|
||||
|
||||
it.each(Object.values(LockType).filter(t => t !== LockType.AUTO_EXTEND))(
|
||||
|
@ -77,7 +83,7 @@ describe("redlockImpl", () => {
|
|||
async (lockType: LockType) => {
|
||||
const mockTask = jest.fn().mockResolvedValue("mockResult")
|
||||
|
||||
const opts = {
|
||||
const opts: LockOptions = {
|
||||
name: LockName.PERSIST_WRITETHROUGH,
|
||||
type: lockType,
|
||||
ttl: lockTtl,
|
||||
|
|
|
@ -54,5 +54,6 @@ export type LockOptions = {
|
|||
}
|
||||
| {
|
||||
type: LockType.AUTO_EXTEND
|
||||
onExtend?: () => void
|
||||
}
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue