@fluidframework/task-manager 2.63.0-359734 → 2.63.0-359962

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -101,6 +101,8 @@ export class TaskManagerClass
101
101
  private readonly completedWatcher: EventEmitter = new EventEmitter();
102
102
  // rollbackWatcher emits an event whenever a pending op is rolled back.
103
103
  private readonly rollbackWatcher: EventEmitter = new EventEmitter();
104
+ // attachedWatcher emits an event whenever the client becomes attached.
105
+ private readonly attachedWatcher: EventEmitter = new EventEmitter();
104
106
 
105
107
  private nextPendingMessageId: number = 0;
106
108
  /**
@@ -116,8 +118,8 @@ export class TaskManagerClass
116
118
  /**
117
119
  * Returns the clientId. Will return a placeholder if the runtime is detached and not yet assigned a clientId.
118
120
  */
119
- private get clientId(): string | undefined {
120
- return this.isAttached() ? this.runtime.clientId : placeholderClientId;
121
+ private get clientId(): string {
122
+ return this.runtime.clientId ?? placeholderClientId;
121
123
  }
122
124
 
123
125
  /**
@@ -220,11 +222,6 @@ export class TaskManagerClass
220
222
  return;
221
223
  }
222
224
 
223
- // Exit early if we are still catching up on reconnect -- we can't be the leader yet anyway.
224
- if (this.clientId === undefined) {
225
- return;
226
- }
227
-
228
225
  if (oldLockHolder !== this.clientId && newLockHolder === this.clientId) {
229
226
  this.emit("assigned", taskId);
230
227
  } else if (oldLockHolder === this.clientId && newLockHolder !== this.clientId) {
@@ -234,8 +231,6 @@ export class TaskManagerClass
234
231
  );
235
232
 
236
233
  this.connectionWatcher.on("disconnect", () => {
237
- assert(this.clientId !== undefined, 0x1d3 /* "Missing client id on disconnect" */);
238
-
239
234
  // Emit "lost" for any tasks we were assigned to.
240
235
  for (const [taskId, clientQueue] of this.taskQueues.entries()) {
241
236
  if (this.isAttached() && clientQueue[0] === this.clientId) {
@@ -325,7 +320,6 @@ export class TaskManagerClass
325
320
 
326
321
  if (this.isDetached()) {
327
322
  // Simulate auto-ack in detached scenario
328
- assert(this.clientId !== undefined, 0x472 /* clientId should not be undefined */);
329
323
  this.addClientToQueue(taskId, this.clientId);
330
324
  return true;
331
325
  }
@@ -382,6 +376,7 @@ export class TaskManagerClass
382
376
  };
383
377
 
384
378
  const rejectOnDisconnect = (): void => {
379
+ this.abandon(taskId);
385
380
  removeListeners();
386
381
  reject(new Error("Disconnected before acquiring task assignment"));
387
382
  };
@@ -430,6 +425,7 @@ export class TaskManagerClass
430
425
  }
431
426
 
432
427
  let volunteerOpMessageId: number | undefined;
428
+ let abandoned = false;
433
429
 
434
430
  const submitVolunteerOp = (): void => {
435
431
  volunteerOpMessageId = this.nextPendingMessageId;
@@ -445,14 +441,16 @@ export class TaskManagerClass
445
441
  const removeListeners = (): void => {
446
442
  this.abandonWatcher.off("abandon", checkIfAbandoned);
447
443
  this.connectionWatcher.off("disconnect", disconnectHandler);
448
- this.connectionWatcher.off("connect", submitVolunteerOp);
449
444
  this.completedWatcher.off("completed", checkIfCompleted);
450
445
  this.rollbackWatcher.off("rollback", checkIfRolledBack);
451
446
  };
452
-
453
447
  const disconnectHandler = (): void => {
454
- // Wait to be connected again and then re-submit volunteer op
455
- this.connectionWatcher.once("connect", submitVolunteerOp);
448
+ // If we are disconnected and have not already sent a volunteer op, then we should
449
+ // submit another volunteer op while disconnected. This will allow the op to be
450
+ // picked up by resubmitCore() and resubmitted when we reconnect.
451
+ if (!this.queuedOptimistically(taskId)) {
452
+ submitVolunteerOp();
453
+ }
456
454
  };
457
455
 
458
456
  const checkIfAbandoned = (eventTaskId: string, messageId: number | undefined): void => {
@@ -475,6 +473,7 @@ export class TaskManagerClass
475
473
  }
476
474
  removeListeners();
477
475
  this.subscribedTasks.delete(taskId);
476
+ abandoned = true;
478
477
  };
479
478
 
480
479
  const checkIfCompleted = (eventTaskId: string, messageId: number | undefined): void => {
@@ -506,23 +505,18 @@ export class TaskManagerClass
506
505
 
507
506
  if (this.isDetached()) {
508
507
  // Simulate auto-ack in detached scenario
509
- assert(this.clientId !== undefined, 0x473 /* clientId should not be undefined */);
510
508
  this.addClientToQueue(taskId, this.clientId);
511
509
  // Because we volunteered with placeholderClientId, we need to wait for when we attach and are assigned
512
510
  // a real clientId. At that point we should re-enter the queue with a real volunteer op (assuming we are
513
511
  // connected).
514
- this.runtime.once("attached", () => {
512
+ this.attachedWatcher.once("attached", () => {
515
513
  // We call scrubClientsNotInQuorum() in case our clientId changed during the attach process.
516
514
  this.scrubClientsNotInQuorum();
517
- if (this.connected) {
515
+ // Make sure abandon() was not called while we were detached.
516
+ if (!abandoned) {
518
517
  submitVolunteerOp();
519
- } else {
520
- this.connectionWatcher.once("connect", submitVolunteerOp);
521
518
  }
522
519
  });
523
- } else if (!this.connected) {
524
- // If we are disconnected (and attached), wait to be connected and submit volunteer op
525
- disconnectHandler();
526
520
  } else if (!this.queuedOptimistically(taskId)) {
527
521
  // We don't need to send a second volunteer op if we just sent one.
528
522
  submitVolunteerOp();
@@ -543,7 +537,6 @@ export class TaskManagerClass
543
537
 
544
538
  if (this.isDetached()) {
545
539
  // Simulate auto-ack in detached scenario
546
- assert(this.clientId !== undefined, 0x474 /* clientId is undefined */);
547
540
  this.removeClientFromQueue(taskId, this.clientId);
548
541
  this.abandonWatcher.emit("abandon", taskId);
549
542
  return;
@@ -573,7 +566,6 @@ export class TaskManagerClass
573
566
  return false;
574
567
  }
575
568
 
576
- assert(this.clientId !== undefined, 0x07f /* "clientId undefined" */);
577
569
  return this.taskQueues.get(taskId)?.includes(this.clientId) ?? false;
578
570
  }
579
571
 
@@ -624,7 +616,7 @@ export class TaskManagerClass
624
616
  * @returns the summary of the current state of the task manager
625
617
  */
626
618
  protected summarizeCore(serializer: IFluidSerializer): ISummaryTreeWithStats {
627
- if (this.runtime.clientId === undefined) {
619
+ if (this.clientId === placeholderClientId) {
628
620
  // If the runtime has still not been assigned a clientId, we should not summarize with the placeholder
629
621
  // clientIds and instead remove them from the queues and require the client to re-volunteer when assigned
630
622
  // a new clientId.
@@ -688,6 +680,12 @@ export class TaskManagerClass
688
680
  );
689
681
  assert(pendingOpIndex !== -1, 0xc43 /* Could not match pending op on resubmit attempt */);
690
682
  pendingOps.splice(pendingOpIndex, 1);
683
+ if (
684
+ content.type === "volunteer" &&
685
+ pendingOps[pendingOps.length - 1]?.type !== "abandon"
686
+ ) {
687
+ this.submitVolunteerOp(content.taskId);
688
+ }
691
689
  if (pendingOps.length === 0) {
692
690
  this.latestPendingOps.delete(content.taskId);
693
691
  }
@@ -837,12 +835,6 @@ export class TaskManagerClass
837
835
  * for the latest pending ops.
838
836
  */
839
837
  private queuedOptimistically(taskId: string): boolean {
840
- if (this.isAttached() && !this.connected) {
841
- return false;
842
- }
843
-
844
- assert(this.clientId !== undefined, 0xc44 /* clientId undefined */);
845
-
846
838
  const inQueue = this.taskQueues.get(taskId)?.includes(this.clientId) ?? false;
847
839
  const latestPendingOps = this.latestPendingOps.get(taskId);
848
840
 
@@ -896,4 +888,11 @@ export class TaskManagerClass
896
888
  }
897
889
  this.rollbackWatcher.emit("rollback", content.taskId);
898
890
  }
891
+
892
+ /**
893
+ * {@inheritDoc @fluidframework/shared-object-base#SharedObject.didAttach}
894
+ */
895
+ protected didAttach(): void {
896
+ this.attachedWatcher.emit("attached");
897
+ }
899
898
  }