mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-10-19 15:44:27 +00:00
Merge pull request #2142 from DYNOSuprovo/fix/workspace-changes-abort-listener
Some checks are pending
Build and Push Docker Images / image_version (push) Waiting to run
Build and Push Docker Images / build (./docker/sandbox, cpu, ./docker/sandbox/Dockerfile, sandbox, surfsense-sandbox, ubuntu-24.04-arm, linux/arm64, arm64, , false, cpu) (push) Blocked by required conditions
Build and Push Docker Images / build (./docker/sandbox, cpu, ./docker/sandbox/Dockerfile, sandbox, surfsense-sandbox, ubuntu-latest, linux/amd64, amd64, , false, cpu) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, , production, false, cpu) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, , production, false, cpu) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda126, production, true, cuda126) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda126, production, true, cuda126) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda, production, true, cuda) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda, production, true, cuda) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64, , runner, false, cpu) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64, , runner, false, cpu) (push) Blocked by required conditions
Build and Push Docker Images / verify_digests (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (backend, surfsense-backend, , cpu) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda, cuda) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda126, cuda126) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (sandbox, surfsense-sandbox, , cpu) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (web, surfsense-web, , cpu) (push) Blocked by required conditions
Some checks are pending
Build and Push Docker Images / image_version (push) Waiting to run
Build and Push Docker Images / build (./docker/sandbox, cpu, ./docker/sandbox/Dockerfile, sandbox, surfsense-sandbox, ubuntu-24.04-arm, linux/arm64, arm64, , false, cpu) (push) Blocked by required conditions
Build and Push Docker Images / build (./docker/sandbox, cpu, ./docker/sandbox/Dockerfile, sandbox, surfsense-sandbox, ubuntu-latest, linux/amd64, amd64, , false, cpu) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, , production, false, cpu) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, , production, false, cpu) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda126, production, true, cuda126) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda126, production, true, cuda126) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda, production, true, cuda) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda, production, true, cuda) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64, , runner, false, cpu) (push) Blocked by required conditions
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64, , runner, false, cpu) (push) Blocked by required conditions
Build and Push Docker Images / verify_digests (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (backend, surfsense-backend, , cpu) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda, cuda) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda126, cuda126) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (sandbox, surfsense-sandbox, , cpu) (push) Blocked by required conditions
Build and Push Docker Images / create_manifest (web, surfsense-web, , cpu) (push) Blocked by required conditions
fix(workspaces): clean up abort listener on retry timer and return early when aborted
This commit is contained in:
commit
1ce2742438
3 changed files with 70 additions and 10 deletions
|
|
@ -125,4 +125,4 @@ The rules that keep the processes out of each other's way:
|
|||
## Known gaps
|
||||
|
||||
- The sources and Studio lists reload themselves on a workspace event rather than through TanStack Query: they are component state, so nothing else that shows a document or an artifact is invalidated by the same event.
|
||||
- The retry `wait()` in [`workspace-changes.ts`](../../surfsense_local/frontend/src/features/workspaces/workspace-changes.ts) never removes its `abort` listener when its timer fires, so a stream that keeps dropping, as it does while the API is down, adds one listener to the workspace's signal per attempt, about 360 an hour, until the last list leaves. It also waits out its timer when the signal is already aborted, so a stream torn down between attempts takes up to 10 seconds to stop.
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ function listen(workspaceId: number, kind: "documents" | "artifacts") {
|
|||
afterEach(() => {
|
||||
for (const unsubscribe of unsubscribes.splice(0)) unsubscribe()
|
||||
vi.unstubAllGlobals()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
describe("workspace changes", () => {
|
||||
|
|
@ -98,4 +99,62 @@ describe("workspace changes", () => {
|
|||
{ timeout: 4000 }
|
||||
)
|
||||
})
|
||||
|
||||
it("removes every abort listener wait() adds across dropped streams", async () => {
|
||||
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] })
|
||||
const first = eventStream()
|
||||
const second = eventStream()
|
||||
const third = eventStream()
|
||||
const fetchMock = apiStreaming([
|
||||
first.response,
|
||||
second.response,
|
||||
third.response,
|
||||
])
|
||||
|
||||
listen(1, "documents")
|
||||
await vi.waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1))
|
||||
|
||||
const signal = fetchMock.mock.calls[0][1]?.signal as AbortSignal
|
||||
const addListenerSpy = vi.spyOn(signal, "addEventListener")
|
||||
const removeListenerSpy = vi.spyOn(signal, "removeEventListener")
|
||||
|
||||
first.drop()
|
||||
await vi.advanceTimersByTimeAsync(1_000)
|
||||
await vi.waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(2))
|
||||
|
||||
second.drop()
|
||||
await vi.advanceTimersByTimeAsync(2_000)
|
||||
await vi.waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(3))
|
||||
|
||||
const abortAdds = addListenerSpy.mock.calls.filter(([event]) => event === "abort")
|
||||
const abortRemoves = removeListenerSpy.mock.calls.filter(
|
||||
([event]) => event === "abort"
|
||||
)
|
||||
|
||||
expect(abortAdds.length).toBeGreaterThanOrEqual(2)
|
||||
expect(abortRemoves.length).toBe(abortAdds.length)
|
||||
})
|
||||
|
||||
it("tears the subscription down during a retry wait without waiting out the timer", async () => {
|
||||
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] })
|
||||
const stream = eventStream()
|
||||
const fetchMock = apiStreaming([stream.response])
|
||||
|
||||
const unsubscribe = subscribeToWorkspaceChanges(1, "documents", vi.fn())
|
||||
unsubscribes.push(unsubscribe)
|
||||
await vi.waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1))
|
||||
|
||||
const signal = fetchMock.mock.calls[0][1]?.signal as AbortSignal
|
||||
|
||||
stream.drop()
|
||||
await vi.waitFor(() => expect(vi.getTimerCount()).toBe(1))
|
||||
|
||||
unsubscribe()
|
||||
|
||||
expect(signal.aborted).toBe(true)
|
||||
expect(vi.getTimerCount()).toBe(0)
|
||||
|
||||
await vi.advanceTimersByTimeAsync(10_000)
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -16,16 +16,17 @@ const LAST_RETRY_MS = 10_000
|
|||
const subscriptions = new Map<number, Subscription>()
|
||||
|
||||
function wait(milliseconds: number, signal: AbortSignal) {
|
||||
if (signal.aborted) return Promise.resolve()
|
||||
return new Promise<void>((resolve) => {
|
||||
const timeout = window.setTimeout(resolve, milliseconds)
|
||||
signal.addEventListener(
|
||||
"abort",
|
||||
() => {
|
||||
window.clearTimeout(timeout)
|
||||
resolve()
|
||||
},
|
||||
{ once: true }
|
||||
)
|
||||
const onAbort = () => {
|
||||
window.clearTimeout(timeout)
|
||||
resolve()
|
||||
}
|
||||
const timeout = window.setTimeout(() => {
|
||||
signal.removeEventListener("abort", onAbort)
|
||||
resolve()
|
||||
}, milliseconds)
|
||||
signal.addEventListener("abort", onAbort, { once: true })
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue