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
29 changes: 17 additions & 12 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,12 @@ later calls or undo changes already applied to workers.

Workers contacted by a change must respond or exit before the call settles,
including when a later send fails. Send failures preserve the original error;
otherwise, at least one successful response is required and any failed response
rejects the change. Without a send failure, an unanswered target's exit does not
reject a change with a successful peer, including synchronous exits from
protected dispatch hooks. All targets exiting unanswered rejects the change.
Deno and Bun preserve buffered operation results before an observed unexpected
exit; browsers preserve only already-delivered results.
otherwise, at least one accepted response is required and any other failed
response rejects the change. Without a send failure, an unanswered target's exit
does not reject a change with a successful peer, including synchronous exits
from protected dispatch hooks. All targets exiting unanswered rejects the
change. Deno and Bun preserve buffered operation results before an observed
unexpected exit; browsers preserve only already-delivered results.

New and replacement workers wait for ongoing task function changes. They accept
tasks only after restoring successfully registered task functions, the selected
Expand All @@ -210,12 +210,13 @@ are enabled.

Custom worker subclasses that defer task function operations must echo the
request's `taskFunctionOperationId` in their acknowledgement, or retain the
original `taskFunctionProperties` object when using the standard transport. An
acknowledgement without a recoverable UUID rejects the operation. Pool-wide
changes still wait for all contacted workers to respond or exit. Status-only
overrides that discard this correlation must be updated. Deno and Bun preserve
incoming asynchronous context; browsers preserve only synchronous context.
Deferred browser handlers must retain the UUID or original properties object.
original `taskFunctionProperties` object when using the standard transport.
Preserve result metadata when forwarding acknowledgements. An acknowledgement
without a recoverable UUID rejects the operation. Pool-wide changes still wait
for all contacted workers to respond or exit. Status-only overrides that discard
this correlation must be updated. Deno and Bun preserve incoming asynchronous
context; browsers preserve only synchronous context. Deferred browser handlers
must retain the UUID or original properties object.

### `pool.removeTaskFunction(name)`

Expand All @@ -224,6 +225,10 @@ Deferred browser handlers must retain the UUID or original properties object.
This method is available on both pool implementations and returns a boolean
promise.

For workers whose initialization failed, confirmed absence of the requested
function also counts as successful removal. Other removal errors still reject
the call.

### `pool.listTaskFunctionsProperties()`

This method is available on both pool implementations and returns an array of
Expand Down
15 changes: 14 additions & 1 deletion src/pools/abstract-pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,7 @@ export abstract class AbstractPool<
if (this.workerNodes.length === 0) {
return true
}
const { taskFunctionOperation } = message
const taskFunctionOperationId = crypto.randomUUID()
const responsesReceived = new Map<
WorkerInfo['id'] | undefined,
Expand Down Expand Up @@ -1011,7 +1012,19 @@ export abstract class AbstractPool<
protocolError ??= this.getTaskFunctionOperationAcknowledgementError(
message.workerId,
)
} else if (!message.taskFunctionOperationStatus) {
} else if (
!message.taskFunctionOperationStatus &&
!(
taskFunctionOperation === 'remove' &&
message.taskFunctionOperationNotFound === true &&
message.workerError == null &&
this.failedWorkerTaskFunctionBootstraps.has(
this.workerNodes[
this.getWorkerNodeKeyByWorkerId(message.workerId)
],
)
)
) {
errorResponse ??= message
}
complete()
Expand Down
4 changes: 4 additions & 0 deletions src/utility-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ export interface MessageValue<Data = unknown, ErrorData = unknown>
* Task function operation request UUID.
*/
readonly taskFunctionOperationId?: UUID
/**
* Whether removal confirmed that the requested task function was absent.
*/
readonly taskFunctionOperationNotFound?: boolean
/**
* Whether the task function operation is successful or not.
*/
Expand Down
29 changes: 27 additions & 2 deletions src/worker/abstract-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ export abstract class AbstractWorker<
*/
protected activeInterval?: ReturnType<typeof setInterval>

/**
* The base removal result and name that confirm absence for the current operation.
*/
private missingTaskFunctionRemoval?: TaskFunctionOperationResult
private missingTaskFunctionRemovalName?: string

/**
* Correlation of task function operations across protected handler overrides.
*/
Expand Down Expand Up @@ -308,7 +314,12 @@ export abstract class AbstractWorker<
}
const deleteStatus = this.taskFunctions.delete(name)
this.sendTaskFunctionsPropertiesToMainWorker()
return { status: deleteStatus }
const response = { status: deleteStatus }
if (!deleteStatus) {
this.missingTaskFunctionRemoval = response
this.missingTaskFunctionRemovalName = name
}
return response
} catch (error) {
return { status: false, error: error as Error }
}
Expand Down Expand Up @@ -466,6 +477,7 @@ export abstract class AbstractWorker<
'Cannot handle task function operation message without task function properties',
)
}
let taskFunctionOperationNotFound = false
let response: TaskFunctionOperationResult
switch (taskFunctionOperation) {
case 'add':
Expand Down Expand Up @@ -493,7 +505,19 @@ export abstract class AbstractWorker<
response = this.setDefaultTaskFunction(taskFunctionProperties.name)
break
case 'remove':
response = this.removeTaskFunction(taskFunctionProperties.name)
this.missingTaskFunctionRemoval = undefined
this.missingTaskFunctionRemovalName = undefined
try {
const name = taskFunctionProperties.name
response = this.removeTaskFunction(name)
taskFunctionOperationNotFound = !response.status &&
response.error == null &&
Object.is(response, this.missingTaskFunctionRemoval) &&
Object.is(name, this.missingTaskFunctionRemovalName)
} finally {
this.missingTaskFunctionRemoval = undefined
this.missingTaskFunctionRemovalName = undefined
}
break
default:
response = {
Expand All @@ -510,6 +534,7 @@ export abstract class AbstractWorker<
taskFunctionOperationId: this.getTaskFunctionOperationId(message),
taskFunctionOperationStatus: status,
taskFunctionProperties,
...(taskFunctionOperationNotFound && { taskFunctionOperationNotFound }),
...(!status &&
error != null && {
workerError: {
Expand Down
Loading
Loading