@remit/imap-worker 0.0.88 → 0.0.89

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.
package/src/index.ts CHANGED
@@ -9,8 +9,14 @@ import type { SQSBatchResponse, SQSEvent, SQSHandler } from "aws-lambda";
9
9
  import type { WorkerEvent } from "./events.js";
10
10
  import { imapFailureKind } from "./failure-kind.js";
11
11
  import { processEvent } from "./processor.js";
12
+ import {
13
+ setWorkerQueues,
14
+ sqsWorkerQueues,
15
+ workerQueueUrlsFromEnv,
16
+ } from "./queues.js";
12
17
 
13
18
  installNodeLogger();
19
+ setWorkerQueues(sqsWorkerQueues(workerQueueUrlsFromEnv()));
14
20
 
15
21
  const log = createLogger();
16
22
 
@@ -1,13 +1,13 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { afterEach, beforeEach, describe, it } from "node:test";
4
- import type { SendMessageCommand, SQSClient } from "@aws-sdk/client-sqs";
5
4
  import {
6
5
  _resetForTest,
7
6
  type RemitClient,
8
7
  setClient,
9
8
  } from "@remit/backend/client";
10
9
  import type { AccountItem } from "@remit/data-ports";
10
+ import { recordingQueue } from "@remit/data-ports/recording-queue";
11
11
  import {
12
12
  AccountAuthType,
13
13
  AccountService,
@@ -25,6 +25,7 @@ import { createShippedSqliteDb } from "@remit/drizzle-service/test-sqlite";
25
25
  import type { Logger } from "@remit/logger";
26
26
  import { syncMailboxes } from "./handlers/sync-mailboxes.js";
27
27
  import { syncMessages } from "./handlers/sync-messages.js";
28
+ import { setWorkerQueues } from "./queues.js";
28
29
  import { runSchedulerTick } from "./scheduler/run-tick.js";
29
30
 
30
31
  const NOW = 1_700_000_000_000;
@@ -69,6 +70,15 @@ beforeEach(() => {
69
70
  mailboxLock: locks,
70
71
  } as unknown as RemitClient;
71
72
  setClient(client);
73
+ const queue = recordingQueue();
74
+ setWorkerQueues({
75
+ mailboxes: queue,
76
+ messages: queue,
77
+ flags: queue,
78
+ body: queue,
79
+ mailboxManagement: queue,
80
+ messageManagement: queue,
81
+ });
72
82
  });
73
83
 
74
84
  afterEach(() => {
@@ -130,18 +140,11 @@ const seedSyncedAccount = async (): Promise<{
130
140
  };
131
141
 
132
142
  const tick = async (): Promise<{ enqueued: number; accountIds: string[] }> => {
133
- const sent: SendMessageCommand[] = [];
134
- const sqsClient = {
135
- send: async (command: SendMessageCommand) => {
136
- sent.push(command);
137
- return {};
138
- },
139
- } as unknown as SQSClient;
143
+ const queue = recordingQueue();
140
144
  const result = await runSchedulerTick({
141
145
  redriveStrandedPlacements: async () => 0,
142
146
  accountService: accounts,
143
- sqsClient,
144
- queueUrl: "https://queue.test/mailboxes",
147
+ queue,
145
148
  log: silentLogger,
146
149
  tickIntervalMs: 60_000,
147
150
  offlineIntervalMs: 0,
@@ -149,9 +152,7 @@ const tick = async (): Promise<{ enqueued: number; accountIds: string[] }> => {
149
152
  });
150
153
  return {
151
154
  enqueued: result.enqueued,
152
- accountIds: sent.map(
153
- (command) => JSON.parse(command.input.MessageBody ?? "{}").accountId,
154
- ),
155
+ accountIds: queue.sent.map((message) => String(message.body.accountId)),
155
156
  };
156
157
  };
157
158
 
@@ -1,18 +1,21 @@
1
1
  import assert from "node:assert/strict";
2
- import { afterEach, before, beforeEach, describe, it } from "node:test";
3
- import { SendMessageCommand, SQSClient } from "@aws-sdk/client-sqs";
2
+ import { afterEach, beforeEach, describe, it } from "node:test";
4
3
  import {
5
4
  _resetForTest,
6
5
  type RemitClient,
7
6
  setClient,
8
7
  } from "@remit/backend/client";
8
+ import {
9
+ type RecordingQueue,
10
+ recordingQueue,
11
+ } from "@remit/data-ports/recording-queue";
9
12
  import { MailboxLockRepo } from "@remit/drizzle-service";
10
13
  import { createShippedSqliteDb } from "@remit/drizzle-service/test-sqlite";
11
- import { mockClient } from "aws-sdk-client-mock";
12
- import type * as Emit from "./emit.js";
14
+ import { emitEvent, SYNC_MESSAGES_PENDING } from "./emit.js";
13
15
  import type { SyncMessagesEvent } from "./events.js";
14
16
  import { emitMailboxResync, emitMoveResync } from "./handlers/message-move.js";
15
17
  import { emitSyncMessagesEvents } from "./handlers/sync-mailboxes.js";
18
+ import { setWorkerQueues } from "./queues.js";
16
19
 
17
20
  const ACCOUNT = "account-1366";
18
21
  const FOLDERS = [
@@ -21,20 +24,17 @@ const FOLDERS = [
21
24
  { mailboxId: "mbx-archive", fullPath: "Archive" },
22
25
  ];
23
26
 
24
- const sqsMock = mockClient(SQSClient);
25
-
26
- let emitEvent: typeof Emit.emitEvent;
27
- let SYNC_MESSAGES_PENDING: typeof Emit.SYNC_MESSAGES_PENDING;
27
+ let queue: RecordingQueue;
28
28
  let locks: MailboxLockRepo;
29
29
  let closeStore: () => void;
30
30
 
31
31
  type Queued = { mailboxId: string; eventId: string };
32
32
 
33
33
  const queued = (): Queued[] =>
34
- sqsMock.commandCalls(SendMessageCommand).map((call) => {
35
- const body = JSON.parse(call.args[0].input.MessageBody ?? "{}") as Queued;
36
- return { mailboxId: body.mailboxId, eventId: body.eventId };
37
- });
34
+ queue.sent.map(({ body }) => ({
35
+ mailboxId: String(body.mailboxId),
36
+ eventId: String(body.eventId),
37
+ }));
38
38
 
39
39
  const inboxSync = (): Omit<SyncMessagesEvent, "eventId" | "timestamp"> => ({
40
40
  type: "SYNC_MESSAGES",
@@ -45,18 +45,16 @@ const inboxSync = (): Omit<SyncMessagesEvent, "eventId" | "timestamp"> => ({
45
45
  const fanOut = (): Promise<void> =>
46
46
  emitSyncMessagesEvents(ACCOUNT, FOLDERS, emitEvent);
47
47
 
48
- before(async () => {
49
- const fifo = (name: string) =>
50
- `https://sqs.eu-west-1.amazonaws.com/0/test-${name}.fifo`;
51
- process.env.SQS_QUEUE_URL_MAILBOXES = fifo("mailboxes");
52
- process.env.SQS_QUEUE_URL_MESSAGES = fifo("messages");
53
- process.env.SQS_QUEUE_URL_FLAGS = fifo("flags");
54
- ({ emitEvent, SYNC_MESSAGES_PENDING } = await import("./emit.js"));
55
- });
56
-
57
48
  beforeEach(() => {
58
- sqsMock.reset();
59
- sqsMock.on(SendMessageCommand).resolves({});
49
+ queue = recordingQueue();
50
+ setWorkerQueues({
51
+ mailboxes: queue,
52
+ messages: queue,
53
+ flags: queue,
54
+ body: queue,
55
+ mailboxManagement: queue,
56
+ messageManagement: queue,
57
+ });
60
58
  const store = createShippedSqliteDb();
61
59
  closeStore = store.close;
62
60
  locks = new MailboxLockRepo(store.db);
@@ -140,10 +138,11 @@ describe("SYNC_MESSAGES under explicit refreshes (#1366)", () => {
140
138
  });
141
139
 
142
140
  it("releases the marker when the send fails, so the next round is not swallowed", async () => {
143
- sqsMock
144
- .on(SendMessageCommand)
145
- .rejectsOnce(new Error("queue unreachable"))
146
- .resolves({});
141
+ let sends = 0;
142
+ queue.answer(async () => {
143
+ sends += 1;
144
+ if (sends === 1) throw new Error("queue unreachable");
145
+ });
147
146
 
148
147
  await assert.rejects(emitEvent(inboxSync()), /queue unreachable/);
149
148
  await emitEvent(inboxSync());
@@ -162,6 +161,6 @@ describe("SYNC_MESSAGES under explicit refreshes (#1366)", () => {
162
161
  } as unknown as RemitClient);
163
162
 
164
163
  await assert.rejects(emitEvent(inboxSync()), /marker store down/);
165
- assert.equal(sqsMock.commandCalls(SendMessageCommand).length, 0);
164
+ assert.equal(queue.sent.length, 0);
166
165
  });
167
166
  });
package/src/processor.ts CHANGED
@@ -54,14 +54,6 @@ export const processEvent = async (
54
54
  return handleAppendSentMessage(event, log, receiveCount);
55
55
  case "DELETE_ACCOUNT_OBJECTS":
56
56
  return handleDeleteAccountObjects(event, log);
57
- case "IMAP_WORKER_STOP":
58
- // Tombstone fence on the account row already stops processing;
59
- // this event acks the cascade contract and is a no-op today.
60
- log.info(
61
- { accountConfigId: event.accountConfigId, accountId: event.accountId },
62
- "Imap worker stop signal received",
63
- );
64
- return;
65
57
  default:
66
58
  throw new Error(`Unknown event type: ${(event as WorkerEvent).type}`);
67
59
  }
package/src/queues.ts ADDED
@@ -0,0 +1,49 @@
1
+ import type { FifoQueueProducer, QueueProducer } from "@remit/data-ports";
2
+ import { createQueueProducer } from "@remit/sqs-client/producer";
3
+ import { env } from "expect-env";
4
+ import type { ImapEvent, WorkerEvent } from "./events.js";
5
+
6
+ export interface WorkerQueues {
7
+ mailboxes: FifoQueueProducer<ImapEvent>;
8
+ messages: FifoQueueProducer<ImapEvent>;
9
+ flags: FifoQueueProducer<ImapEvent>;
10
+ body: QueueProducer<ImapEvent>;
11
+ mailboxManagement: QueueProducer<ImapEvent>;
12
+ messageManagement: QueueProducer<WorkerEvent>;
13
+ }
14
+
15
+ export type WorkerQueueUrls = Record<keyof WorkerQueues, string>;
16
+
17
+ export const workerQueueUrlsFromEnv = (): WorkerQueueUrls => ({
18
+ mailboxes: env.SQS_QUEUE_URL_MAILBOXES,
19
+ messages: env.SQS_QUEUE_URL_MESSAGES,
20
+ flags: env.SQS_QUEUE_URL_FLAGS,
21
+ body: env.SQS_QUEUE_URL_BODY,
22
+ mailboxManagement: env.SQS_QUEUE_URL_MAILBOX_MGMT,
23
+ messageManagement: env.SQS_QUEUE_URL_MESSAGE_MGMT,
24
+ });
25
+
26
+ export const sqsWorkerQueues = (urls: WorkerQueueUrls): WorkerQueues => ({
27
+ mailboxes: createQueueProducer({ queueUrl: urls.mailboxes }),
28
+ messages: createQueueProducer({ queueUrl: urls.messages }),
29
+ flags: createQueueProducer({ queueUrl: urls.flags }),
30
+ body: createQueueProducer({ queueUrl: urls.body }),
31
+ mailboxManagement: createQueueProducer({ queueUrl: urls.mailboxManagement }),
32
+ messageManagement: createQueueProducer({ queueUrl: urls.messageManagement }),
33
+ });
34
+
35
+ let composed: WorkerQueues | null = null;
36
+
37
+ /** Register the worker's queues from its entry, before the first event. */
38
+ export const setWorkerQueues = (queues: WorkerQueues): void => {
39
+ composed = queues;
40
+ };
41
+
42
+ export const workerQueues = (): WorkerQueues => {
43
+ if (!composed) {
44
+ throw new Error(
45
+ "no worker queues registered — register them with setWorkerQueues() from the entry",
46
+ );
47
+ }
48
+ return composed;
49
+ };
@@ -58,6 +58,7 @@ describe("the outbox attachment sweep", () => {
58
58
  const secondDraft = await arrange(second);
59
59
 
60
60
  const { handler } = await import("./handler.js");
61
+ store.bindWorkerQueues();
61
62
  await handler(
62
63
  { time: new Date().toISOString() } as never,
63
64
  {} as never,
@@ -1,17 +1,19 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { randomBytes } from "node:crypto";
3
3
  import { after, before, beforeEach, describe, it } from "node:test";
4
- import type { SendMessageCommand, SQSClient } from "@aws-sdk/client-sqs";
5
4
  import { finishHalfDeletedAccounts } from "@remit/backend/account-deletion";
6
5
  import type { RemitClient } from "@remit/backend/client";
7
6
  import { importConfig } from "@remit/config-transfer";
8
7
  import type { AccountItem } from "@remit/data-ports";
8
+ import {
9
+ failingQueue,
10
+ type RecordingQueue,
11
+ recordingQueue,
12
+ } from "@remit/data-ports/recording-queue";
9
13
  import { AccountAuthType, ConnectionState } from "@remit/domain-enums";
10
14
  import { noopLogger } from "@remit/logger";
11
15
  import { openStore } from "../test-helpers/test-world.js";
12
16
 
13
- const FANOUT_URL = "http://queue:9324/000000000000/remit-account-fanout";
14
-
15
17
  let store: Awaited<ReturnType<typeof openStore>>;
16
18
  let client: RemitClient;
17
19
 
@@ -105,21 +107,11 @@ const importAccount = async (email: string): Promise<AccountItem> => {
105
107
  return client.account.get(accountId);
106
108
  };
107
109
 
108
- const recordingSqs = (sent: SendMessageCommand[], fail = false): SQSClient =>
109
- ({
110
- send: async (command: SendMessageCommand) => {
111
- if (fail) throw new Error("queue unreachable");
112
- sent.push(command);
113
- return {};
114
- },
115
- }) as unknown as SQSClient;
116
-
117
- const heal = (sqsClient: SQSClient) =>
110
+ const heal = (queue: RecordingQueue) =>
118
111
  finishHalfDeletedAccounts({
119
112
  account: client.account,
120
113
  configImport: client.configImport,
121
- sqsClient,
122
- accountFanoutQueueUrl: FANOUT_URL,
114
+ queue,
123
115
  log: noopLogger,
124
116
  });
125
117
 
@@ -150,13 +142,11 @@ describe("finishing an account deleted before #1384 on scheduler start", () => {
150
142
  ]);
151
143
 
152
144
  it("tombstones it and asks for the same purge a delete asks for", async () => {
153
- const sent: SendMessageCommand[] = [];
145
+ const queue = recordingQueue();
154
146
 
155
- await heal(recordingSqs(sent));
147
+ await heal(queue);
156
148
 
157
- const purges = sent
158
- .filter((command) => command.input.QueueUrl === FANOUT_URL)
159
- .map((command) => JSON.parse(command.input.MessageBody ?? "{}"));
149
+ const purges = queue.sent.map((message) => message.body);
160
150
  assert.ok(
161
151
  purges.some(
162
152
  (body) =>
@@ -175,27 +165,30 @@ describe("finishing an account deleted before #1384 on scheduler start", () => {
175
165
  assert.ok(
176
166
  !purges.some((body) =>
177
167
  [live.accountId, imported.accountId, deleted.accountId].includes(
178
- body.accountId,
168
+ String(body.accountId),
179
169
  ),
180
170
  ),
181
171
  );
182
172
  });
183
173
 
184
174
  it("does nothing for it on the next start", async () => {
185
- await heal(recordingSqs([]));
186
- const sent: SendMessageCommand[] = [];
175
+ await heal(recordingQueue());
176
+ const queue = recordingQueue();
187
177
 
188
- await heal(recordingSqs(sent));
178
+ await heal(queue);
189
179
 
190
180
  assert.ok(
191
- !sent.some((command) =>
192
- (command.input.MessageBody ?? "").includes(halfDeleted.accountId),
181
+ !queue.sent.some(
182
+ (message) => message.body.accountId === halfDeleted.accountId,
193
183
  ),
194
184
  );
195
185
  });
196
186
 
197
187
  it("fails the start and leaves it to be found again when the purge cannot be asked for", async () => {
198
- await assert.rejects(heal(recordingSqs([], true)), /queue unreachable/);
188
+ await assert.rejects(
189
+ heal(failingQueue(new Error("queue unreachable"))),
190
+ /queue unreachable/,
191
+ );
199
192
 
200
193
  const after = await client.account.get(halfDeleted.accountId);
201
194
  assert.equal(after.deletedAt, undefined);
@@ -1,10 +1,14 @@
1
1
  import { getClient } from "@remit/backend/client";
2
2
  import { createLogger } from "@remit/logger";
3
3
  import { installNodeLogger, withTelemetry } from "@remit/logger-lambda/node";
4
- import { createQueueProducer } from "@remit/sqs-client/producer";
5
4
  import type { ScheduledHandler } from "aws-lambda";
6
- import { env } from "expect-env";
7
5
  import { emitEvent } from "../emit.js";
6
+ import {
7
+ setWorkerQueues,
8
+ sqsWorkerQueues,
9
+ workerQueues,
10
+ workerQueueUrlsFromEnv,
11
+ } from "../queues.js";
8
12
  import { buildAttachmentSweep } from "./attachment-sweep.js";
9
13
  import { buildCalendarSubscriptionRefresh } from "./calendar-subscriptions.js";
10
14
  import { getOfflineIntervalMs, getTickIntervalMs } from "./config.js";
@@ -15,8 +19,7 @@ installNodeLogger();
15
19
 
16
20
  const log = createLogger();
17
21
 
18
- const mailboxesQueueUrl = env.SQS_QUEUE_URL_MAILBOXES;
19
- const sqsClient = createQueueProducer({ queueUrl: mailboxesQueueUrl });
22
+ setWorkerQueues(sqsWorkerQueues(workerQueueUrlsFromEnv()));
20
23
 
21
24
  /**
22
25
  * EventBridge-scheduled entry point for the periodic mailbox-sync tick
@@ -41,8 +44,7 @@ export const handler: ScheduledHandler = withTelemetry(async (event) => {
41
44
 
42
45
  await runSchedulerTick({
43
46
  accountService: client.account,
44
- sqsClient,
45
- queueUrl: mailboxesQueueUrl,
47
+ queue: workerQueues().mailboxes,
46
48
  log,
47
49
  tickIntervalMs: getTickIntervalMs(),
48
50
  offlineIntervalMs: getOfflineIntervalMs(),
@@ -1,7 +1,7 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { afterEach, beforeEach, describe, it } from "node:test";
3
- import type { SendMessageCommand, SQSClient } from "@aws-sdk/client-sqs";
4
3
  import type { AccountItem } from "@remit/data-ports";
4
+ import { recordingQueue } from "@remit/data-ports/recording-queue";
5
5
  import {
6
6
  AccountAuthType,
7
7
  AccountService,
@@ -60,18 +60,12 @@ const account = async (
60
60
  const tick = async (
61
61
  options: Partial<Parameters<typeof runSchedulerTick>[0]> = {},
62
62
  ) => {
63
- const sent: SendMessageCommand[] = [];
63
+ const queue = recordingQueue();
64
+ const { sent } = queue;
64
65
  const logs: LogLine[] = [];
65
66
  const result = await runSchedulerTick({
66
67
  accountService: accounts,
67
- sqsClient: {
68
- send: async (command: SendMessageCommand) => {
69
- sent.push(command);
70
- return {};
71
- },
72
- } as unknown as SQSClient,
73
- queueUrl:
74
- "https://sqs.eu-west-1.amazonaws.com/123/remit-dev-mailboxes.fifo",
68
+ queue,
75
69
  log: capturingLogger(logs),
76
70
  tickIntervalMs: TICK_INTERVAL_MS,
77
71
  offlineIntervalMs: OFFLINE_INTERVAL_MS,
@@ -79,9 +73,7 @@ const tick = async (
79
73
  redriveStrandedPlacements: async () => 0,
80
74
  ...options,
81
75
  });
82
- const enqueued = sent.map(
83
- (command) => JSON.parse(command.input.MessageBody ?? "{}").accountId,
84
- );
76
+ const enqueued = sent.map((message) => String(message.body.accountId));
85
77
  return { result, sent, enqueued, logs };
86
78
  };
87
79
 
@@ -129,7 +121,7 @@ describe("a scheduler tick", () => {
129
121
 
130
122
  const { sent } = await tick();
131
123
 
132
- const dedupId = sent[0]?.input.MessageDeduplicationId;
124
+ const dedupId = sent[0]?.deduplicationId;
133
125
  assert.ok(
134
126
  dedupId?.startsWith(`SYNC_MAILBOXES:scheduled:${live.accountId}:`),
135
127
  );
@@ -1,9 +1,13 @@
1
- import type { SQSClient } from "@aws-sdk/client-sqs";
2
1
  import {
3
2
  buildScheduledSyncDedupId,
3
+ type SyncMailboxesEvent,
4
4
  triggerAccountSync,
5
5
  } from "@remit/backend/trigger-sync";
6
- import type { AccountItem, IAccountRepository } from "@remit/data-ports";
6
+ import type {
7
+ AccountItem,
8
+ FifoQueueProducer,
9
+ IAccountRepository,
10
+ } from "@remit/data-ports";
7
11
  import type { Logger } from "@remit/logger";
8
12
  import { hasStoredCredential } from "@remit/mailbox-service/account-credentials";
9
13
  import pMap from "p-map";
@@ -22,8 +26,7 @@ import { isSyncDue } from "./decide-due.js";
22
26
 
23
27
  export interface RunSchedulerTickDeps {
24
28
  accountService: Pick<IAccountRepository, "listAllAccountsPage">;
25
- sqsClient: SQSClient;
26
- queueUrl: string;
29
+ queue: FifoQueueProducer<SyncMailboxesEvent>;
27
30
  log: Logger;
28
31
  offlineIntervalMs: number;
29
32
  /**
@@ -125,14 +128,8 @@ const isEligible = (account: AccountItem): boolean => {
125
128
  export const runSchedulerTick = async (
126
129
  deps: RunSchedulerTickDeps,
127
130
  ): Promise<SchedulerTickResult> => {
128
- const {
129
- accountService,
130
- sqsClient,
131
- queueUrl,
132
- log,
133
- offlineIntervalMs,
134
- tickIntervalMs,
135
- } = deps;
131
+ const { accountService, queue, log, offlineIntervalMs, tickIntervalMs } =
132
+ deps;
136
133
  const now = deps.now ?? Date.now();
137
134
 
138
135
  let cursor: string | undefined;
@@ -161,8 +158,7 @@ export const runSchedulerTick = async (
161
158
  due,
162
159
  (account) =>
163
160
  triggerAccountSync({
164
- sqsClient,
165
- queueUrl,
161
+ queue,
166
162
  accountId: account.accountId,
167
163
  dedupId: buildScheduledSyncDedupId(
168
164
  account.accountId,
@@ -4,11 +4,16 @@ import { finishHalfDeletedAccounts } from "@remit/backend/account-deletion";
4
4
  import { getClient } from "@remit/backend/client";
5
5
  import { createLogger } from "@remit/logger";
6
6
  import { installNodeLogger } from "@remit/logger-lambda/node";
7
+ import { backfillSearchText } from "@remit/mailbox-service";
7
8
  import { clearHeartbeats, createHeartbeat } from "@remit/sqs-client/heartbeat";
8
- import { createQueueProducer } from "@remit/sqs-client/producer";
9
9
 
10
- import { env } from "expect-env";
11
10
  import { emitEvent } from "../emit.js";
11
+ import {
12
+ setWorkerQueues,
13
+ sqsWorkerQueues,
14
+ workerQueues,
15
+ workerQueueUrlsFromEnv,
16
+ } from "../queues.js";
12
17
  import { buildAttachmentSweep } from "./attachment-sweep.js";
13
18
  import { buildCalendarSubscriptionRefresh } from "./calendar-subscriptions.js";
14
19
  import { getOfflineIntervalMs, getTickIntervalMs } from "./config.js";
@@ -40,9 +45,7 @@ installNodeLogger();
40
45
 
41
46
  const log = createLogger();
42
47
 
43
- const mailboxesQueueUrl = env.SQS_QUEUE_URL_MAILBOXES;
44
- const sqsClient = createQueueProducer({ queueUrl: mailboxesQueueUrl });
45
- const accountFanoutQueueUrl = env.SQS_QUEUE_URL_ACCOUNT_FANOUT;
48
+ setWorkerQueues(sqsWorkerQueues(workerQueueUrlsFromEnv()));
46
49
 
47
50
  const tickIntervalMs = getTickIntervalMs();
48
51
  const offlineIntervalMs = getOfflineIntervalMs();
@@ -81,16 +84,20 @@ const runLoop = async (): Promise<void> => {
81
84
  await finishHalfDeletedAccounts({
82
85
  account,
83
86
  configImport: client.configImport,
84
- sqsClient: createQueueProducer({ queueUrl: accountFanoutQueueUrl }),
85
- accountFanoutQueueUrl,
87
+ queue: client.queues.accountFanout,
86
88
  log,
87
89
  });
88
- await runSchedulerLoop({
90
+ const searchTextBackfill = backfillSearchText(
91
+ { messageService: client.message, storageService: client.storage },
92
+ log,
93
+ ).then((result) => {
94
+ log.debug({ ...result }, "Search text backfill finished");
95
+ });
96
+ const schedulerLoop = runSchedulerLoop({
89
97
  tick: runSchedulerTick,
90
98
  tickDeps: {
91
99
  accountService: account,
92
- sqsClient,
93
- queueUrl: mailboxesQueueUrl,
100
+ queue: workerQueues().mailboxes,
94
101
  log,
95
102
  tickIntervalMs,
96
103
  offlineIntervalMs,
@@ -110,6 +117,7 @@ const runLoop = async (): Promise<void> => {
110
117
  onHeartbeatError: onBeatError,
111
118
  tickIntervalMs,
112
119
  });
120
+ await Promise.all([searchTextBackfill, schedulerLoop]);
113
121
  };
114
122
 
115
123
  runLoop()
@@ -84,7 +84,7 @@ const placementMover = (w: World): PlacementMoveService =>
84
84
  addressService: w.client.address,
85
85
  mailboxSpecialUseService: w.client.mailboxSpecialUse,
86
86
  mailboxService: w.client.mailbox,
87
- sqsQueueUrl: process.env.SQS_QUEUE_URL_MESSAGE_MGMT ?? "",
87
+ queue: w.client.queues.messageManagement,
88
88
  });
89
89
 
90
90
  const placementOf = async (a: Arranged) => {
@@ -131,7 +131,8 @@ const queueRefuses = (w: World): void => {
131
131
  };
132
132
 
133
133
  const queueTakes = (w: World): void => {
134
- w.queue.answer(async () => ({ Failed: [] }));
134
+ w.queue.answer(async () => ({}));
135
+ w.queue.refuse([]);
135
136
  };
136
137
 
137
138
  const redriveLater = async (
@@ -255,9 +256,7 @@ describe("a move whose event dies leaves no row moving", () => {
255
256
 
256
257
  it("hands back a move to Trash whose batch entry the queue refused", async () => {
257
258
  const a = await arrange();
258
- a.w.queue.answer(async () => ({
259
- Failed: [{ Id: "0", Code: "InvalidParameterValue", SenderFault: true }],
260
- }));
259
+ a.w.queue.refuse([0]);
261
260
 
262
261
  await assert.rejects(
263
262
  a.w.client.messageMove.deleteMessages(