Get rid of Worker, use TaskQueue instead

This commit is contained in:
Jon Staab
2025-03-31 15:55:10 -07:00
parent db588495e3
commit 04816f8377
5 changed files with 82 additions and 433 deletions
+81 -78
View File
@@ -1,5 +1,5 @@
import {Writable, Readable, writable, derived, get} from "svelte/store"
import {Deferred, Worker, dissoc, identity, uniq, defer, sleep, assoc} from "@welshman/lib"
import {Deferred, TaskQueue, dissoc, identity, uniq, defer, sleep, assoc} from "@welshman/lib"
import {stamp, own, hash} from "@welshman/signer"
import {
TrustedEvent,
@@ -129,7 +129,7 @@ export const thunks = writable<Record<string, Thunk | MergedThunk>>({})
export const publishThunk = (request: ThunkRequest) => {
const thunk = makeThunk(request)
thunkWorker.push(thunk)
thunkQueue.push(thunk)
repository.publish(thunk.event)
@@ -147,7 +147,7 @@ export const publishThunks = (requests: ThunkRequest[]) => {
const mergedThunk = mergeThunks(newThunks)
for (const thunk of newThunks) {
thunkWorker.push(thunk)
thunkQueue.push(thunk)
repository.publish(thunk.event)
@@ -167,96 +167,99 @@ export const abortThunk = (thunk: Thunk) => {
repository.removeEvent(thunk.event.id)
}
export const thunkWorker = new Worker<Thunk>()
export const thunkQueue = new TaskQueue<Thunk>({
batchSize: 50,
processItem: (thunk: Thunk) => {
let event = thunk.event
thunkWorker.addGlobalHandler((thunk: Thunk) => {
let event = thunk.event
// Handle abort immediately if possible
if (thunk.controller.signal.aborted) return
// Handle abort immediately if possible
if (thunk.controller.signal.aborted) return
// If we were given a wrapped event, make sure to publish the wrapper, not the rumor
if (isUnwrappedEvent(event)) {
event = event.wrap
}
// If we were given a wrapped event, make sure to publish the wrapper, not the rumor
if (isUnwrappedEvent(event)) {
event = event.wrap
}
// Avoid making this function async so multiple publishes can run concurrently
Promise.resolve().then(async () => {
const fail = (message: string) => {
const status: ThunkStatusByUrl = {}
// Avoid making this function async so multiple publishes can run concurrently
Promise.resolve().then(async () => {
const fail = (message: string) => {
const status: ThunkStatusByUrl = {}
for (const url of thunk.request.relays) {
status[url] = {status: PublishStatus.Failure, message}
}
for (const url of thunk.request.relays) {
status[url] = {status: PublishStatus.Failure, message}
thunk.status.set(status)
}
thunk.status.set(status)
}
// If the event was already signed, leave it alone. Otherwise, sign it now. This is to
// decrease apparent latency in the UI that results from waiting for remote signers
if (!isSignedEvent(event)) {
const signer = getSigner(getSession(event.pubkey))
// If the event was already signed, leave it alone. Otherwise, sign it now. This is to
// decrease apparent latency in the UI that results from waiting for remote signers
if (!isSignedEvent(event)) {
const signer = getSigner(getSession(event.pubkey))
if (!signer) {
return fail(`No signer found for ${event.pubkey}`)
}
if (!signer) {
return fail(`No signer found for ${event.pubkey}`)
try {
event = await signer.sign(event)
} catch (e: any) {
return fail(String(e.error || e))
}
}
try {
event = await signer.sign(event)
} catch (e: any) {
return fail(String(e.error || e))
// We're guaranteed to have a signed event at this point
const signedEvent = event as SignedEvent
// Wait if the thunk is to be delayed
if (thunk.request.delay) {
await sleep(thunk.request.delay)
}
}
// We're guaranteed to have a signed event at this point
const signedEvent = event as SignedEvent
// Skip publishing if aborted
if (thunk.controller.signal.aborted) {
return
}
// Wait if the thunk is to be delayed
if (thunk.request.delay) {
await sleep(thunk.request.delay)
}
// Send it off
const pub = new MultiPublish({
event: signedEvent,
relays: thunk.request.relays,
context: thunk.request.context,
})
// Skip publishing if aborted
if (thunk.controller.signal.aborted) {
return
}
// Copy the signature over since we had deferred it
const savedEvent = repository.getEvent(signedEvent.id) as SignedEvent
// Send it off
const pub = new MultiPublish({
event: signedEvent,
relays: thunk.request.relays,
context: thunk.request.context,
// The event may already be replaced or deleted
if (savedEvent) {
savedEvent.sig = signedEvent.sig
}
pub.on(PublishEvent.Success, (id: string, message: string, url: string) => {
tracker.track(id, url)
thunk.status.update(assoc(url, {status: PublishStatus.Success, message}))
})
pub.on(PublishEvent.Failure, (id: string, message: string, url: string) => {
thunk.status.update(assoc(url, {status: PublishStatus.Failure, message}))
})
pub.on(PublishEvent.Timeout, (url: string) => {
thunk.status.update(
assoc(url, {status: PublishStatus.Timeout, message: "Publish timed out"}),
)
})
pub.on(PublishEvent.Aborted, (url: string) => {
thunk.status.update(
assoc(url, {status: PublishStatus.Aborted, message: "Publish was aborted"}),
)
})
pub.on(PublishEvent.Complete, () => {
thunk.result.resolve(get(thunk.status))
})
})
// Copy the signature over since we had deferred it
const savedEvent = repository.getEvent(signedEvent.id) as SignedEvent
// The event may already be replaced or deleted
if (savedEvent) {
savedEvent.sig = signedEvent.sig
}
pub.on(PublishEvent.Success, (id: string, message: string, url: string) => {
tracker.track(id, url)
thunk.status.update(assoc(url, {status: PublishStatus.Success, message}))
})
pub.on(PublishEvent.Failure, (id: string, message: string, url: string) => {
thunk.status.update(assoc(url, {status: PublishStatus.Failure, message}))
})
pub.on(PublishEvent.Timeout, (url: string) => {
thunk.status.update(assoc(url, {status: PublishStatus.Timeout, message: "Publish timed out"}))
})
pub.on(PublishEvent.Aborted, (url: string) => {
thunk.status.update(
assoc(url, {status: PublishStatus.Aborted, message: "Publish was aborted"}),
)
})
pub.on(PublishEvent.Complete, () => {
thunk.result.resolve(get(thunk.status))
})
})
},
})