From 511a97c4b021c9bdbec6bc9e28774363d8a0875e Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Mon, 10 Feb 2025 13:48:48 +0000 Subject: [PATCH 1/9] run engine v1: orgs are no longer considered for concurrency --- apps/webapp/app/env.server.ts | 2 +- .../registerProjectMetrics.server.ts | 44 ----- .../app/v3/marqs/concurrencyMonitor.server.ts | 8 +- .../v3/marqs/fairDequeuingStrategy.server.ts | 176 ++++-------------- apps/webapp/app/v3/marqs/index.server.ts | 172 ++--------------- .../app/v3/marqs/marqsKeyProducer.server.ts | 40 ---- apps/webapp/app/v3/marqs/types.ts | 10 - apps/webapp/app/v3/marqs/v2.server.ts | 1 - .../webapp/test/fairDequeuingStrategy.test.ts | 125 ++++--------- apps/webapp/test/utils/marqs.ts | 25 +-- 10 files changed, 94 insertions(+), 509 deletions(-) diff --git a/apps/webapp/app/env.server.ts b/apps/webapp/app/env.server.ts index 310732470e..c644503f42 100644 --- a/apps/webapp/app/env.server.ts +++ b/apps/webapp/app/env.server.ts @@ -357,7 +357,7 @@ const EnvironmentSchema = z.object({ MARQS_AVAILABLE_CAPACITY_BIAS: z.coerce.number().default(0.3), MARQS_QUEUE_AGE_RANDOMIZATION_BIAS: z.coerce.number().default(0.25), MARQS_REUSE_SNAPSHOT_COUNT: z.coerce.number().int().default(0), - MARQS_MAXIMUM_ORG_COUNT: z.coerce.number().int().optional(), + MARQS_MAXIMUM_ENV_COUNT: z.coerce.number().int().optional(), PROD_TASK_HEARTBEAT_INTERVAL_MS: z.coerce.number().int().optional(), diff --git a/apps/webapp/app/routes/projects.v3.$projectRef.metrics/registerProjectMetrics.server.ts b/apps/webapp/app/routes/projects.v3.$projectRef.metrics/registerProjectMetrics.server.ts index f6503112fa..636968e9fc 100644 --- a/apps/webapp/app/routes/projects.v3.$projectRef.metrics/registerProjectMetrics.server.ts +++ b/apps/webapp/app/routes/projects.v3.$projectRef.metrics/registerProjectMetrics.server.ts @@ -25,50 +25,6 @@ export async function registerProjectMetrics( }, }); - const firstEnv = allEnvironments[0]; - - if (firstEnv) { - new Gauge({ - name: sanitizeMetricName(`trigger_org_queue_concurrency`), - help: `The number of tasks currently being executed in the org environment queue`, - registers: [registry], - async collect() { - const length = await marqs?.currentConcurrencyOfOrg(firstEnv); - - if (length) { - this.set(length); - } - }, - }); - - new Gauge({ - name: sanitizeMetricName(`trigger_org_queue_concurrency_limit`), - help: `The concurrency limit for the org queue`, - registers: [registry], - async collect() { - const length = await marqs?.getOrgConcurrencyLimit(firstEnv); - - if (length) { - this.set(length); - } - }, - }); - - new Gauge({ - name: sanitizeMetricName(`trigger_org_queue_capacity`), - help: "The capacity of the org queue", - registers: [registry], - async collect() { - const concurrencyLimit = await marqs?.getOrgConcurrencyLimit(firstEnv); - const currentConcurrency = await marqs?.currentConcurrencyOfOrg(firstEnv); - - if (typeof concurrencyLimit === "number" && typeof currentConcurrency === "number") { - this.set(concurrencyLimit - currentConcurrency); - } - }, - }); - } - for (const env of allEnvironments) { if (env.type === "DEVELOPMENT" && env.orgMember?.userId === userId) { await registerEnvironmentMetrics(env, registry); diff --git a/apps/webapp/app/v3/marqs/concurrencyMonitor.server.ts b/apps/webapp/app/v3/marqs/concurrencyMonitor.server.ts index cc091a6a08..f68d342d31 100644 --- a/apps/webapp/app/v3/marqs/concurrencyMonitor.server.ts +++ b/apps/webapp/app/v3/marqs/concurrencyMonitor.server.ts @@ -103,18 +103,16 @@ export class MarqsConcurrencyMonitor { async #processKey(key: string, redis: Redis) { key = this.keys.stripKeyPrefix(key); - const orgKey = this.keys.orgCurrentConcurrencyKeyFromQueue(key); const envKey = this.keys.envCurrentConcurrencyKeyFromQueue(key); let runIds: string[] = []; try { // Next, we need to get all the items from the key, and any parent keys (org, env, queue) using sunion. - runIds = await redis.sunion(orgKey, envKey, key); + runIds = await redis.sunion(envKey, key); } catch (e) { this._logger.error("[MarqsConcurrencyMonitor] error during sunion", { key, - orgKey, envKey, runIds, error: e, @@ -136,7 +134,6 @@ export class MarqsConcurrencyMonitor { if (completedRunIds.length === 0) { this._logger.debug("[MarqsConcurrencyMonitor] no completed runs found", { key, - orgKey, envKey, runIds, durationMs, @@ -147,7 +144,6 @@ export class MarqsConcurrencyMonitor { this._logger.debug("[MarqsConcurrencyMonitor] removing completed runs from queue", { key, - orgKey, envKey, completedRunIds, durationMs, @@ -160,7 +156,6 @@ export class MarqsConcurrencyMonitor { const pipeline = redis.pipeline(); pipeline.srem(key, ...completedRunIds); - pipeline.srem(orgKey, ...completedRunIds); pipeline.srem(envKey, ...completedRunIds); try { @@ -168,7 +163,6 @@ export class MarqsConcurrencyMonitor { } catch (e) { this._logger.error("[MarqsConcurrencyMonitor] error removing completed runs from queue", { key, - orgKey, envKey, completedRunIds, error: e, diff --git a/apps/webapp/app/v3/marqs/fairDequeuingStrategy.server.ts b/apps/webapp/app/v3/marqs/fairDequeuingStrategy.server.ts index 6d971ce3c1..099c67c941 100644 --- a/apps/webapp/app/v3/marqs/fairDequeuingStrategy.server.ts +++ b/apps/webapp/app/v3/marqs/fairDequeuingStrategy.server.ts @@ -33,7 +33,6 @@ export type FairDequeuingStrategyBiases = { export type FairDequeuingStrategyOptions = { redis: Redis; keys: MarQSKeyProducer; - defaultOrgConcurrency: number; defaultEnvConcurrency: number; parentQueueLimit: number; tracer: Tracer; @@ -44,7 +43,7 @@ export type FairDequeuingStrategyOptions = { */ biases?: FairDequeuingStrategyBiases; reuseSnapshotCount?: number; - maximumOrgCount?: number; + maximumEnvCount?: number; }; type FairQueueConcurrency = { @@ -56,7 +55,6 @@ type FairQueue = { id: string; age: number; org: string; env: string }; type FairQueueSnapshot = { id: string; - orgs: Record; envs: Record; queues: Array; }; @@ -73,7 +71,6 @@ type WeightedQueue = { const emptyFairQueueSnapshot: FairQueueSnapshot = { id: "empty", - orgs: {}, envs: {}, queues: [], }; @@ -124,7 +121,6 @@ export class FairDequeuingStrategy implements MarQSFairDequeueStrategy { const snapshot = await this.#createQueueSnapshot(parentQueue, consumerId); span.setAttributes({ - snapshot_org_count: Object.keys(snapshot.orgs).length, snapshot_env_count: Object.keys(snapshot.envs).length, snapshot_queue_count: snapshot.queues.length, }); @@ -344,69 +340,27 @@ export class FairDequeuingStrategy implements MarQSFairDequeueStrategy { return emptyFairQueueSnapshot; } - // Apply org selection if maximumOrgCount is specified - let selectedOrgIds: Set; - if (this.options.maximumOrgCount && this.options.maximumOrgCount > 0) { - selectedOrgIds = this.#selectTopOrgs(queues, this.options.maximumOrgCount); - // Filter queues to only include selected orgs - queues = queues.filter((queue) => selectedOrgIds.has(queue.org)); + // Apply env selection if maximumEnvCount is specified + let selectedEnvIds: Set; + if (this.options.maximumEnvCount && this.options.maximumEnvCount > 0) { + selectedEnvIds = this.#selectTopEnvs(queues, this.options.maximumEnvCount); + // Filter queues to only include selected envs + queues = queues.filter((queue) => selectedEnvIds.has(queue.env)); - span.setAttribute("selected_org_count", selectedOrgIds.size); + span.setAttribute("selected_env_count", selectedEnvIds.size); } span.setAttribute("selected_queue_count", queues.length); - const orgIds = new Set(); const envIds = new Set(); - const envIdToOrgId = new Map(); for (const queue of queues) { - orgIds.add(queue.org); envIds.add(queue.env); - - envIdToOrgId.set(queue.env, queue.org); - } - - const orgs = await Promise.all( - Array.from(orgIds).map(async (orgId) => { - return { id: orgId, concurrency: await this.#getOrgConcurrency(orgId) }; - }) - ); - - const orgsAtFullConcurrency = orgs.filter( - (org) => org.concurrency.current >= org.concurrency.limit - ); - - const orgIdsAtFullConcurrency = new Set(orgsAtFullConcurrency.map((org) => org.id)); - - const orgsSnapshot = orgs.reduce((acc, org) => { - if (!orgIdsAtFullConcurrency.has(org.id)) { - acc[org.id] = org; - } - - return acc; - }, {} as Record); - - span.setAttributes({ - org_count: orgs.length, - orgs_at_full_concurrency_count: orgsAtFullConcurrency.length, - orgs_snapshot_count: Object.keys(orgsSnapshot).length, - }); - - if (Object.keys(orgsSnapshot).length === 0) { - return emptyFairQueueSnapshot; } - const envsWithoutFullOrgs = Array.from(envIds).filter( - (envId) => !orgIdsAtFullConcurrency.has(envIdToOrgId.get(envId)!) - ); - const envs = await Promise.all( - envsWithoutFullOrgs.map(async (envId) => { - return { - id: envId, - concurrency: await this.#getEnvConcurrency(envId, envIdToOrgId.get(envId)!), - }; + Array.from(envIds).map(async (envId) => { + return { id: envId, concurrency: await this.#getEnvConcurrency(envId) }; }) ); @@ -420,7 +374,6 @@ export class FairDequeuingStrategy implements MarQSFairDequeueStrategy { if (!envIdsAtFullConcurrency.has(env.id)) { acc[env.id] = env; } - return acc; }, {} as Record); @@ -429,14 +382,10 @@ export class FairDequeuingStrategy implements MarQSFairDequeueStrategy { envs_at_full_concurrency_count: envsAtFullConcurrency.length, }); - const queuesSnapshot = queues.filter( - (queue) => - !orgIdsAtFullConcurrency.has(queue.org) && !envIdsAtFullConcurrency.has(queue.env) - ); + const queuesSnapshot = queues.filter((queue) => !envIdsAtFullConcurrency.has(queue.env)); const snapshot = { id: randomUUID(), - orgs: orgsSnapshot, envs: envsSnapshot, queues: queuesSnapshot, }; @@ -455,71 +404,54 @@ export class FairDequeuingStrategy implements MarQSFairDequeueStrategy { }); } - #selectTopOrgs(queues: FairQueue[], maximumOrgCount: number): Set { - // Group queues by org - const queuesByOrg = queues.reduce((acc, queue) => { - if (!acc[queue.org]) { - acc[queue.org] = []; + #selectTopEnvs(queues: FairQueue[], maximumEnvCount: number): Set { + // Group queues by env + const queuesByEnv = queues.reduce((acc, queue) => { + if (!acc[queue.env]) { + acc[queue.env] = []; } - acc[queue.org].push(queue); + acc[queue.env].push(queue); return acc; }, {} as Record); - // Calculate average age for each org - const orgAverageAges = Object.entries(queuesByOrg).map(([orgId, orgQueues]) => { - const averageAge = orgQueues.reduce((sum, q) => sum + q.age, 0) / orgQueues.length; - return { orgId, averageAge }; + // Calculate average age for each env + const envAverageAges = Object.entries(queuesByEnv).map(([envId, envQueues]) => { + const averageAge = envQueues.reduce((sum, q) => sum + q.age, 0) / envQueues.length; + return { envId, averageAge }; }); // Perform weighted shuffle based on average ages - const maxAge = Math.max(...orgAverageAges.map((o) => o.averageAge)); - const weightedOrgs = orgAverageAges.map((org) => ({ - orgId: org.orgId, - weight: org.averageAge / maxAge, // Normalize weights + const maxAge = Math.max(...envAverageAges.map((e) => e.averageAge)); + const weightedEnvs = envAverageAges.map((env) => ({ + envId: env.envId, + weight: env.averageAge / maxAge, // Normalize weights })); - // Select top N orgs using weighted shuffle - const selectedOrgs = new Set(); - let remainingOrgs = [...weightedOrgs]; - let totalWeight = remainingOrgs.reduce((sum, org) => sum + org.weight, 0); + // Select top N envs using weighted shuffle + const selectedEnvs = new Set(); + let remainingEnvs = [...weightedEnvs]; + let totalWeight = remainingEnvs.reduce((sum, env) => sum + env.weight, 0); - while (selectedOrgs.size < maximumOrgCount && remainingOrgs.length > 0) { + while (selectedEnvs.size < maximumEnvCount && remainingEnvs.length > 0) { let random = this._rng() * totalWeight; let index = 0; - while (random > 0 && index < remainingOrgs.length) { - random -= remainingOrgs[index].weight; + while (random > 0 && index < remainingEnvs.length) { + random -= remainingEnvs[index].weight; index++; } index = Math.max(0, index - 1); - selectedOrgs.add(remainingOrgs[index].orgId); - totalWeight -= remainingOrgs[index].weight; - remainingOrgs.splice(index, 1); + selectedEnvs.add(remainingEnvs[index].envId); + totalWeight -= remainingEnvs[index].weight; + remainingEnvs.splice(index, 1); } - return selectedOrgs; - } - - async #getOrgConcurrency(orgId: string): Promise { - return await startSpan(this.options.tracer, "getOrgConcurrency", async (span) => { - span.setAttribute("org_id", orgId); - - const [currentValue, limitValue] = await Promise.all([ - this.#getOrgCurrentConcurrency(orgId), - this.#getOrgConcurrencyLimit(orgId), - ]); - - span.setAttribute("current_value", currentValue); - span.setAttribute("limit_value", limitValue); - - return { current: currentValue, limit: limitValue }; - }); + return selectedEnvs; } - async #getEnvConcurrency(envId: string, orgId: string): Promise { + async #getEnvConcurrency(envId: string): Promise { return await startSpan(this.options.tracer, "getEnvConcurrency", async (span) => { - span.setAttribute("org_id", orgId); span.setAttribute("env_id", envId); const [currentValue, limitValue] = await Promise.all([ @@ -570,40 +502,6 @@ export class FairDequeuingStrategy implements MarQSFairDequeueStrategy { }); } - async #getOrgConcurrencyLimit(orgId: string) { - return await startSpan(this.options.tracer, "getOrgConcurrencyLimit", async (span) => { - span.setAttribute("org_id", orgId); - - const key = this.options.keys.orgConcurrencyLimitKey(orgId); - - const result = await this._cache.concurrencyLimit.swr(key, async () => { - const value = await this.options.redis.get(key); - - if (!value) { - return this.options.defaultOrgConcurrency; - } - - return Number(value); - }); - - return result.val ?? this.options.defaultOrgConcurrency; - }); - } - - async #getOrgCurrentConcurrency(orgId: string) { - return await startSpan(this.options.tracer, "getOrgCurrentConcurrency", async (span) => { - span.setAttribute("org_id", orgId); - - const key = this.options.keys.orgCurrentConcurrencyKey(orgId); - - const result = await this.options.redis.scard(key); - - span.setAttribute("current_value", result); - - return result; - }); - } - async #getEnvConcurrencyLimit(envId: string) { return await startSpan(this.options.tracer, "getEnvConcurrencyLimit", async (span) => { span.setAttribute("env_id", envId); diff --git a/apps/webapp/app/v3/marqs/index.server.ts b/apps/webapp/app/v3/marqs/index.server.ts index 341f003464..f27e9b6b0e 100644 --- a/apps/webapp/app/v3/marqs/index.server.ts +++ b/apps/webapp/app/v3/marqs/index.server.ts @@ -101,9 +101,7 @@ export class MarQS { public async updateEnvConcurrencyLimits(env: AuthenticatedEnvironment) { await this.#callUpdateGlobalConcurrencyLimits({ envConcurrencyLimitKey: this.keys.envConcurrencyLimitKey(env), - orgConcurrencyLimitKey: this.keys.orgConcurrencyLimitKey(env), envConcurrencyLimit: env.maximumConcurrencyLimit, - orgConcurrencyLimit: env.organization.maximumConcurrencyLimit, }); } @@ -119,12 +117,6 @@ export class MarQS { return result ? Number(result) : this.options.defaultEnvConcurrency; } - public async getOrgConcurrencyLimit(env: AuthenticatedEnvironment) { - const result = await this.redis.get(this.keys.orgConcurrencyLimitKey(env)); - - return result ? Number(result) : this.options.defaultOrgConcurrency; - } - public async lengthOfQueue( env: AuthenticatedEnvironment, queue: string, @@ -169,10 +161,6 @@ export class MarQS { return this.redis.scard(this.keys.envCurrentConcurrencyKey(env)); } - public async currentConcurrencyOfOrg(env: AuthenticatedEnvironment) { - return this.redis.scard(this.keys.orgCurrentConcurrencyKey(env)); - } - public async enqueueMessage( env: AuthenticatedEnvironment, queue: string, @@ -249,8 +237,6 @@ export class MarQS { currentConcurrencyKey: this.keys.currentConcurrencyKeyFromQueue(messageQueue), envConcurrencyLimitKey: this.keys.envConcurrencyLimitKeyFromQueue(messageQueue), envCurrentConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue), - orgConcurrencyLimitKey: this.keys.orgConcurrencyLimitKeyFromQueue(messageQueue), - orgCurrentConcurrencyKey: this.keys.orgCurrentConcurrencyKeyFromQueue(messageQueue), }); if (!messageData) { @@ -284,7 +270,6 @@ export class MarQS { messageQueue: messageQueue, concurrencyKey: this.keys.currentConcurrencyKeyFromQueue(messageQueue), envConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue), - orgConcurrencyKey: this.keys.orgCurrentConcurrencyKeyFromQueue(messageQueue), messageId: messageData.messageId, }); @@ -348,8 +333,6 @@ export class MarQS { currentConcurrencyKey: this.keys.currentConcurrencyKeyFromQueue(messageQueue), envConcurrencyLimitKey: this.keys.envConcurrencyLimitKeyFromQueue(messageQueue), envCurrentConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue), - orgConcurrencyLimitKey: this.keys.orgConcurrencyLimitKeyFromQueue(messageQueue), - orgCurrentConcurrencyKey: this.keys.orgCurrentConcurrencyKeyFromQueue(messageQueue), }); if (!messageData) { @@ -434,7 +417,6 @@ export class MarQS { messageQueue: message.queue, concurrencyKey: this.keys.currentConcurrencyKeyFromQueue(message.queue), envConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(message.queue), - orgConcurrencyKey: this.keys.orgCurrentConcurrencyKeyFromQueue(message.queue), messageId, }); @@ -502,7 +484,6 @@ export class MarQS { messageQueue: oldMessage.queue, concurrencyKey: this.keys.currentConcurrencyKeyFromQueue(oldMessage.queue), envConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(oldMessage.queue), - orgConcurrencyKey: this.keys.orgCurrentConcurrencyKeyFromQueue(oldMessage.queue), messageId, }); @@ -570,14 +551,12 @@ export class MarQS { const concurrencyKey = this.keys.currentConcurrencyKeyFromQueue(message.queue); const envConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(message.queue); - const orgConcurrencyKey = this.keys.orgCurrentConcurrencyKeyFromQueue(message.queue); logger.debug("Calling releaseConcurrency", { messageId, queue: message.queue, concurrencyKey, envConcurrencyKey, - orgConcurrencyKey, service: this.name, releaseForRun, }); @@ -586,7 +565,6 @@ export class MarQS { //don't release the for the run, it breaks concurrencyLimits releaseForRun ? concurrencyKey : "", envConcurrencyKey, - orgConcurrencyKey, message.messageId ); }, @@ -699,7 +677,6 @@ export class MarQS { parentQueue: message.parentQueue, concurrencyKey: this.keys.currentConcurrencyKeyFromQueue(message.queue), envConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(message.queue), - orgConcurrencyKey: this.keys.orgCurrentConcurrencyKeyFromQueue(message.queue), nackCounterKey: this.keys.nackCounterKey(messageId), messageId, messageScore: retryAt, @@ -931,13 +908,11 @@ export class MarQS { async #callEnqueueMessage(message: MessagePayload) { const concurrencyKey = this.keys.currentConcurrencyKeyFromQueue(message.queue); const envConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(message.queue); - const orgConcurrencyKey = this.keys.orgCurrentConcurrencyKeyFromQueue(message.queue); logger.debug("Calling enqueueMessage", { messagePayload: message, concurrencyKey, envConcurrencyKey, - orgConcurrencyKey, service: this.name, }); @@ -947,7 +922,6 @@ export class MarQS { this.keys.messageKey(message.messageId), concurrencyKey, envConcurrencyKey, - orgConcurrencyKey, this.keys.envQueueKeyFromQueue(message.queue), message.queue, message.messageId, @@ -961,34 +935,27 @@ export class MarQS { parentQueue, concurrencyLimitKey, envConcurrencyLimitKey, - orgConcurrencyLimitKey, currentConcurrencyKey, envCurrentConcurrencyKey, - orgCurrentConcurrencyKey, }: { messageQueue: string; parentQueue: string; concurrencyLimitKey: string; envConcurrencyLimitKey: string; - orgConcurrencyLimitKey: string; currentConcurrencyKey: string; envCurrentConcurrencyKey: string; - orgCurrentConcurrencyKey: string; }) { const result = await this.redis.dequeueMessage( messageQueue, parentQueue, concurrencyLimitKey, envConcurrencyLimitKey, - orgConcurrencyLimitKey, currentConcurrencyKey, envCurrentConcurrencyKey, - orgCurrentConcurrencyKey, this.keys.envQueueKeyFromQueue(messageQueue), messageQueue, String(Date.now()), - String(this.options.defaultEnvConcurrency), - String(this.options.defaultOrgConcurrency) + String(this.options.defaultEnvConcurrency) ); if (!result) { @@ -1028,7 +995,6 @@ export class MarQS { messageQueue, concurrencyKey, envConcurrencyKey, - orgConcurrencyKey, messageId, }: { parentQueue: string; @@ -1036,7 +1002,6 @@ export class MarQS { messageQueue: string; concurrencyKey: string; envConcurrencyKey: string; - orgConcurrencyKey: string; messageId: string; }) { logger.debug("Calling acknowledgeMessage", { @@ -1044,7 +1009,6 @@ export class MarQS { messageQueue, concurrencyKey, envConcurrencyKey, - orgConcurrencyKey, messageId, parentQueue, service: this.name, @@ -1056,7 +1020,6 @@ export class MarQS { messageQueue, concurrencyKey, envConcurrencyKey, - orgConcurrencyKey, this.keys.envQueueKeyFromQueue(messageQueue), messageId, messageQueue @@ -1069,7 +1032,6 @@ export class MarQS { parentQueue, concurrencyKey, envConcurrencyKey, - orgConcurrencyKey, nackCounterKey, messageId, messageScore, @@ -1079,7 +1041,6 @@ export class MarQS { parentQueue: string; concurrencyKey: string; envConcurrencyKey: string; - orgConcurrencyKey: string; nackCounterKey: string; messageId: string; messageScore: number; @@ -1090,7 +1051,6 @@ export class MarQS { parentQueue, concurrencyKey, envConcurrencyKey, - orgConcurrencyKey, nackCounterKey, messageId, messageScore, @@ -1103,7 +1063,6 @@ export class MarQS { parentQueue, concurrencyKey, envConcurrencyKey, - orgConcurrencyKey, this.keys.envQueueKeyFromQueue(messageQueue), nackCounterKey, messageQueue, @@ -1113,48 +1072,16 @@ export class MarQS { ); } - async #callCalculateQueueCurrentConcurrencies({ - currentConcurrencyKey, - currentEnvConcurrencyKey, - currentOrgConcurrencyKey, - }: { - currentConcurrencyKey: string; - currentEnvConcurrencyKey: string; - currentOrgConcurrencyKey: string; - }) { - const currentConcurrencies = await this.redis.calculateQueueCurrentConcurrencies( - currentConcurrencyKey, - currentEnvConcurrencyKey, - currentOrgConcurrencyKey - ); - - const orgCurrent = Number(currentConcurrencies[0]); - const envCurrent = Number(currentConcurrencies[1]); - const queueCurrent = Number(currentConcurrencies[2]); - - return { - queue: queueCurrent, - env: envCurrent, - org: orgCurrent, - }; - } - #callUpdateGlobalConcurrencyLimits({ envConcurrencyLimitKey, - orgConcurrencyLimitKey, envConcurrencyLimit, - orgConcurrencyLimit, }: { envConcurrencyLimitKey: string; - orgConcurrencyLimitKey: string; envConcurrencyLimit: number; - orgConcurrencyLimit: number; }) { return this.redis.updateGlobalConcurrencyLimits( envConcurrencyLimitKey, - orgConcurrencyLimitKey, - String(envConcurrencyLimit), - String(orgConcurrencyLimit) + String(envConcurrencyLimit) ); } @@ -1190,15 +1117,14 @@ export class MarQS { #registerCommands() { this.redis.defineCommand("enqueueMessage", { - numberOfKeys: 7, + numberOfKeys: 6, lua: ` local queue = KEYS[1] local parentQueue = KEYS[2] local messageKey = KEYS[3] local concurrencyKey = KEYS[4] local envCurrentConcurrencyKey = KEYS[5] -local orgCurrentConcurrencyKey = KEYS[6] -local envQueue = KEYS[7] +local envQueue = KEYS[6] local queueName = ARGV[1] local messageId = ARGV[2] @@ -1225,37 +1151,23 @@ end -- Update the concurrency keys redis.call('SREM', concurrencyKey, messageId) redis.call('SREM', envCurrentConcurrencyKey, messageId) -redis.call('SREM', orgCurrentConcurrencyKey, messageId) `, }); this.redis.defineCommand("dequeueMessage", { - numberOfKeys: 9, + numberOfKeys: 7, lua: ` --- Keys: childQueue, parentQueue, concurrencyLimitKey, envConcurrencyLimitKey, orgConcurrencyLimitKey, currentConcurrencyKey, envCurrentConcurrencyKey, orgCurrentConcurrencyKey local childQueue = KEYS[1] local parentQueue = KEYS[2] local concurrencyLimitKey = KEYS[3] local envConcurrencyLimitKey = KEYS[4] -local orgConcurrencyLimitKey = KEYS[5] -local currentConcurrencyKey = KEYS[6] -local envCurrentConcurrencyKey = KEYS[7] -local orgCurrentConcurrencyKey = KEYS[8] -local envQueueKey = KEYS[9] +local currentConcurrencyKey = KEYS[5] +local envCurrentConcurrencyKey = KEYS[6] +local envQueueKey = KEYS[7] --- Args: childQueueName, currentTime, defaultEnvConcurrencyLimit, defaultOrgConcurrencyLimit local childQueueName = ARGV[1] local currentTime = tonumber(ARGV[2]) local defaultEnvConcurrencyLimit = ARGV[3] -local defaultOrgConcurrencyLimit = ARGV[4] - --- Check current org concurrency against the limit -local orgCurrentConcurrency = tonumber(redis.call('SCARD', orgCurrentConcurrencyKey) or '0') -local orgConcurrencyLimit = tonumber(redis.call('GET', orgConcurrencyLimitKey) or defaultOrgConcurrencyLimit) - -if orgCurrentConcurrency >= orgConcurrencyLimit then - return nil -end -- Check current env concurrency against the limit local envCurrentConcurrency = tonumber(redis.call('SCARD', envCurrentConcurrencyKey) or '0') @@ -1284,12 +1196,11 @@ end local messageId = messages[1] local messageScore = tonumber(messages[2]) --- Move message to timeout queue and update concurrency +-- Remove the message from the queue and update concurrency redis.call('ZREM', childQueue, messageId) redis.call('ZREM', envQueueKey, messageId) redis.call('SADD', currentConcurrencyKey, messageId) redis.call('SADD', envCurrentConcurrencyKey, messageId) -redis.call('SADD', orgCurrentConcurrencyKey, messageId) -- Rebalance the parent queue local earliestMessage = redis.call('ZRANGE', childQueue, 0, 0, 'WITHSCORES') @@ -1323,16 +1234,14 @@ redis.call('SET', messageKey, messageData, 'GET') }); this.redis.defineCommand("acknowledgeMessage", { - numberOfKeys: 7, + numberOfKeys: 6, lua: ` --- Keys: parentQueue, messageKey, messageQueue, concurrencyKey, envCurrentConcurrencyKey, orgCurrentConcurrencyKey local parentQueue = KEYS[1] local messageKey = KEYS[2] local messageQueue = KEYS[3] local concurrencyKey = KEYS[4] local envCurrentConcurrencyKey = KEYS[5] -local orgCurrentConcurrencyKey = KEYS[6] -local envQueueKey = KEYS[7] +local envQueueKey = KEYS[6] -- Args: messageId, messageQueueName local messageId = ARGV[1] @@ -1358,21 +1267,19 @@ end -- Update the concurrency keys redis.call('SREM', concurrencyKey, messageId) redis.call('SREM', envCurrentConcurrencyKey, messageId) -redis.call('SREM', orgCurrentConcurrencyKey, messageId) `, }); this.redis.defineCommand("nackMessage", { - numberOfKeys: 8, + numberOfKeys: 7, lua: ` local messageKey = KEYS[1] local childQueueKey = KEYS[2] local parentQueueKey = KEYS[3] local concurrencyKey = KEYS[4] local envConcurrencyKey = KEYS[5] -local orgConcurrencyKey = KEYS[6] -local envQueueKey = KEYS[7] -local nackCounterKey = KEYS[8] +local envQueueKey = KEYS[6] +local nackCounterKey = KEYS[7] -- Args: childQueueName, messageId, currentTime, messageScore local childQueueName = ARGV[1] @@ -1383,7 +1290,6 @@ local messageScore = tonumber(ARGV[4]) -- Update the concurrency keys redis.call('SREM', concurrencyKey, messageId) redis.call('SREM', envConcurrencyKey, messageId) -redis.call('SREM', orgConcurrencyKey, messageId) -- Enqueue the message into the queue redis.call('ZADD', childQueueKey, messageScore, messageId) @@ -1406,11 +1312,10 @@ end }); this.redis.defineCommand("releaseConcurrency", { - numberOfKeys: 3, + numberOfKeys: 2, lua: ` local concurrencyKey = KEYS[1] local envCurrentConcurrencyKey = KEYS[2] -local orgCurrentConcurrencyKey = KEYS[3] local messageId = ARGV[1] @@ -1419,41 +1324,16 @@ if concurrencyKey ~= "" then redis.call('SREM', concurrencyKey, messageId) end redis.call('SREM', envCurrentConcurrencyKey, messageId) -redis.call('SREM', orgCurrentConcurrencyKey, messageId) `, }); - this.redis.defineCommand("calculateQueueCurrentConcurrencies", { - numberOfKeys: 3, - lua: ` --- Keys: currentConcurrencyKey, currentEnvConcurrencyKey, currentOrgConcurrencyKey -local currentConcurrencyKey = KEYS[1] -local currentEnvConcurrencyKey = KEYS[2] -local currentOrgConcurrencyKey = KEYS[3] - -local currentOrgConcurrency = tonumber(redis.call('SCARD', currentOrgConcurrencyKey) or '0') - -local currentEnvConcurrency = tonumber(redis.call('SCARD', currentEnvConcurrencyKey) or '0') - -local currentConcurrency = tonumber(redis.call('SCARD', currentConcurrencyKey) or '0') - -return { currentOrgConcurrency, currentEnvConcurrency, currentConcurrency } - `, - }); - this.redis.defineCommand("updateGlobalConcurrencyLimits", { - numberOfKeys: 2, + numberOfKeys: 1, lua: ` --- Keys: envConcurrencyLimitKey, orgConcurrencyLimitKey local envConcurrencyLimitKey = KEYS[1] -local orgConcurrencyLimitKey = KEYS[2] - --- Args: envConcurrencyLimit, orgConcurrencyLimit local envConcurrencyLimit = ARGV[1] -local orgConcurrencyLimit = ARGV[2] redis.call('SET', envConcurrencyLimitKey, envConcurrencyLimit) -redis.call('SET', orgConcurrencyLimitKey, orgConcurrencyLimit) `, }); @@ -1498,7 +1378,6 @@ declare module "ioredis" { messageKey: string, concurrencyKey: string, envConcurrencyKey: string, - orgConcurrencyKey: string, envQueue: string, queueName: string, messageId: string, @@ -1512,15 +1391,12 @@ declare module "ioredis" { parentQueue: string, concurrencyLimitKey: string, envConcurrencyLimitKey: string, - orgConcurrencyLimitKey: string, currentConcurrencyKey: string, envCurrentConcurrencyKey: string, - orgCurrentConcurrencyKey: string, envQueueKey: string, childQueueName: string, currentTime: string, defaultEnvConcurrencyLimit: string, - defaultOrgConcurrencyLimit: string, callback?: Callback<[string, string]> ): Result<[string, string] | null, Context>; @@ -1536,7 +1412,6 @@ declare module "ioredis" { messageQueue: string, concurrencyKey: string, envConcurrencyKey: string, - orgConcurrencyKey: string, envQueueKey: string, messageId: string, messageQueueName: string, @@ -1549,7 +1424,6 @@ declare module "ioredis" { parentQueueKey: string, concurrencyKey: string, envConcurrencyKey: string, - orgConcurrencyKey: string, envQueueKey: string, nackCounterKey: string, childQueueName: string, @@ -1562,16 +1436,13 @@ declare module "ioredis" { releaseConcurrency( concurrencyKey: string, envConcurrencyKey: string, - orgConcurrencyKey: string, messageId: string, callback?: Callback ): Result; updateGlobalConcurrencyLimits( envConcurrencyLimitKey: string, - orgConcurrencyLimitKey: string, envConcurrencyLimit: string, - orgConcurrencyLimit: string, callback?: Callback ): Result; @@ -1582,13 +1453,6 @@ declare module "ioredis" { currentScore: string, callback?: Callback ): Result; - - calculateQueueCurrentConcurrencies( - currentConcurrencyKey: string, - currentEnvConcurrencyKey: string, - currentOrgConcurrencyKey: string, - callback?: Callback - ): Result; } } @@ -1621,14 +1485,13 @@ function getMarQSClient() { parentQueueLimit: env.MARQS_SHARED_QUEUE_LIMIT, keys: keysProducer, defaultEnvConcurrency: env.DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT, - defaultOrgConcurrency: env.DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT, biases: { concurrencyLimitBias: env.MARQS_CONCURRENCY_LIMIT_BIAS, availableCapacityBias: env.MARQS_AVAILABLE_CAPACITY_BIAS, queueAgeRandomization: env.MARQS_QUEUE_AGE_RANDOMIZATION_BIAS, }, reuseSnapshotCount: env.MARQS_REUSE_SNAPSHOT_COUNT, - maximumOrgCount: env.MARQS_MAXIMUM_ORG_COUNT, + maximumEnvCount: env.MARQS_MAXIMUM_ENV_COUNT, }), envQueuePriorityStrategy: new FairDequeuingStrategy({ tracer: tracer, @@ -1636,7 +1499,6 @@ function getMarQSClient() { parentQueueLimit: env.MARQS_DEV_QUEUE_LIMIT, keys: keysProducer, defaultEnvConcurrency: env.DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT, - defaultOrgConcurrency: env.DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT, biases: { concurrencyLimitBias: 0.0, availableCapacityBias: 0.0, diff --git a/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts b/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts index 227a0a9dd2..bb844f47be 100644 --- a/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts +++ b/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts @@ -45,15 +45,6 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { ].join(":"); } - orgConcurrencyLimitKey(orgId: string): string; - orgConcurrencyLimitKey(env: AuthenticatedEnvironment): string; - orgConcurrencyLimitKey(envOrOrgId: AuthenticatedEnvironment | string) { - return [ - this.orgKeySection(typeof envOrOrgId === "string" ? envOrOrgId : envOrOrgId.organizationId), - constants.CONCURRENCY_LIMIT_PART, - ].join(":"); - } - queueKey(orgId: string, envId: string, queue: string, concurrencyKey?: string): string; queueKey(env: AuthenticatedEnvironment, queue: string, concurrencyKey?: string): string; queueKey( @@ -117,28 +108,6 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { ); } - disabledConcurrencyLimitKeyFromQueue(queue: string) { - const orgId = this.normalizeQueue(queue).split(":")[1]; - - return this.disabledConcurrencyLimitKey(orgId); - } - - disabledConcurrencyLimitKey(orgId: string) { - return `${constants.ORG_PART}:${orgId}:${constants.DISABLED_CONCURRENCY_LIMIT_PART}`; - } - - orgConcurrencyLimitKeyFromQueue(queue: string) { - const orgId = this.normalizeQueue(queue).split(":")[1]; - - return `${constants.ORG_PART}:${orgId}:${constants.CONCURRENCY_LIMIT_PART}`; - } - - orgCurrentConcurrencyKeyFromQueue(queue: string) { - const orgId = this.normalizeQueue(queue).split(":")[1]; - - return `${constants.ORG_PART}:${orgId}:${constants.CURRENT_CONCURRENCY_PART}`; - } - envConcurrencyLimitKeyFromQueue(queue: string) { const envId = this.normalizeQueue(queue).split(":")[3]; @@ -151,15 +120,6 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { return `${constants.ENV_PART}:${envId}:${constants.CURRENT_CONCURRENCY_PART}`; } - orgCurrentConcurrencyKey(orgId: string): string; - orgCurrentConcurrencyKey(env: AuthenticatedEnvironment): string; - orgCurrentConcurrencyKey(envOrOrgId: AuthenticatedEnvironment | string): string { - return [ - this.orgKeySection(typeof envOrOrgId === "string" ? envOrOrgId : envOrOrgId.organizationId), - constants.CURRENT_CONCURRENCY_PART, - ].join(":"); - } - envCurrentConcurrencyKey(envId: string): string; envCurrentConcurrencyKey(env: AuthenticatedEnvironment): string; envCurrentConcurrencyKey(envOrId: AuthenticatedEnvironment | string): string { diff --git a/apps/webapp/app/v3/marqs/types.ts b/apps/webapp/app/v3/marqs/types.ts index e769a71206..d39c1539ac 100644 --- a/apps/webapp/app/v3/marqs/types.ts +++ b/apps/webapp/app/v3/marqs/types.ts @@ -9,12 +9,6 @@ export interface MarQSKeyProducer { envConcurrencyLimitKey(envId: string): string; envConcurrencyLimitKey(env: AuthenticatedEnvironment): string; - orgConcurrencyLimitKey(orgId: string): string; - orgConcurrencyLimitKey(env: AuthenticatedEnvironment): string; - - orgCurrentConcurrencyKey(orgId: string): string; - orgCurrentConcurrencyKey(env: AuthenticatedEnvironment): string; - envCurrentConcurrencyKey(envId: string): string; envCurrentConcurrencyKey(env: AuthenticatedEnvironment): string; @@ -33,10 +27,6 @@ export interface MarQSKeyProducer { queue: string, concurrencyKey?: string ): string; - disabledConcurrencyLimitKey(orgId: string): string; - disabledConcurrencyLimitKeyFromQueue(queue: string): string; - orgConcurrencyLimitKeyFromQueue(queue: string): string; - orgCurrentConcurrencyKeyFromQueue(queue: string): string; envConcurrencyLimitKeyFromQueue(queue: string): string; envCurrentConcurrencyKeyFromQueue(queue: string): string; envQueueKeyFromQueue(queue: string): string; diff --git a/apps/webapp/app/v3/marqs/v2.server.ts b/apps/webapp/app/v3/marqs/v2.server.ts index fee98846b4..6d620536cf 100644 --- a/apps/webapp/app/v3/marqs/v2.server.ts +++ b/apps/webapp/app/v3/marqs/v2.server.ts @@ -80,7 +80,6 @@ function getMarQSClient() { parentQueueLimit: 100, keys: new MarQSV2KeyProducer(KEY_PREFIX), defaultEnvConcurrency: env.V2_MARQS_DEFAULT_ENV_CONCURRENCY, - defaultOrgConcurrency: env.DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT, }), envQueuePriorityStrategy: new NoopFairDequeuingStrategy(), // We don't use this in v2, since all queues go through the shared queue workers: 0, diff --git a/apps/webapp/test/fairDequeuingStrategy.test.ts b/apps/webapp/test/fairDequeuingStrategy.test.ts index fa96d11bc4..be46addad9 100644 --- a/apps/webapp/test/fairDequeuingStrategy.test.ts +++ b/apps/webapp/test/fairDequeuingStrategy.test.ts @@ -14,13 +14,12 @@ const tracer = trace.getTracer("test"); vi.setConfig({ testTimeout: 30_000 }); // 30 seconds timeout describe("FairDequeuingStrategy", () => { - redisTest("should distribute a single queue from a single org/env", async ({ redis }) => { + redisTest("should distribute a single queue from a single env", async ({ redis }) => { const keyProducer = createKeyProducer("test"); const strategy = new FairDequeuingStrategy({ tracer, redis, keys: keyProducer, - defaultOrgConcurrency: 10, defaultEnvConcurrency: 5, parentQueueLimit: 100, seed: "test-seed-1", // for deterministic shuffling @@ -43,48 +42,12 @@ describe("FairDequeuingStrategy", () => { expect(result[0]).toBe("org:org-1:env:env-1:queue:queue-1"); }); - redisTest("should respect org concurrency limits", async ({ redis }) => { - const keyProducer = createKeyProducer("test"); - const strategy = new FairDequeuingStrategy({ - tracer, - redis, - keys: keyProducer, - defaultOrgConcurrency: 2, - defaultEnvConcurrency: 5, - parentQueueLimit: 100, - seed: "test-seed-2", - }); - - // Setup queue - await setupQueue({ - redis, - keyProducer, - parentQueue: "parent-queue", - score: Date.now() - 1000, - queueId: "queue-1", - orgId: "org-1", - envId: "env-1", - }); - - // Set org-1 to be at its concurrency limit - await setupConcurrency({ - redis, - keyProducer, - org: { id: "org-1", currentConcurrency: 2, limit: 2 }, - env: { id: "env-1", currentConcurrency: 0 }, - }); - - const result = await strategy.distributeFairQueuesFromParentQueue("parent-queue", "consumer-1"); - expect(result).toHaveLength(0); - }); - redisTest("should respect env concurrency limits", async ({ redis }) => { const keyProducer = createKeyProducer("test"); const strategy = new FairDequeuingStrategy({ tracer, redis, keys: keyProducer, - defaultOrgConcurrency: 10, defaultEnvConcurrency: 2, parentQueueLimit: 100, seed: "test-seed-3", @@ -103,7 +66,6 @@ describe("FairDequeuingStrategy", () => { await setupConcurrency({ redis, keyProducer, - org: { id: "org-1", currentConcurrency: 0 }, env: { id: "env-1", currentConcurrency: 2, limit: 2 }, }); @@ -117,7 +79,6 @@ describe("FairDequeuingStrategy", () => { tracer, redis, keys: keyProducer, - defaultOrgConcurrency: 10, defaultEnvConcurrency: 5, parentQueueLimit: 2, // Only take 2 queues seed: "test-seed-6", @@ -171,7 +132,6 @@ describe("FairDequeuingStrategy", () => { tracer, redis, keys: keyProducer, - defaultOrgConcurrency: 10, defaultEnvConcurrency: 5, parentQueueLimit: 10, seed: "test-seed-reuse-1", @@ -260,7 +220,6 @@ describe("FairDequeuingStrategy", () => { tracer, redis, keys: keyProducer, - defaultOrgConcurrency: 10, defaultEnvConcurrency: 5, parentQueueLimit: 100, seed: "test-seed-5", @@ -296,7 +255,6 @@ describe("FairDequeuingStrategy", () => { await setupConcurrency({ redis, keyProducer, - org: { id: orgId, currentConcurrency: 2, limit: 10 }, env: { id: envId, currentConcurrency: 1, limit: 5 }, }); } @@ -417,7 +375,6 @@ describe("FairDequeuingStrategy", () => { tracer, redis, keys: keyProducer, - defaultOrgConcurrency: 10, defaultEnvConcurrency: 5, parentQueueLimit: 100, seed: "fixed-seed", @@ -472,13 +429,11 @@ describe("FairDequeuingStrategy", () => { await setupConcurrency({ redis, keyProducer, - org: { id: "org-1", currentConcurrency: 0, limit: 10 }, env: { id: "env-1", currentConcurrency: 0, limit: 5 }, }); await setupConcurrency({ redis, keyProducer, - org: { id: "org-1", currentConcurrency: 0, limit: 10 }, env: { id: "env-2", currentConcurrency: 0, limit: 5 }, }); @@ -546,7 +501,6 @@ describe("FairDequeuingStrategy", () => { await setupConcurrency({ redis, keyProducer, - org: { id: "org-1", currentConcurrency: 0, limit: 200 }, env: { id: setup.envId, currentConcurrency: setup.current, @@ -576,7 +530,6 @@ describe("FairDequeuingStrategy", () => { tracer, redis, keys: keyProducer, - defaultOrgConcurrency: 10, defaultEnvConcurrency: 5, parentQueueLimit: 100, seed: `test-seed-${i}`, @@ -660,7 +613,6 @@ describe("FairDequeuingStrategy", () => { tracer, redis, keys: keyProducer, - defaultOrgConcurrency: 10, defaultEnvConcurrency: 5, parentQueueLimit: 100, seed: "fixed-seed", @@ -709,7 +661,6 @@ describe("FairDequeuingStrategy", () => { await setupConcurrency({ redis, keyProducer, - org: { id: "org-1", currentConcurrency: 0, limit: 10 }, env: { id: "env-1", currentConcurrency: 0, limit: 5 }, }); @@ -738,46 +689,45 @@ describe("FairDequeuingStrategy", () => { }); redisTest( - "should respect maximumOrgCount and select orgs based on queue ages", + "should respect maximumEnvCount and select envs based on queue ages", async ({ redis }) => { const keyProducer = createKeyProducer("test"); const strategy = new FairDequeuingStrategy({ tracer, redis, keys: keyProducer, - defaultOrgConcurrency: 10, defaultEnvConcurrency: 5, parentQueueLimit: 100, seed: "test-seed-max-orgs", - maximumOrgCount: 2, // Only select top 2 orgs + maximumEnvCount: 2, // Only select top 2 orgs }); const now = Date.now(); - // Setup 4 orgs with different queue age profiles - const orgSetups = [ + // Setup 4 envs with different queue age profiles + const envSetups = [ { - orgId: "org-1", + envId: "env-1", queues: [ { age: 1000 }, // Average age: 1000 ], }, { - orgId: "org-2", + envId: "env-2", queues: [ { age: 5000 }, // Average age: 5000 { age: 5000 }, ], }, { - orgId: "org-3", + envId: "env-3", queues: [ { age: 2000 }, // Average age: 2000 { age: 2000 }, ], }, { - orgId: "org-4", + envId: "env-4", queues: [ { age: 500 }, // Average age: 500 { age: 500 }, @@ -786,12 +736,11 @@ describe("FairDequeuingStrategy", () => { ]; // Setup queues and concurrency for each org - for (const setup of orgSetups) { + for (const setup of envSetups) { await setupConcurrency({ redis, keyProducer, - org: { id: setup.orgId, currentConcurrency: 0, limit: 10 }, - env: { id: "env-1", currentConcurrency: 0, limit: 5 }, + env: { id: setup.envId, currentConcurrency: 0, limit: 5 }, }); for (let i = 0; i < setup.queues.length; i++) { @@ -800,16 +749,16 @@ describe("FairDequeuingStrategy", () => { keyProducer, parentQueue: "parent-queue", score: now - setup.queues[i].age, - queueId: `queue-${setup.orgId}-${i}`, - orgId: setup.orgId, - envId: "env-1", + queueId: `queue-${setup.envId}-${i}`, + orgId: `org-${setup.envId}`, + envId: setup.envId, }); } } // Run multiple iterations to verify consistent behavior const iterations = 100; - const selectedOrgCounts: Record = {}; + const selectedEnvCounts: Record = {}; for (let i = 0; i < iterations; i++) { const result = await strategy.distributeFairQueuesFromParentQueue( @@ -818,38 +767,38 @@ describe("FairDequeuingStrategy", () => { ); // Track which orgs were included in the result - const selectedOrgs = new Set(result.map((queueId) => keyProducer.orgIdFromQueue(queueId))); + const selectedEnvs = new Set(result.map((queueId) => keyProducer.envIdFromQueue(queueId))); // Verify we never get more than maximumOrgCount orgs - expect(selectedOrgs.size).toBeLessThanOrEqual(2); + expect(selectedEnvs.size).toBeLessThanOrEqual(2); - for (const orgId of selectedOrgs) { - selectedOrgCounts[orgId] = (selectedOrgCounts[orgId] || 0) + 1; + for (const envId of selectedEnvs) { + selectedEnvCounts[envId] = (selectedEnvCounts[envId] || 0) + 1; } } - console.log("Organization selection counts:", selectedOrgCounts); + console.log("Environment selection counts:", selectedEnvCounts); // org-2 should be selected most often (highest average age) - expect(selectedOrgCounts["org-2"]).toBeGreaterThan(selectedOrgCounts["org-4"] || 0); + expect(selectedEnvCounts["env-2"]).toBeGreaterThan(selectedEnvCounts["env-4"] || 0); // org-4 should be selected least often (lowest average age) - const org4Count = selectedOrgCounts["org-4"] || 0; - expect(org4Count).toBeLessThan(selectedOrgCounts["org-2"]); + const env4Count = selectedEnvCounts["env-4"] || 0; + expect(env4Count).toBeLessThan(selectedEnvCounts["env-2"]); - // Verify that orgs with higher average queue age are selected more frequently - const sortedOrgs = Object.entries(selectedOrgCounts).sort((a, b) => b[1] - a[1]); - console.log("Sorted organization frequencies:", sortedOrgs); + // Verify that envs with higher average queue age are selected more frequently + const sortedEnvs = Object.entries(selectedEnvCounts).sort((a, b) => b[1] - a[1]); + console.log("Sorted environment frequencies:", sortedEnvs); - // The top 2 most frequently selected orgs should be org-2 and org-3 + // The top 2 most frequently selected orgs should be env-2 and env-3 // as they have the highest average queue ages - const topTwoOrgs = new Set([sortedOrgs[0][0], sortedOrgs[1][0]]); - expect(topTwoOrgs).toContain("org-2"); // Highest average age - expect(topTwoOrgs).toContain("org-3"); // Second highest average age + const topTwoEnvs = new Set([sortedEnvs[0][0], sortedEnvs[1][0]]); + expect(topTwoEnvs).toContain("env-2"); // Highest average age + expect(topTwoEnvs).toContain("env-3"); // Second highest average age // Calculate selection percentages - const totalSelections = Object.values(selectedOrgCounts).reduce((a, b) => a + b, 0); - const selectionPercentages = Object.entries(selectedOrgCounts).reduce( + const totalSelections = Object.values(selectedEnvCounts).reduce((a, b) => a + b, 0); + const selectionPercentages = Object.entries(selectedEnvCounts).reduce( (acc, [orgId, count]) => { acc[orgId] = (count / totalSelections) * 100; return acc; @@ -857,13 +806,13 @@ describe("FairDequeuingStrategy", () => { {} as Record ); - console.log("Organization selection percentages:", selectionPercentages); + console.log("Environment selection percentages:", selectionPercentages); - // Verify that org-2 (highest average age) gets selected in at least 40% of iterations - expect(selectionPercentages["org-2"]).toBeGreaterThan(40); + // Verify that env-2 (highest average age) gets selected in at least 40% of iterations + expect(selectionPercentages["env-2"]).toBeGreaterThan(40); - // Verify that org-4 (lowest average age) gets selected in less than 20% of iterations - expect(selectionPercentages["org-4"] || 0).toBeLessThan(20); + // Verify that env-4 (lowest average age) gets selected in less than 20% of iterations + expect(selectionPercentages["env-4"] || 0).toBeLessThan(20); } ); }); diff --git a/apps/webapp/test/utils/marqs.ts b/apps/webapp/test/utils/marqs.ts index 246ce413d3..45e3f552ab 100644 --- a/apps/webapp/test/utils/marqs.ts +++ b/apps/webapp/test/utils/marqs.ts @@ -48,36 +48,13 @@ export async function setupQueue({ type SetupConcurrencyOptions = { redis: Redis; keyProducer: MarQSKeyProducer; - org: { id: string; currentConcurrency: number; limit?: number; isDisabled?: boolean }; env: { id: string; currentConcurrency: number; limit?: number }; }; /** * Sets up concurrency-related Redis keys for orgs and envs */ -export async function setupConcurrency({ redis, keyProducer, org, env }: SetupConcurrencyOptions) { - // Set org concurrency limit if provided - if (typeof org.limit === "number") { - await redis.set(keyProducer.orgConcurrencyLimitKey(org.id), org.limit.toString()); - } - - if (org.currentConcurrency > 0) { - // Set current concurrency by adding dummy members to the set - const orgCurrentKey = keyProducer.orgCurrentConcurrencyKey(org.id); - - // Add dummy running job IDs to simulate current concurrency - const dummyJobs = Array.from( - { length: org.currentConcurrency }, - (_, i) => `dummy-job-${i}-${Date.now()}` - ); - - await redis.sadd(orgCurrentKey, ...dummyJobs); - } - - if (org.isDisabled) { - await redis.set(keyProducer.disabledConcurrencyLimitKey(org.id), "1"); - } - +export async function setupConcurrency({ redis, keyProducer, env }: SetupConcurrencyOptions) { // Set env concurrency limit if (typeof env.limit === "number") { await redis.set(keyProducer.envConcurrencyLimitKey(env.id), env.limit.toString()); From ac8fa75f9f1197c8278e0ed499260c1af4db564e Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Mon, 10 Feb 2025 15:47:45 +0000 Subject: [PATCH 2/9] Add reserve concurrency concept to allow waiting to resume parent tasks to release concurrency at the env level for child tasks to use (or else there is a deadlock). WIP recursive tasks --- .../v3/marqs/fairDequeuingStrategy.server.ts | 23 ++- apps/webapp/app/v3/marqs/index.server.ts | 141 ++++++++++-------- .../app/v3/marqs/marqsKeyProducer.server.ts | 11 ++ apps/webapp/app/v3/marqs/types.ts | 3 + .../app/v3/services/triggerTask.server.ts | 17 +-- .../webapp/test/fairDequeuingStrategy.test.ts | 41 ++++- apps/webapp/test/utils/marqs.ts | 15 +- .../v3-catalog/src/trigger/concurrency.ts | 87 ++++++++++- 8 files changed, 248 insertions(+), 90 deletions(-) diff --git a/apps/webapp/app/v3/marqs/fairDequeuingStrategy.server.ts b/apps/webapp/app/v3/marqs/fairDequeuingStrategy.server.ts index 099c67c941..64e44b9562 100644 --- a/apps/webapp/app/v3/marqs/fairDequeuingStrategy.server.ts +++ b/apps/webapp/app/v3/marqs/fairDequeuingStrategy.server.ts @@ -49,6 +49,7 @@ export type FairDequeuingStrategyOptions = { type FairQueueConcurrency = { current: number; limit: number; + reserve: number; }; type FairQueue = { id: string; age: number; org: string; env: string }; @@ -365,7 +366,7 @@ export class FairDequeuingStrategy implements MarQSFairDequeueStrategy { ); const envsAtFullConcurrency = envs.filter( - (env) => env.concurrency.current >= env.concurrency.limit + (env) => env.concurrency.current >= env.concurrency.limit + env.concurrency.reserve ); const envIdsAtFullConcurrency = new Set(envsAtFullConcurrency.map((env) => env.id)); @@ -454,15 +455,17 @@ export class FairDequeuingStrategy implements MarQSFairDequeueStrategy { return await startSpan(this.options.tracer, "getEnvConcurrency", async (span) => { span.setAttribute("env_id", envId); - const [currentValue, limitValue] = await Promise.all([ + const [currentValue, limitValue, reserveValue] = await Promise.all([ this.#getEnvCurrentConcurrency(envId), this.#getEnvConcurrencyLimit(envId), + this.#getEnvReserveConcurrency(envId), ]); span.setAttribute("current_value", currentValue); span.setAttribute("limit_value", limitValue); + span.setAttribute("reserve_value", reserveValue); - return { current: currentValue, limit: limitValue }; + return { current: currentValue, limit: limitValue, reserve: reserveValue }; }); } @@ -535,6 +538,20 @@ export class FairDequeuingStrategy implements MarQSFairDequeueStrategy { return result; }); } + + async #getEnvReserveConcurrency(envId: string) { + return await startSpan(this.options.tracer, "getEnvReserveConcurrency", async (span) => { + span.setAttribute("env_id", envId); + + const key = this.options.keys.envReserveConcurrencyKey(envId); + + const result = await this.options.redis.scard(key); + + span.setAttribute("current_value", result); + + return result; + }); + } } export class NoopFairDequeuingStrategy implements MarQSFairDequeueStrategy { diff --git a/apps/webapp/app/v3/marqs/index.server.ts b/apps/webapp/app/v3/marqs/index.server.ts index f27e9b6b0e..7fc4a33e6a 100644 --- a/apps/webapp/app/v3/marqs/index.server.ts +++ b/apps/webapp/app/v3/marqs/index.server.ts @@ -99,8 +99,15 @@ export class MarQS { } public async updateEnvConcurrencyLimits(env: AuthenticatedEnvironment) { + const envConcurrencyLimitKey = this.keys.envConcurrencyLimitKey(env); + + logger.debug("Updating env concurrency limits", { + envConcurrencyLimitKey, + service: this.name, + }); + await this.#callUpdateGlobalConcurrencyLimits({ - envConcurrencyLimitKey: this.keys.envConcurrencyLimitKey(env), + envConcurrencyLimitKey, envConcurrencyLimit: env.maximumConcurrencyLimit, }); } @@ -233,10 +240,6 @@ export class MarQS { const messageData = await this.#callDequeueMessage({ messageQueue, parentQueue, - concurrencyLimitKey: this.keys.concurrencyLimitKeyFromQueue(messageQueue), - currentConcurrencyKey: this.keys.currentConcurrencyKeyFromQueue(messageQueue), - envConcurrencyLimitKey: this.keys.envConcurrencyLimitKeyFromQueue(messageQueue), - envCurrentConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue), }); if (!messageData) { @@ -266,10 +269,7 @@ export class MarQS { await this.#callAcknowledgeMessage({ parentQueue, - messageKey: this.keys.messageKey(messageData.messageId), messageQueue: messageQueue, - concurrencyKey: this.keys.currentConcurrencyKeyFromQueue(messageQueue), - envConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue), messageId: messageData.messageId, }); @@ -329,10 +329,6 @@ export class MarQS { const messageData = await this.#callDequeueMessage({ messageQueue, parentQueue, - concurrencyLimitKey: this.keys.concurrencyLimitKeyFromQueue(messageQueue), - currentConcurrencyKey: this.keys.currentConcurrencyKeyFromQueue(messageQueue), - envConcurrencyLimitKey: this.keys.envConcurrencyLimitKeyFromQueue(messageQueue), - envCurrentConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue), }); if (!messageData) { @@ -413,10 +409,7 @@ export class MarQS { await this.#callAcknowledgeMessage({ parentQueue: message.parentQueue, - messageKey: this.keys.messageKey(messageId), messageQueue: message.queue, - concurrencyKey: this.keys.currentConcurrencyKeyFromQueue(message.queue), - envConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(message.queue), messageId, }); @@ -480,10 +473,7 @@ export class MarQS { await this.#callAcknowledgeMessage({ parentQueue: oldMessage.parentQueue, - messageKey: this.keys.messageKey(messageId), messageQueue: oldMessage.queue, - concurrencyKey: this.keys.currentConcurrencyKeyFromQueue(oldMessage.queue), - envConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(oldMessage.queue), messageId, }); @@ -523,9 +513,9 @@ export class MarQS { ); } - public async releaseConcurrency(messageId: string, releaseForRun: boolean = false) { + public async reserveConcurrency(messageId: string) { return this.#trace( - "releaseConcurrency", + "reserveConcurrency", async (span) => { span.setAttributes({ [SemanticAttributes.MESSAGE_ID]: messageId, @@ -534,9 +524,8 @@ export class MarQS { const message = await this.readMessage(messageId); if (!message) { - logger.log(`[${this.name}].releaseConcurrency() message not found`, { + logger.log(`[${this.name}].reserveConcurrency() message not found`, { messageId, - releaseForRun, service: this.name, }); return; @@ -549,29 +538,28 @@ export class MarQS { [SemanticAttributes.PARENT_QUEUE]: message.parentQueue, }); - const concurrencyKey = this.keys.currentConcurrencyKeyFromQueue(message.queue); - const envConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(message.queue); + const reserveConcurrencyKey = this.keys.envReserveConcurrencyKeyFromQueue(message.queue); + const envConcurrencyLimitKey = this.keys.envConcurrencyLimitKeyFromQueue(message.queue); - logger.debug("Calling releaseConcurrency", { + logger.debug("Calling reserveConcurrency", { messageId, queue: message.queue, - concurrencyKey, - envConcurrencyKey, + reserveConcurrencyKey, + envConcurrencyLimitKey, service: this.name, - releaseForRun, }); - return this.redis.releaseConcurrency( - //don't release the for the run, it breaks concurrencyLimits - releaseForRun ? concurrencyKey : "", - envConcurrencyKey, - message.messageId + return this.redis.reserveConcurrency( + reserveConcurrencyKey, + envConcurrencyLimitKey, + message.messageId, + String(this.options.defaultEnvConcurrency) ); }, { kind: SpanKind.CONSUMER, attributes: { - [SEMATTRS_MESSAGING_OPERATION]: "releaseConcurrency", + [SEMATTRS_MESSAGING_OPERATION]: "reserveConcurrency", [SEMATTRS_MESSAGE_ID]: messageId, [SEMATTRS_MESSAGING_SYSTEM]: "marqs", }, @@ -908,6 +896,7 @@ export class MarQS { async #callEnqueueMessage(message: MessagePayload) { const concurrencyKey = this.keys.currentConcurrencyKeyFromQueue(message.queue); const envConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(message.queue); + const envReserveConcurrencyKey = this.keys.envReserveConcurrencyKeyFromQueue(message.queue); logger.debug("Calling enqueueMessage", { messagePayload: message, @@ -922,6 +911,7 @@ export class MarQS { this.keys.messageKey(message.messageId), concurrencyKey, envConcurrencyKey, + envReserveConcurrencyKey, this.keys.envQueueKeyFromQueue(message.queue), message.queue, message.messageId, @@ -933,18 +923,16 @@ export class MarQS { async #callDequeueMessage({ messageQueue, parentQueue, - concurrencyLimitKey, - envConcurrencyLimitKey, - currentConcurrencyKey, - envCurrentConcurrencyKey, }: { messageQueue: string; parentQueue: string; - concurrencyLimitKey: string; - envConcurrencyLimitKey: string; - currentConcurrencyKey: string; - envCurrentConcurrencyKey: string; }) { + const concurrencyLimitKey = this.keys.concurrencyLimitKeyFromQueue(messageQueue); + const currentConcurrencyKey = this.keys.currentConcurrencyKeyFromQueue(messageQueue); + const envConcurrencyLimitKey = this.keys.envConcurrencyLimitKeyFromQueue(messageQueue); + const envCurrentConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue); + const envReserveConcurrencyKey = this.keys.envReserveConcurrencyKeyFromQueue(messageQueue); + const result = await this.redis.dequeueMessage( messageQueue, parentQueue, @@ -952,6 +940,7 @@ export class MarQS { envConcurrencyLimitKey, currentConcurrencyKey, envCurrentConcurrencyKey, + envReserveConcurrencyKey, this.keys.envQueueKeyFromQueue(messageQueue), messageQueue, String(Date.now()), @@ -991,19 +980,18 @@ export class MarQS { async #callAcknowledgeMessage({ parentQueue, - messageKey, messageQueue, - concurrencyKey, - envConcurrencyKey, messageId, }: { parentQueue: string; - messageKey: string; messageQueue: string; - concurrencyKey: string; - envConcurrencyKey: string; messageId: string; }) { + const messageKey = this.keys.messageKey(messageId); + const concurrencyKey = this.keys.currentConcurrencyKeyFromQueue(messageQueue); + const envConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue); + const envReserveConcurrencyKey = this.keys.envReserveConcurrencyKeyFromQueue(messageQueue); + logger.debug("Calling acknowledgeMessage", { messageKey, messageQueue, @@ -1020,6 +1008,7 @@ export class MarQS { messageQueue, concurrencyKey, envConcurrencyKey, + envReserveConcurrencyKey, this.keys.envQueueKeyFromQueue(messageQueue), messageId, messageQueue @@ -1117,14 +1106,15 @@ export class MarQS { #registerCommands() { this.redis.defineCommand("enqueueMessage", { - numberOfKeys: 6, + numberOfKeys: 7, lua: ` local queue = KEYS[1] local parentQueue = KEYS[2] local messageKey = KEYS[3] local concurrencyKey = KEYS[4] local envCurrentConcurrencyKey = KEYS[5] -local envQueue = KEYS[6] +local envReserveConcurrencyKey = KEYS[6] +local envQueue = KEYS[7] local queueName = ARGV[1] local messageId = ARGV[2] @@ -1151,11 +1141,12 @@ end -- Update the concurrency keys redis.call('SREM', concurrencyKey, messageId) redis.call('SREM', envCurrentConcurrencyKey, messageId) +redis.call('SREM', envReserveConcurrencyKey, messageId) `, }); this.redis.defineCommand("dequeueMessage", { - numberOfKeys: 7, + numberOfKeys: 8, lua: ` local childQueue = KEYS[1] local parentQueue = KEYS[2] @@ -1163,7 +1154,8 @@ local concurrencyLimitKey = KEYS[3] local envConcurrencyLimitKey = KEYS[4] local currentConcurrencyKey = KEYS[5] local envCurrentConcurrencyKey = KEYS[6] -local envQueueKey = KEYS[7] +local envReserveConcurrencyKey = KEYS[7] +local envQueueKey = KEYS[8] local childQueueName = ARGV[1] local currentTime = tonumber(ARGV[2]) @@ -1172,14 +1164,16 @@ local defaultEnvConcurrencyLimit = ARGV[3] -- Check current env concurrency against the limit local envCurrentConcurrency = tonumber(redis.call('SCARD', envCurrentConcurrencyKey) or '0') local envConcurrencyLimit = tonumber(redis.call('GET', envConcurrencyLimitKey) or defaultEnvConcurrencyLimit) +local envReserveConcurrency = tonumber(redis.call('SCARD', envReserveConcurrencyKey) or '0') +local totalEnvConcurrencyLimit = envConcurrencyLimit + envReserveConcurrency -if envCurrentConcurrency >= envConcurrencyLimit then +if envCurrentConcurrency >= totalEnvConcurrencyLimit then return nil end -- Check current queue concurrency against the limit local currentConcurrency = tonumber(redis.call('SCARD', currentConcurrencyKey) or '0') -local concurrencyLimit = tonumber(redis.call('GET', concurrencyLimitKey) or envConcurrencyLimit) +local concurrencyLimit = math.min(tonumber(redis.call('GET', concurrencyLimitKey) or '1000000'), envConcurrencyLimit) -- Check condition only if concurrencyLimit exists if currentConcurrency >= concurrencyLimit then @@ -1202,6 +1196,9 @@ redis.call('ZREM', envQueueKey, messageId) redis.call('SADD', currentConcurrencyKey, messageId) redis.call('SADD', envCurrentConcurrencyKey, messageId) +-- Remove the message from the reserve concurrency set +redis.call('SREM', envReserveConcurrencyKey, messageId) + -- Rebalance the parent queue local earliestMessage = redis.call('ZRANGE', childQueue, 0, 0, 'WITHSCORES') if #earliestMessage == 0 then @@ -1234,14 +1231,15 @@ redis.call('SET', messageKey, messageData, 'GET') }); this.redis.defineCommand("acknowledgeMessage", { - numberOfKeys: 6, + numberOfKeys: 7, lua: ` local parentQueue = KEYS[1] local messageKey = KEYS[2] local messageQueue = KEYS[3] local concurrencyKey = KEYS[4] local envCurrentConcurrencyKey = KEYS[5] -local envQueueKey = KEYS[6] +local envReserveConcurrencyKey = KEYS[6] +local envQueueKey = KEYS[7] -- Args: messageId, messageQueueName local messageId = ARGV[1] @@ -1256,6 +1254,9 @@ redis.call('ZREM', messageQueue, messageId) -- Remove the message from the env queue redis.call('ZREM', envQueueKey, messageId) +-- Remove the message from the reserve concurrency set +redis.call('SREM', envReserveConcurrencyKey, messageId) + -- Rebalance the parent queue local earliestMessage = redis.call('ZRANGE', messageQueue, 0, 0, 'WITHSCORES') if #earliestMessage == 0 then @@ -1311,19 +1312,23 @@ end `, }); - this.redis.defineCommand("releaseConcurrency", { + this.redis.defineCommand("reserveConcurrency", { numberOfKeys: 2, lua: ` -local concurrencyKey = KEYS[1] -local envCurrentConcurrencyKey = KEYS[2] +local reserveConcurrencyKey = KEYS[1] +local envConcurrencyLimitKey = KEYS[2] local messageId = ARGV[1] +local defaultEnvConcurrencyLimit = ARGV[2] --- Update the concurrency keys -if concurrencyKey ~= "" then - redis.call('SREM', concurrencyKey, messageId) +local envConcurrencyLimit = tonumber(redis.call('GET', envConcurrencyLimitKey) or defaultEnvConcurrencyLimit) +-- Count the number of messages in the reserve concurrency set +local reserveConcurrency = tonumber(redis.call('SCARD', reserveConcurrencyKey) or '0') + +-- If there is space, add the messaageId to the reserve concurrency set +if reserveConcurrency < envConcurrencyLimit then + redis.call('SADD', reserveConcurrencyKey, messageId) end -redis.call('SREM', envCurrentConcurrencyKey, messageId) `, }); @@ -1378,6 +1383,7 @@ declare module "ioredis" { messageKey: string, concurrencyKey: string, envConcurrencyKey: string, + envReserveConcurrencyKey: string, envQueue: string, queueName: string, messageId: string, @@ -1393,6 +1399,7 @@ declare module "ioredis" { envConcurrencyLimitKey: string, currentConcurrencyKey: string, envCurrentConcurrencyKey: string, + envReserveConcurrencyKey: string, envQueueKey: string, childQueueName: string, currentTime: string, @@ -1412,6 +1419,7 @@ declare module "ioredis" { messageQueue: string, concurrencyKey: string, envConcurrencyKey: string, + envReserveConcurrencyKey: string, envQueueKey: string, messageId: string, messageQueueName: string, @@ -1433,10 +1441,11 @@ declare module "ioredis" { callback?: Callback ): Result; - releaseConcurrency( - concurrencyKey: string, - envConcurrencyKey: string, + reserveConcurrency( + reserveConcurrencyKey: string, + envConcurrencyLimitKey: string, messageId: string, + defaultEnvConcurrencyLimit: string, callback?: Callback ): Result; diff --git a/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts b/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts index bb844f47be..2191f16aec 100644 --- a/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts +++ b/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts @@ -11,6 +11,7 @@ const constants = { QUEUE_PART: "queue", CONCURRENCY_KEY_PART: "ck", MESSAGE_PART: "message", + RESERVE_CONCURRENCY_PART: "reserveConcurrency", } as const; export class MarQSShortKeyProducer implements MarQSKeyProducer { @@ -120,6 +121,16 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { return `${constants.ENV_PART}:${envId}:${constants.CURRENT_CONCURRENCY_PART}`; } + envReserveConcurrencyKeyFromQueue(queue: string) { + const envId = this.normalizeQueue(queue).split(":")[3]; + + return this.envReserveConcurrencyKey(envId); + } + + envReserveConcurrencyKey(envId: string): string { + return `${constants.ENV_PART}:${envId}:${constants.RESERVE_CONCURRENCY_PART}`; + } + envCurrentConcurrencyKey(envId: string): string; envCurrentConcurrencyKey(env: AuthenticatedEnvironment): string; envCurrentConcurrencyKey(envOrId: AuthenticatedEnvironment | string): string { diff --git a/apps/webapp/app/v3/marqs/types.ts b/apps/webapp/app/v3/marqs/types.ts index d39c1539ac..a283dc3cfd 100644 --- a/apps/webapp/app/v3/marqs/types.ts +++ b/apps/webapp/app/v3/marqs/types.ts @@ -12,6 +12,8 @@ export interface MarQSKeyProducer { envCurrentConcurrencyKey(envId: string): string; envCurrentConcurrencyKey(env: AuthenticatedEnvironment): string; + envReserveConcurrencyKey(envId: string): string; + queueKey(orgId: string, envId: string, queue: string, concurrencyKey?: string): string; queueKey(env: AuthenticatedEnvironment, queue: string, concurrencyKey?: string): string; @@ -29,6 +31,7 @@ export interface MarQSKeyProducer { ): string; envConcurrencyLimitKeyFromQueue(queue: string): string; envCurrentConcurrencyKeyFromQueue(queue: string): string; + envReserveConcurrencyKeyFromQueue(queue: string): string; envQueueKeyFromQueue(queue: string): string; messageKey(messageId: string): string; nackCounterKey(messageId: string): string; diff --git a/apps/webapp/app/v3/services/triggerTask.server.ts b/apps/webapp/app/v3/services/triggerTask.server.ts index b7df21cde5..284a14d85b 100644 --- a/apps/webapp/app/v3/services/triggerTask.server.ts +++ b/apps/webapp/app/v3/services/triggerTask.server.ts @@ -547,18 +547,13 @@ export class TriggerTaskService extends BaseService { this._prisma ); - //release the concurrency for the env and org, if part of a (batch)triggerAndWait + // If this is a triggerAndWait or batchTriggerAndWait, + // we need to add the parent run to the reserve concurrency set + // to free up concurrency for the children to run if (dependentAttempt) { - const isSameTask = dependentAttempt.taskRun.taskIdentifier === taskId; - await marqs?.releaseConcurrency(dependentAttempt.taskRun.id, isSameTask); - } - if (dependentBatchRun?.dependentTaskAttempt) { - const isSameTask = - dependentBatchRun.dependentTaskAttempt.taskRun.taskIdentifier === taskId; - await marqs?.releaseConcurrency( - dependentBatchRun.dependentTaskAttempt.taskRun.id, - isSameTask - ); + await marqs?.reserveConcurrency(dependentAttempt.taskRun.id); + } else if (dependentBatchRun?.dependentTaskAttempt) { + await marqs?.reserveConcurrency(dependentBatchRun.dependentTaskAttempt.taskRun.id); } if (!run) { diff --git a/apps/webapp/test/fairDequeuingStrategy.test.ts b/apps/webapp/test/fairDequeuingStrategy.test.ts index be46addad9..c608863198 100644 --- a/apps/webapp/test/fairDequeuingStrategy.test.ts +++ b/apps/webapp/test/fairDequeuingStrategy.test.ts @@ -73,6 +73,43 @@ describe("FairDequeuingStrategy", () => { expect(result).toHaveLength(0); }); + redisTest( + "should give extra concurrency when the env has reserve concurrency", + async ({ redis }) => { + const keyProducer = createKeyProducer("test"); + const strategy = new FairDequeuingStrategy({ + tracer, + redis, + keys: keyProducer, + defaultEnvConcurrency: 2, + parentQueueLimit: 100, + seed: "test-seed-3", + }); + + await setupQueue({ + redis, + keyProducer, + parentQueue: "parent-queue", + score: Date.now() - 1000, + queueId: "queue-1", + orgId: "org-1", + envId: "env-1", + }); + + await setupConcurrency({ + redis, + keyProducer, + env: { id: "env-1", currentConcurrency: 2, limit: 2, reserveConcurrency: 1 }, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue( + "parent-queue", + "consumer-1" + ); + expect(result).toHaveLength(1); + } + ); + redisTest("should respect parentQueueLimit", async ({ redis }) => { const keyProducer = createKeyProducer("test"); const strategy = new FairDequeuingStrategy({ @@ -196,8 +233,8 @@ describe("FairDequeuingStrategy", () => { console.log("Second distribution took", distribute2Duration, "ms"); - // Make sure the second call is more than 10 times faster than the first - expect(distribute2Duration).toBeLessThan(distribute1Duration / 10); + // Make sure the second call is more than 9 times faster than the first + expect(distribute2Duration).toBeLessThan(distribute1Duration / 9); const startDistribute3 = performance.now(); diff --git a/apps/webapp/test/utils/marqs.ts b/apps/webapp/test/utils/marqs.ts index 45e3f552ab..b303aa15b1 100644 --- a/apps/webapp/test/utils/marqs.ts +++ b/apps/webapp/test/utils/marqs.ts @@ -48,7 +48,7 @@ export async function setupQueue({ type SetupConcurrencyOptions = { redis: Redis; keyProducer: MarQSKeyProducer; - env: { id: string; currentConcurrency: number; limit?: number }; + env: { id: string; currentConcurrency: number; limit?: number; reserveConcurrency?: number }; }; /** @@ -72,6 +72,19 @@ export async function setupConcurrency({ redis, keyProducer, env }: SetupConcurr await redis.sadd(envCurrentKey, ...dummyJobs); } + + if (env.reserveConcurrency && env.reserveConcurrency > 0) { + // Set reserved concurrency by adding dummy members to the set + const envReservedKey = keyProducer.envReserveConcurrencyKey(env.id); + + // Add dummy reserved job IDs to simulate reserved concurrency + const dummyJobs = Array.from( + { length: env.reserveConcurrency }, + (_, i) => `dummy-reserved-job-${i}-${Date.now()}` + ); + + await redis.sadd(envReservedKey, ...dummyJobs); + } } /** diff --git a/references/v3-catalog/src/trigger/concurrency.ts b/references/v3-catalog/src/trigger/concurrency.ts index 693b8d4c19..f41d7bd445 100644 --- a/references/v3-catalog/src/trigger/concurrency.ts +++ b/references/v3-catalog/src/trigger/concurrency.ts @@ -17,32 +17,63 @@ export const oneAtATime = task({ }); export const testConcurrency = task({ - id: "test-concurrency", - run: async ({ count = 10, delay = 5000 }: { count: number; delay: number }) => { - logger.info(`Running ${count} tasks`); + id: "test-concurrency-controller", + run: async ({ + count = 10, + delay = 5000, + childDelay = 1000, + }: { + count: number; + delay: number; + childDelay: number; + }) => { + logger.info(`Running ${count} tasks baby`); - await new Promise((resolve) => setTimeout(resolve, 3000)); - - await testConcurrencyChild.batchTrigger( + await testConcurrencyParent.batchTrigger( Array.from({ length: count }).map((_, index) => ({ payload: { delay, + childDelay, }, })) ); logger.info(`All ${count} tasks triggered`); + // wait for about 2 seconds + await new Promise((resolve) => setTimeout(resolve, 2000)); + + // Now trigger the parent task again + await testConcurrencyParent.trigger({ + delay, + childDelay, + }); + return { finished: new Date().toISOString(), }; }, }); +export const testConcurrencyParent = task({ + id: "test-concurrency-parent", + run: async ({ delay = 5000, childDelay = 1000 }: { delay: number; childDelay: number }) => { + logger.info(`Delaying for ${delay}ms`); + + await new Promise((resolve) => setTimeout(resolve, delay)); + + logger.info(`Delay of ${delay}ms completed`); + + return await testConcurrencyChild.triggerAndWait({ + delay: childDelay, + }); + }, +}); + export const testConcurrencyChild = task({ id: "test-concurrency-child", queue: { - concurrencyLimit: 1, + concurrencyLimit: 10, }, run: async ({ delay = 5000 }: { delay: number }) => { logger.info(`Delaying for ${delay}ms`); @@ -56,3 +87,45 @@ export const testConcurrencyChild = task({ }; }, }); + +export const testReserveConcurrencyRecursiveWaits = task({ + id: "test-reserve-concurrency-recursive-waits", + queue: { + concurrencyLimit: 10, + }, + run: async ({ + delay = 5000, + depth = 2, + currentDepth = 0, + }: { + delay: number; + depth: number; + currentDepth?: number; + }) => { + logger.info(`Running task at depth ${currentDepth}`); + + logger.info(`Delaying for ${delay}ms`); + + await new Promise((resolve) => setTimeout(resolve, delay)); + + logger.info(`Delay of ${delay}ms completed`); + + if (currentDepth < depth) { + logger.info(`Triggering child task at depth ${currentDepth + 1}`); + + await testReserveConcurrencyRecursiveWaits.triggerAndWait({ + delay, + depth, + currentDepth: currentDepth + 1, + }); + + logger.info(`Child task at depth ${currentDepth + 1} completed`); + } + + logger.info(`Task at depth ${currentDepth} completed`); + + return { + completedAt: new Date(), + }; + }, +}); From 7061d3acdadd5f39a8a3502b6d33d90b0a006782 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Mon, 10 Feb 2025 17:00:21 +0000 Subject: [PATCH 3/9] child tasks inherit the queue timestamp from their parent tasks to prioritize completing child tasks based on when their parent started --- apps/webapp/app/v3/marqs/index.server.ts | 9 +- .../v3/services/enqueueDelayedRun.server.ts | 3 +- .../app/v3/services/resumeBatchRun.server.ts | 1 + .../services/resumeTaskDependency.server.ts | 2 + .../app/v3/services/triggerTask.server.ts | 12 +- .../migration.sql | 8 ++ .../v3-catalog/src/trigger/concurrency.ts | 107 ++++++++++++++++++ 7 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 internal-packages/database/prisma/migrations/20250210164232_add_queue_timestamp_to_run/migration.sql diff --git a/apps/webapp/app/v3/marqs/index.server.ts b/apps/webapp/app/v3/marqs/index.server.ts index 7fc4a33e6a..60c8d81ed8 100644 --- a/apps/webapp/app/v3/marqs/index.server.ts +++ b/apps/webapp/app/v3/marqs/index.server.ts @@ -174,7 +174,7 @@ export class MarQS { messageId: string, messageData: Record, concurrencyKey?: string, - timestamp?: number + timestamp?: number | Date ) { return await this.#trace( "enqueueMessage", @@ -190,7 +190,12 @@ export class MarQS { data: messageData, queue: messageQueue, concurrencyKey, - timestamp: timestamp ?? Date.now(), + timestamp: + typeof timestamp === "undefined" + ? Date.now() + : typeof timestamp === "number" + ? timestamp + : timestamp.getTime(), messageId, parentQueue, }; diff --git a/apps/webapp/app/v3/services/enqueueDelayedRun.server.ts b/apps/webapp/app/v3/services/enqueueDelayedRun.server.ts index 6306df4765..d85d1d6804 100644 --- a/apps/webapp/app/v3/services/enqueueDelayedRun.server.ts +++ b/apps/webapp/app/v3/services/enqueueDelayedRun.server.ts @@ -94,7 +94,8 @@ export class EnqueueDelayedRunService extends BaseService { environmentId: run.runtimeEnvironment.id, environmentType: run.runtimeEnvironment.type, }, - run.concurrencyKey ?? undefined + run.concurrencyKey ?? undefined, + run.queueTimestamp ?? undefined ); } } diff --git a/apps/webapp/app/v3/services/resumeBatchRun.server.ts b/apps/webapp/app/v3/services/resumeBatchRun.server.ts index 6a794e238c..c4a8180bca 100644 --- a/apps/webapp/app/v3/services/resumeBatchRun.server.ts +++ b/apps/webapp/app/v3/services/resumeBatchRun.server.ts @@ -186,6 +186,7 @@ export class ResumeBatchRunService extends BaseService { dependentTaskAttemptId: dependentTaskAttempt.id, }); + // TODO: use the new priority queue thingie await marqs?.enqueueMessage( environment, dependentRun.queue, diff --git a/apps/webapp/app/v3/services/resumeTaskDependency.server.ts b/apps/webapp/app/v3/services/resumeTaskDependency.server.ts index bdbece9778..b424730414 100644 --- a/apps/webapp/app/v3/services/resumeTaskDependency.server.ts +++ b/apps/webapp/app/v3/services/resumeTaskDependency.server.ts @@ -49,6 +49,8 @@ export class ResumeTaskDependencyService extends BaseService { runId: dependentRun.id, } ); + + // TODO: use the new priority queue thingie await marqs?.enqueueMessage( dependency.taskRun.runtimeEnvironment, dependentRun.queue, diff --git a/apps/webapp/app/v3/services/triggerTask.server.ts b/apps/webapp/app/v3/services/triggerTask.server.ts index 284a14d85b..aed88d5bba 100644 --- a/apps/webapp/app/v3/services/triggerTask.server.ts +++ b/apps/webapp/app/v3/services/triggerTask.server.ts @@ -186,6 +186,7 @@ export class TriggerTaskService extends BaseService { taskIdentifier: true, rootTaskRunId: true, depth: true, + queueTimestamp: true, }, }, }, @@ -242,6 +243,7 @@ export class TriggerTaskService extends BaseService { taskIdentifier: true, rootTaskRunId: true, depth: true, + queueTimestamp: true, }, }, }, @@ -367,6 +369,12 @@ export class TriggerTaskService extends BaseService { ? dependentBatchRun.dependentTaskAttempt.taskRun.depth + 1 : 0; + const queueTimestamp = + dependentAttempt?.taskRun.queueTimestamp ?? + dependentBatchRun?.dependentTaskAttempt?.taskRun.queueTimestamp ?? + delayUntil ?? + new Date(); + const taskRun = await tx.taskRun.create({ data: { status: delayUntil ? "DELAYED" : "PENDING", @@ -394,6 +402,7 @@ export class TriggerTaskService extends BaseService { isTest: body.options?.test ?? false, delayUntil, queuedAt: delayUntil ? undefined : new Date(), + queueTimestamp, maxAttempts: body.options?.maxAttempts, taskEventStore: getTaskEventStore(), ttl, @@ -573,7 +582,8 @@ export class TriggerTaskService extends BaseService { environmentId: environment.id, environmentType: environment.type, }, - body.options?.concurrencyKey + body.options?.concurrencyKey, + run.queueTimestamp ?? undefined ); } diff --git a/internal-packages/database/prisma/migrations/20250210164232_add_queue_timestamp_to_run/migration.sql b/internal-packages/database/prisma/migrations/20250210164232_add_queue_timestamp_to_run/migration.sql new file mode 100644 index 0000000000..b6e7ac1c91 --- /dev/null +++ b/internal-packages/database/prisma/migrations/20250210164232_add_queue_timestamp_to_run/migration.sql @@ -0,0 +1,8 @@ +-- DropIndex +DROP INDEX "SecretStore_key_idx"; + +-- AlterTable +ALTER TABLE "TaskRun" ADD COLUMN "queueTimestamp" TIMESTAMP(3); + +-- CreateIndex +CREATE INDEX "SecretStore_key_idx" ON "SecretStore"("key" text_pattern_ops); diff --git a/references/v3-catalog/src/trigger/concurrency.ts b/references/v3-catalog/src/trigger/concurrency.ts index f41d7bd445..35f5677c38 100644 --- a/references/v3-catalog/src/trigger/concurrency.ts +++ b/references/v3-catalog/src/trigger/concurrency.ts @@ -129,3 +129,110 @@ export const testReserveConcurrencyRecursiveWaits = task({ }; }, }); + +export const testChildTaskPriorityController = task({ + id: "test-child-task-priority-controller", + run: async ({ delay = 5000 }: { delay: number }) => { + logger.info(`Delaying for ${delay}ms`); + + await new Promise((resolve) => setTimeout(resolve, delay)); + + return { + completedAt: new Date(), + }; + }, +}); + +export const testChildTaskPriorityParent = task({ + id: "test-child-task-priority-parent", + run: async ({ delay = 5000 }: { delay: number }) => { + logger.info(`Delaying for ${delay}ms`); + + await new Promise((resolve) => setTimeout(resolve, delay)); + + await testChildTaskPriorityChild.triggerAndWait({ + delay, + }); + + logger.info(`Delay of ${delay}ms completed`); + + return { + completedAt: new Date(), + }; + }, +}); + +export const testChildTaskPriorityChildCreator = task({ + id: "test-child-task-priority-child-creator", + run: async ({ delay = 5000 }: { delay: number }) => { + await testChildTaskPriorityChild.batchTrigger([ + { payload: { delay, propagate: false } }, + { payload: { delay, propagate: false } }, + { payload: { delay, propagate: false } }, + { payload: { delay, propagate: false } }, + ]); + + return { + completedAt: new Date(), + }; + }, +}); + +export const testChildTaskPriorityChild = task({ + id: "test-child-task-priority-child", + queue: { + concurrencyLimit: 1, + }, + run: async ({ delay = 5000, propagate }: { delay: number; propagate?: boolean }) => { + logger.info(`Delaying for ${delay}ms`); + + await new Promise((resolve) => setTimeout(resolve, delay)); + + if (typeof propagate === "undefined" || propagate) { + await testChildTaskPriorityGrandChild.triggerAndWait({ + delay, + }); + } + + logger.info(`Delay of ${delay}ms completed`); + + return { + completedAt: new Date(), + }; + }, +}); + +export const testChildTaskPriorityGrandChildCreator = task({ + id: "test-child-task-priority-grand-child-creator", + run: async ({ delay = 5000 }: { delay: number }) => { + await testChildTaskPriorityGrandChild.batchTrigger([ + { payload: { delay } }, + { payload: { delay } }, + { payload: { delay } }, + ]); + + logger.info(`Delay of ${delay}ms completed`); + + return { + completedAt: new Date(), + }; + }, +}); + +export const testChildTaskPriorityGrandChild = task({ + id: "test-child-task-priority-grandchild", + queue: { + concurrencyLimit: 1, + }, + run: async ({ delay = 5000 }: { delay: number }) => { + logger.info(`Delaying for ${delay}ms`); + + await new Promise((resolve) => setTimeout(resolve, delay)); + + logger.info(`Delay of ${delay}ms completed`); + + return { + completedAt: new Date(), + }; + }, +}); From 0587b5a0e671cdff8266d1af061d39ffe253dd7e Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Tue, 11 Feb 2025 17:57:09 +0000 Subject: [PATCH 4/9] handle reserve concurrency with recursive deadlocks --- .../app/components/runs/v3/BatchStatus.tsx | 11 +- .../v3/BatchListPresenter.server.ts | 2 +- .../app/presenters/v3/SpanPresenter.server.ts | 43 +- apps/webapp/app/v3/eventRepository.server.ts | 31 +- apps/webapp/app/v3/marqs/index.server.ts | 682 +++++++++++++----- .../app/v3/marqs/marqsKeyProducer.server.ts | 4 + apps/webapp/app/v3/marqs/types.ts | 7 + .../app/v3/services/batchTriggerV3.server.ts | 17 +- .../v3/services/enqueueDelayedRun.server.ts | 2 +- .../app/v3/services/enqueueRun.server.ts | 68 ++ docs/queue-concurrency.mdx | 188 ++++- .../migration.sql | 8 + .../database/prisma/schema.prisma | 1 + packages/core/src/v3/errors.ts | 31 + packages/core/src/v3/links.ts | 3 + packages/core/src/v3/schemas/common.ts | 1 + .../v3-catalog/src/trigger/concurrency.ts | 69 +- references/v3-catalog/src/trigger/simple.ts | 172 ++--- references/v3-catalog/trigger.config.ts | 2 +- 19 files changed, 974 insertions(+), 368 deletions(-) create mode 100644 apps/webapp/app/v3/services/enqueueRun.server.ts create mode 100644 internal-packages/database/prisma/migrations/20250211150836_add_aborted_batch_task_run_status/migration.sql diff --git a/apps/webapp/app/components/runs/v3/BatchStatus.tsx b/apps/webapp/app/components/runs/v3/BatchStatus.tsx index b512ac1b3c..c67b1b4016 100644 --- a/apps/webapp/app/components/runs/v3/BatchStatus.tsx +++ b/apps/webapp/app/components/runs/v3/BatchStatus.tsx @@ -1,16 +1,17 @@ -import { CheckCircleIcon } from "@heroicons/react/20/solid"; +import { CheckCircleIcon, XCircleIcon } from "@heroicons/react/20/solid"; import { BatchTaskRunStatus } from "@trigger.dev/database"; import assertNever from "assert-never"; import { Spinner } from "~/components/primitives/Spinner"; import { cn } from "~/utils/cn"; -export const allBatchStatuses = ["PENDING", "COMPLETED"] as const satisfies Readonly< +export const allBatchStatuses = ["PENDING", "COMPLETED", "ABORTED"] as const satisfies Readonly< Array >; const descriptions: Record = { PENDING: "The batch has child runs that have not yet completed.", COMPLETED: "All the batch child runs have finished.", + ABORTED: "The batch was aborted because some child tasks could not be triggered.", }; export function descriptionForBatchStatus(status: BatchTaskRunStatus): string { @@ -50,6 +51,8 @@ export function BatchStatusIcon({ return ; case "COMPLETED": return ; + case "ABORTED": + return ; default: { assertNever(status); } @@ -62,6 +65,8 @@ export function batchStatusColor(status: BatchTaskRunStatus): string { return "text-pending"; case "COMPLETED": return "text-success"; + case "ABORTED": + return "text-error"; default: { assertNever(status); } @@ -74,6 +79,8 @@ export function batchStatusTitle(status: BatchTaskRunStatus): string { return "In progress"; case "COMPLETED": return "Completed"; + case "ABORTED": + return "Aborted"; default: { assertNever(status); } diff --git a/apps/webapp/app/presenters/v3/BatchListPresenter.server.ts b/apps/webapp/app/presenters/v3/BatchListPresenter.server.ts index 2317f68a14..ef7d3f2dd4 100644 --- a/apps/webapp/app/presenters/v3/BatchListPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/BatchListPresenter.server.ts @@ -190,7 +190,7 @@ WHERE throw new Error(`Environment not found for Batch ${batch.id}`); } - const hasFinished = batch.status === "COMPLETED"; + const hasFinished = batch.status !== "PENDING"; return { id: batch.id, diff --git a/apps/webapp/app/presenters/v3/SpanPresenter.server.ts b/apps/webapp/app/presenters/v3/SpanPresenter.server.ts index 010efcba12..0f6bb9de4b 100644 --- a/apps/webapp/app/presenters/v3/SpanPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/SpanPresenter.server.ts @@ -118,6 +118,9 @@ export class SpanPresenter extends BasePresenter { metadata: true, metadataType: true, maxAttempts: true, + output: true, + outputType: true, + error: true, project: { include: { organization: true, @@ -162,31 +165,13 @@ export class SpanPresenter extends BasePresenter { const isFinished = isFinalRunStatus(run.status); - const finishedAttempt = isFinished - ? await this._replica.taskRunAttempt.findFirst({ - select: { - output: true, - outputType: true, - error: true, - }, - where: { - status: { in: FINAL_ATTEMPT_STATUSES }, - taskRunId: run.id, - }, - orderBy: { - createdAt: "desc", - }, - }) - : null; - - const output = - finishedAttempt === null - ? undefined - : finishedAttempt.outputType === "application/store" - ? `/resources/packets/${run.runtimeEnvironment.id}/${finishedAttempt.output}` - : typeof finishedAttempt.output !== "undefined" && finishedAttempt.output !== null - ? await prettyPrintPacket(finishedAttempt.output, finishedAttempt.outputType ?? undefined) - : undefined; + const output = !isFinished + ? undefined + : run.outputType === "application/store" + ? `/resources/packets/${run.runtimeEnvironment.id}/${run.output}` + : typeof run.output !== "undefined" && run.output !== null + ? await prettyPrintPacket(run.output, run.outputType ?? undefined) + : undefined; const payload = run.payloadType === "application/store" @@ -196,14 +181,14 @@ export class SpanPresenter extends BasePresenter { : undefined; let error: TaskRunError | undefined = undefined; - if (finishedAttempt?.error) { - const result = TaskRunError.safeParse(finishedAttempt.error); + if (run?.error) { + const result = TaskRunError.safeParse(run.error); if (result.success) { error = result.data; } else { error = { type: "CUSTOM_ERROR", - raw: JSON.stringify(finishedAttempt.error), + raw: JSON.stringify(run.error), }; } } @@ -300,7 +285,7 @@ export class SpanPresenter extends BasePresenter { payload, payloadType: run.payloadType, output, - outputType: finishedAttempt?.outputType ?? "application/json", + outputType: run?.outputType ?? "application/json", error, relationships: { root: run.rootTaskRun diff --git a/apps/webapp/app/v3/eventRepository.server.ts b/apps/webapp/app/v3/eventRepository.server.ts index bed3871fc5..911e7435d1 100644 --- a/apps/webapp/app/v3/eventRepository.server.ts +++ b/apps/webapp/app/v3/eventRepository.server.ts @@ -95,6 +95,8 @@ export type EventBuilder = { traceId: string; spanId: string; setAttribute: SetAttribute; + stop: () => void; + failWithError: (error: TaskRunError) => void; }; export type EventRepoConfig = { @@ -916,6 +918,9 @@ export class EventRepository { ] : []; + let isStopped = false; + let failedWithError: TaskRunError | undefined; + const eventBuilder = { traceId, spanId, @@ -933,10 +938,20 @@ export class EventRepository { } } }, + stop: () => { + isStopped = true; + }, + failWithError: (error: TaskRunError) => { + failedWithError = error; + }, }; const result = await callback(eventBuilder, traceContext, propagatedContext?.traceparent); + if (isStopped) { + return result; + } + const duration = process.hrtime.bigint() - start; const metadata = { @@ -970,13 +985,14 @@ export class EventRepository { parentId, tracestate, duration: options.incomplete ? 0 : duration, - isPartial: options.incomplete, + isPartial: failedWithError ? false : options.incomplete, + isError: !!failedWithError, message: message, serviceName: "api server", serviceNamespace: "trigger.dev", level: "TRACE", kind: options.kind, - status: "OK", + status: failedWithError ? "ERROR" : "OK", startTime, environmentId: options.environment.id, environmentType: options.environment.type, @@ -1004,6 +1020,17 @@ export class EventRepository { payload: options.attributes.payload, payloadType: options.attributes.payloadType, idempotencyKey: options.attributes.idempotencyKey, + events: failedWithError + ? [ + { + name: "exception", + time: new Date(), + properties: { + exception: createExceptionPropertiesFromError(failedWithError), + }, + }, + ] + : undefined, }; if (options.immediate) { diff --git a/apps/webapp/app/v3/marqs/index.server.ts b/apps/webapp/app/v3/marqs/index.server.ts index 60c8d81ed8..d26a766161 100644 --- a/apps/webapp/app/v3/marqs/index.server.ts +++ b/apps/webapp/app/v3/marqs/index.server.ts @@ -24,6 +24,7 @@ import { AsyncWorker } from "./asyncWorker.server"; import { FairDequeuingStrategy } from "./fairDequeuingStrategy.server"; import { MarQSShortKeyProducer } from "./marqsKeyProducer.server"; import { + EnqueueMessageReserveConcurrencyOptions, MarQSFairDequeueStrategy, MarQSKeyProducer, MessagePayload, @@ -174,7 +175,8 @@ export class MarQS { messageId: string, messageData: Record, concurrencyKey?: string, - timestamp?: number | Date + timestamp?: number | Date, + reserve?: EnqueueMessageReserveConcurrencyOptions ) { return await this.#trace( "enqueueMessage", @@ -207,9 +209,18 @@ export class MarQS { [SemanticAttributes.PARENT_QUEUE]: parentQueue, }); - await this.#callEnqueueMessage(messagePayload); + if (reserve) { + span.setAttribute("reserve_message_id", reserve.messageId); + span.setAttribute("reserve_recursive_queue", reserve.recursiveQueue); + } + + const result = await this.#callEnqueueMessage(messagePayload, reserve); + + if (result) { + await this.options.subscriber?.messageEnqueued(messagePayload); + } - await this.options.subscriber?.messageEnqueued(messagePayload); + return result; }, { kind: SpanKind.PRODUCER, @@ -518,60 +529,6 @@ export class MarQS { ); } - public async reserveConcurrency(messageId: string) { - return this.#trace( - "reserveConcurrency", - async (span) => { - span.setAttributes({ - [SemanticAttributes.MESSAGE_ID]: messageId, - }); - - const message = await this.readMessage(messageId); - - if (!message) { - logger.log(`[${this.name}].reserveConcurrency() message not found`, { - messageId, - service: this.name, - }); - return; - } - - span.setAttributes({ - [SemanticAttributes.QUEUE]: message.queue, - [SemanticAttributes.MESSAGE_ID]: message.messageId, - [SemanticAttributes.CONCURRENCY_KEY]: message.concurrencyKey, - [SemanticAttributes.PARENT_QUEUE]: message.parentQueue, - }); - - const reserveConcurrencyKey = this.keys.envReserveConcurrencyKeyFromQueue(message.queue); - const envConcurrencyLimitKey = this.keys.envConcurrencyLimitKeyFromQueue(message.queue); - - logger.debug("Calling reserveConcurrency", { - messageId, - queue: message.queue, - reserveConcurrencyKey, - envConcurrencyLimitKey, - service: this.name, - }); - - return this.redis.reserveConcurrency( - reserveConcurrencyKey, - envConcurrencyLimitKey, - message.messageId, - String(this.options.defaultEnvConcurrency) - ); - }, - { - kind: SpanKind.CONSUMER, - attributes: { - [SEMATTRS_MESSAGING_OPERATION]: "reserveConcurrency", - [SEMATTRS_MESSAGE_ID]: messageId, - [SEMATTRS_MESSAGING_SYSTEM]: "marqs", - }, - } - ); - } - async #trace( name: string, fn: (span: Span) => Promise, @@ -898,31 +855,204 @@ export class MarQS { }); } - async #callEnqueueMessage(message: MessagePayload) { - const concurrencyKey = this.keys.currentConcurrencyKeyFromQueue(message.queue); - const envConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(message.queue); + async #callEnqueueMessage( + message: MessagePayload, + reserve?: EnqueueMessageReserveConcurrencyOptions + ) { + const queueKey = message.queue; + const parentQueueKey = message.parentQueue; + const messageKey = this.keys.messageKey(message.messageId); + const queueCurrentConcurrencyKey = this.keys.currentConcurrencyKeyFromQueue(message.queue); + const queueReserveConcurrencyKey = this.keys.queueReserveConcurrencyKeyFromQueue(message.queue); + const envCurrentConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(message.queue); const envReserveConcurrencyKey = this.keys.envReserveConcurrencyKeyFromQueue(message.queue); + const envQueueKey = this.keys.envQueueKeyFromQueue(message.queue); - logger.debug("Calling enqueueMessage", { - messagePayload: message, - concurrencyKey, - envConcurrencyKey, - service: this.name, - }); + const queueName = message.queue; + const messageId = message.messageId; + const messageData = JSON.stringify(message); + const messageScore = String(message.timestamp); - return this.redis.enqueueMessage( - message.queue, - message.parentQueue, - this.keys.messageKey(message.messageId), - concurrencyKey, - envConcurrencyKey, - envReserveConcurrencyKey, - this.keys.envQueueKeyFromQueue(message.queue), - message.queue, - message.messageId, - JSON.stringify(message), - String(message.timestamp) - ); + if (!reserve) { + logger.debug("Calling enqueueMessage", { + service: this.name, + queueKey, + parentQueueKey, + messageKey, + queueCurrentConcurrencyKey, + queueReserveConcurrencyKey, + envCurrentConcurrencyKey, + envReserveConcurrencyKey, + envQueueKey, + queueName, + messageId, + messageData, + messageScore, + }); + + const result = await this.redis.enqueueMessage( + queueKey, + parentQueueKey, + messageKey, + queueCurrentConcurrencyKey, + queueReserveConcurrencyKey, + envCurrentConcurrencyKey, + envReserveConcurrencyKey, + envQueueKey, + queueName, + messageId, + messageData, + messageScore + ); + + logger.debug("enqueueMessage result", { + service: this.name, + queueKey, + parentQueueKey, + messageKey, + queueCurrentConcurrencyKey, + queueReserveConcurrencyKey, + envCurrentConcurrencyKey, + envReserveConcurrencyKey, + envQueueKey, + queueName, + messageId, + messageData, + messageScore, + result, + }); + + return true; + } + + const envConcurrencyLimitKey = this.keys.envConcurrencyLimitKeyFromQueue(message.queue); + const reserveMessageId = reserve.messageId; + const defaultEnvConcurrencyLimit = String(this.options.defaultEnvConcurrency); + + if (!reserve.recursiveQueue) { + logger.debug("Calling enqueueMessageWithReservingConcurrency", { + service: this.name, + queueKey, + parentQueueKey, + messageKey, + queueCurrentConcurrencyKey, + queueReserveConcurrencyKey, + envCurrentConcurrencyKey, + envReserveConcurrencyKey, + envConcurrencyLimitKey, + envQueueKey, + queueName, + messageId, + messageData, + messageScore, + reserveMessageId, + defaultEnvConcurrencyLimit, + }); + + const result = await this.redis.enqueueMessageWithReservingConcurrency( + queueKey, + parentQueueKey, + messageKey, + queueCurrentConcurrencyKey, + queueReserveConcurrencyKey, + envCurrentConcurrencyKey, + envReserveConcurrencyKey, + envConcurrencyLimitKey, + envQueueKey, + queueName, + messageId, + messageData, + messageScore, + reserveMessageId, + defaultEnvConcurrencyLimit + ); + + logger.debug("enqueueMessageWithReservingConcurrency result", { + service: this.name, + queueKey, + parentQueueKey, + messageKey, + queueCurrentConcurrencyKey, + queueReserveConcurrencyKey, + envCurrentConcurrencyKey, + envReserveConcurrencyKey, + envConcurrencyLimitKey, + envQueueKey, + queueName, + messageId, + messageData, + messageScore, + reserveMessageId, + defaultEnvConcurrencyLimit, + result, + }); + + return true; + } else { + const queueConcurrencyLimitKey = this.keys.concurrencyLimitKeyFromQueue(message.queue); + + logger.debug("Calling enqueueMessageWithReservingConcurrencyForRecursiveQueue", { + service: this.name, + queueKey, + parentQueueKey, + messageKey, + queueCurrentConcurrencyKey, + queueReserveConcurrencyKey, + queueConcurrencyLimitKey, + envCurrentConcurrencyKey, + envReserveConcurrencyKey, + envConcurrencyLimitKey, + envQueueKey, + queueName, + messageId, + messageData, + messageScore, + reserveMessageId, + defaultEnvConcurrencyLimit, + }); + + const result = await this.redis.enqueueMessageWithReservingConcurrencyOnRecursiveQueue( + queueKey, + parentQueueKey, + messageKey, + queueCurrentConcurrencyKey, + queueReserveConcurrencyKey, + queueConcurrencyLimitKey, + envCurrentConcurrencyKey, + envReserveConcurrencyKey, + envConcurrencyLimitKey, + envQueueKey, + queueName, + messageId, + messageData, + messageScore, + reserveMessageId, + defaultEnvConcurrencyLimit + ); + + logger.debug("enqueueMessageWithReservingConcurrencyOnRecursiveQueue result", { + service: this.name, + queueKey, + parentQueueKey, + messageKey, + queueCurrentConcurrencyKey, + queueReserveConcurrencyKey, + queueConcurrencyLimitKey, + envCurrentConcurrencyKey, + envReserveConcurrencyKey, + envConcurrencyLimitKey, + envQueueKey, + queueName, + messageId, + messageData, + messageScore, + reserveMessageId, + defaultEnvConcurrencyLimit, + result, + }); + + return !!result; + } } async #callDequeueMessage({ @@ -937,6 +1067,7 @@ export class MarQS { const envConcurrencyLimitKey = this.keys.envConcurrencyLimitKeyFromQueue(messageQueue); const envCurrentConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue); const envReserveConcurrencyKey = this.keys.envReserveConcurrencyKeyFromQueue(messageQueue); + const queueReserveConcurrencyKey = this.keys.queueReserveConcurrencyKeyFromQueue(messageQueue); const result = await this.redis.dequeueMessage( messageQueue, @@ -944,6 +1075,7 @@ export class MarQS { concurrencyLimitKey, envConcurrencyLimitKey, currentConcurrencyKey, + queueReserveConcurrencyKey, envCurrentConcurrencyKey, envReserveConcurrencyKey, this.keys.envQueueKeyFromQueue(messageQueue), @@ -996,6 +1128,7 @@ export class MarQS { const concurrencyKey = this.keys.currentConcurrencyKeyFromQueue(messageQueue); const envConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue); const envReserveConcurrencyKey = this.keys.envReserveConcurrencyKeyFromQueue(messageQueue); + const queueReserveConcurrencyKey = this.keys.queueReserveConcurrencyKeyFromQueue(messageQueue); logger.debug("Calling acknowledgeMessage", { messageKey, @@ -1012,6 +1145,7 @@ export class MarQS { messageKey, messageQueue, concurrencyKey, + queueReserveConcurrencyKey, envConcurrencyKey, envReserveConcurrencyKey, this.keys.envQueueKeyFromQueue(messageQueue), @@ -1111,15 +1245,16 @@ export class MarQS { #registerCommands() { this.redis.defineCommand("enqueueMessage", { - numberOfKeys: 7, + numberOfKeys: 8, lua: ` -local queue = KEYS[1] -local parentQueue = KEYS[2] +local queueKey = KEYS[1] +local parentQueueKey = KEYS[2] local messageKey = KEYS[3] -local concurrencyKey = KEYS[4] -local envCurrentConcurrencyKey = KEYS[5] -local envReserveConcurrencyKey = KEYS[6] -local envQueue = KEYS[7] +local queueCurrentConcurrencyKey = KEYS[4] +local queueReserveConcurrencyKey = KEYS[5] +local envCurrentConcurrencyKey = KEYS[6] +local envReserveConcurrencyKey = KEYS[7] +local envQueueKey = KEYS[8] local queueName = ARGV[1] local messageId = ARGV[2] @@ -1130,37 +1265,171 @@ local messageScore = ARGV[4] redis.call('SET', messageKey, messageData) -- Add the message to the queue -redis.call('ZADD', queue, messageScore, messageId) +redis.call('ZADD', queueKey, messageScore, messageId) -- Add the message to the env queue -redis.call('ZADD', envQueue, messageScore, messageId) +redis.call('ZADD', envQueueKey, messageScore, messageId) -- Rebalance the parent queue -local earliestMessage = redis.call('ZRANGE', queue, 0, 0, 'WITHSCORES') +local earliestMessage = redis.call('ZRANGE', queueKey, 0, 0, 'WITHSCORES') if #earliestMessage == 0 then - redis.call('ZREM', parentQueue, queueName) + redis.call('ZREM', parentQueueKey, queueName) else - redis.call('ZADD', parentQueue, earliestMessage[2], queueName) + redis.call('ZADD', parentQueueKey, earliestMessage[2], queueName) end -- Update the concurrency keys -redis.call('SREM', concurrencyKey, messageId) +redis.call('SREM', queueCurrentConcurrencyKey, messageId) redis.call('SREM', envCurrentConcurrencyKey, messageId) redis.call('SREM', envReserveConcurrencyKey, messageId) +redis.call('SREM', queueReserveConcurrencyKey, messageId) + +return true + `, + }); + + this.redis.defineCommand("enqueueMessageWithReservingConcurrency", { + numberOfKeys: 9, + lua: ` +local queueKey = KEYS[1] +local parentQueueKey = KEYS[2] +local messageKey = KEYS[3] +local queueCurrentConcurrencyKey = KEYS[4] +local queueReserveConcurrencyKey = KEYS[5] +local envCurrentConcurrencyKey = KEYS[6] +local envReserveConcurrencyKey = KEYS[7] +local envConcurrencyLimitKey = KEYS[8] +local envQueueKey = KEYS[9] + +local queueName = ARGV[1] +local messageId = ARGV[2] +local messageData = ARGV[3] +local messageScore = ARGV[4] +local reserveMessageId = ARGV[5] +local defaultEnvConcurrencyLimit = ARGV[6] + +-- Write the message to the message key +redis.call('SET', messageKey, messageData) + +-- Add the message to the queue +redis.call('ZADD', queueKey, messageScore, messageId) + +-- Add the message to the env queue +redis.call('ZADD', envQueueKey, messageScore, messageId) + +-- Rebalance the parent queue +local earliestMessage = redis.call('ZRANGE', queueKey, 0, 0, 'WITHSCORES') +if #earliestMessage == 0 then + redis.call('ZREM', parentQueueKey, queueName) +else + redis.call('ZADD', parentQueueKey, earliestMessage[2], queueName) +end + +-- Update the concurrency keys +redis.call('SREM', queueCurrentConcurrencyKey, messageId) +redis.call('SREM', envCurrentConcurrencyKey, messageId) +redis.call('SREM', envReserveConcurrencyKey, messageId) +redis.call('SREM', queueReserveConcurrencyKey, messageId) + +-- Reserve the concurrency for the message +local envReserveConcurrencyLimit = tonumber(redis.call('GET', envConcurrencyLimitKey) or defaultEnvConcurrencyLimit) +-- Count the number of messages in the reserve concurrency set +local envReserveConcurrency = tonumber(redis.call('SCARD', envReserveConcurrencyKey) or '0') + +-- If there is space, add the messaageId to the env reserve concurrency set +if envReserveConcurrency < envReserveConcurrencyLimit then + redis.call('SADD', envReserveConcurrencyKey, reserveMessageId) +end + +return true + `, + }); + + this.redis.defineCommand("enqueueMessageWithReservingConcurrencyOnRecursiveQueue", { + numberOfKeys: 10, + lua: ` +local queueKey = KEYS[1] +local parentQueueKey = KEYS[2] +local messageKey = KEYS[3] +local queueCurrentConcurrencyKey = KEYS[4] +local queueReserveConcurrencyKey = KEYS[5] +local queueConcurrencyLimitKey = KEYS[6] +local envCurrentConcurrencyKey = KEYS[7] +local envReserveConcurrencyKey = KEYS[8] +local envConcurrencyLimitKey = KEYS[9] +local envQueueKey = KEYS[10] + +local queueName = ARGV[1] +local messageId = ARGV[2] +local messageData = ARGV[3] +local messageScore = ARGV[4] +local reserveMessageId = ARGV[5] +local defaultEnvConcurrencyLimit = ARGV[6] + +-- Get the env reserve concurrency limit because we need it to calculate the max reserve concurrency +-- for the specific queue +local envReserveConcurrencyLimit = tonumber(redis.call('GET', envConcurrencyLimitKey) or defaultEnvConcurrencyLimit) + +-- Count the number of messages in the queue reserve concurrency set +local queueReserveConcurrency = tonumber(redis.call('SCARD', queueReserveConcurrencyKey) or '0') +local queueConcurrencyLimit = tonumber(redis.call('GET', queueConcurrencyLimitKey) or '1000000') + +local queueReserveConcurrencyLimit = math.min(queueConcurrencyLimit, envReserveConcurrencyLimit) + +-- If we cannot add the reserve concurrency, then we have to return false +if queueReserveConcurrency >= queueReserveConcurrencyLimit then + return false +end + +-- Write the message to the message key +redis.call('SET', messageKey, messageData) + +-- Add the message to the queue +redis.call('ZADD', queueKey, messageScore, messageId) + +-- Add the message to the env queue +redis.call('ZADD', envQueueKey, messageScore, messageId) + +-- Rebalance the parent queue +local earliestMessage = redis.call('ZRANGE', queueKey, 0, 0, 'WITHSCORES') +if #earliestMessage == 0 then + redis.call('ZREM', parentQueueKey, queueName) +else + redis.call('ZADD', parentQueueKey, earliestMessage[2], queueName) +end + +-- Update the concurrency keys +redis.call('SREM', queueCurrentConcurrencyKey, messageId) +redis.call('SREM', envCurrentConcurrencyKey, messageId) +redis.call('SREM', envReserveConcurrencyKey, messageId) +redis.call('SREM', queueReserveConcurrencyKey, messageId) + +-- Count the number of messages in the env reserve concurrency set +local envReserveConcurrency = tonumber(redis.call('SCARD', envReserveConcurrencyKey) or '0') + +-- If there is space, add the messaageId to the env reserve concurrency set +if envReserveConcurrency < envReserveConcurrencyLimit then + redis.call('SADD', envReserveConcurrencyKey, reserveMessageId) +end + +redis.call('SADD', queueReserveConcurrencyKey, reserveMessageId) + +return true `, }); this.redis.defineCommand("dequeueMessage", { - numberOfKeys: 8, + numberOfKeys: 9, lua: ` local childQueue = KEYS[1] local parentQueue = KEYS[2] local concurrencyLimitKey = KEYS[3] local envConcurrencyLimitKey = KEYS[4] local currentConcurrencyKey = KEYS[5] -local envCurrentConcurrencyKey = KEYS[6] -local envReserveConcurrencyKey = KEYS[7] -local envQueueKey = KEYS[8] +local queueReserveConcurrencyKey = KEYS[6] +local envCurrentConcurrencyKey = KEYS[7] +local envReserveConcurrencyKey = KEYS[8] +local envQueueKey = KEYS[9] local childQueueName = ARGV[1] local currentTime = tonumber(ARGV[2]) @@ -1179,9 +1448,11 @@ end -- Check current queue concurrency against the limit local currentConcurrency = tonumber(redis.call('SCARD', currentConcurrencyKey) or '0') local concurrencyLimit = math.min(tonumber(redis.call('GET', concurrencyLimitKey) or '1000000'), envConcurrencyLimit) +local queueReserveConcurrency = tonumber(redis.call('SCARD', queueReserveConcurrencyKey) or '0') +local totalQueueConcurrencyLimit = concurrencyLimit + queueReserveConcurrency -- Check condition only if concurrencyLimit exists -if currentConcurrency >= concurrencyLimit then +if currentConcurrency >= totalQueueConcurrencyLimit then return nil end @@ -1204,6 +1475,9 @@ redis.call('SADD', envCurrentConcurrencyKey, messageId) -- Remove the message from the reserve concurrency set redis.call('SREM', envReserveConcurrencyKey, messageId) +-- Remove the message from the queue reserve concurrency set +redis.call('SREM', queueReserveConcurrencyKey, messageId) + -- Rebalance the parent queue local earliestMessage = redis.call('ZRANGE', childQueue, 0, 0, 'WITHSCORES') if #earliestMessage == 0 then @@ -1236,15 +1510,16 @@ redis.call('SET', messageKey, messageData, 'GET') }); this.redis.defineCommand("acknowledgeMessage", { - numberOfKeys: 7, + numberOfKeys: 8, lua: ` local parentQueue = KEYS[1] local messageKey = KEYS[2] local messageQueue = KEYS[3] local concurrencyKey = KEYS[4] -local envCurrentConcurrencyKey = KEYS[5] -local envReserveConcurrencyKey = KEYS[6] -local envQueueKey = KEYS[7] +local queueReserveConcurrencyKey = KEYS[5] +local envCurrentConcurrencyKey = KEYS[6] +local envReserveConcurrencyKey = KEYS[7] +local envQueueKey = KEYS[8] -- Args: messageId, messageQueueName local messageId = ARGV[1] @@ -1262,6 +1537,9 @@ redis.call('ZREM', envQueueKey, messageId) -- Remove the message from the reserve concurrency set redis.call('SREM', envReserveConcurrencyKey, messageId) +-- Remove the message from the queue reserve concurrency set +redis.call('SREM', queueReserveConcurrencyKey, messageId) + -- Rebalance the parent queue local earliestMessage = redis.call('ZRANGE', messageQueue, 0, 0, 'WITHSCORES') if #earliestMessage == 0 then @@ -1317,26 +1595,6 @@ end `, }); - this.redis.defineCommand("reserveConcurrency", { - numberOfKeys: 2, - lua: ` -local reserveConcurrencyKey = KEYS[1] -local envConcurrencyLimitKey = KEYS[2] - -local messageId = ARGV[1] -local defaultEnvConcurrencyLimit = ARGV[2] - -local envConcurrencyLimit = tonumber(redis.call('GET', envConcurrencyLimitKey) or defaultEnvConcurrencyLimit) --- Count the number of messages in the reserve concurrency set -local reserveConcurrency = tonumber(redis.call('SCARD', reserveConcurrencyKey) or '0') - --- If there is space, add the messaageId to the reserve concurrency set -if reserveConcurrency < envConcurrencyLimit then - redis.call('SADD', reserveConcurrencyKey, messageId) -end -`, - }); - this.redis.defineCommand("updateGlobalConcurrencyLimits", { numberOfKeys: 1, lua: ` @@ -1383,19 +1641,59 @@ end declare module "ioredis" { interface RedisCommander { enqueueMessage( - queue: string, - parentQueue: string, + queueKey: string, + parentQueueKey: string, messageKey: string, - concurrencyKey: string, - envConcurrencyKey: string, + queueCurrentConcurrencyKey: string, + queueReserveConcurrencyKey: string, + envCurrentConcurrencyKey: string, envReserveConcurrencyKey: string, - envQueue: string, + envQueueKey: string, queueName: string, messageId: string, messageData: string, messageScore: string, - callback?: Callback - ): Result; + callback?: Callback + ): Result; + + enqueueMessageWithReservingConcurrency( + queueKey: string, + parentQueueKey: string, + messageKey: string, + queueCurrentConcurrencyKey: string, + queueReserveConcurrencyKey: string, + envCurrentConcurrencyKey: string, + envReserveConcurrencyKey: string, + envConcurrencyLimitKey: string, + envQueueKey: string, + queueName: string, + messageId: string, + messageData: string, + messageScore: string, + reserveMessageId: string, + defaultEnvConcurrencyLimit: string, + callback?: Callback + ): Result; + + enqueueMessageWithReservingConcurrencyOnRecursiveQueue( + queueKey: string, + parentQueueKey: string, + messageKey: string, + queueCurrentConcurrencyKey: string, + queueReserveConcurrencyKey: string, + queueConcurrencyLimitKey: string, + envCurrentConcurrencyKey: string, + envReserveConcurrencyKey: string, + envConcurrencyLimitKey: string, + envQueueKey: string, + queueName: string, + messageId: string, + messageData: string, + messageScore: string, + reserveMessageId: string, + defaultEnvConcurrencyLimit: string, + callback?: Callback + ): Result; dequeueMessage( childQueue: string, @@ -1403,6 +1701,7 @@ declare module "ioredis" { concurrencyLimitKey: string, envConcurrencyLimitKey: string, currentConcurrencyKey: string, + queueReserveConcurrencyKey: string, envCurrentConcurrencyKey: string, envReserveConcurrencyKey: string, envQueueKey: string, @@ -1423,6 +1722,7 @@ declare module "ioredis" { messageKey: string, messageQueue: string, concurrencyKey: string, + queueReserveConcurrencyKey: string, envConcurrencyKey: string, envReserveConcurrencyKey: string, envQueueKey: string, @@ -1446,14 +1746,6 @@ declare module "ioredis" { callback?: Callback ): Result; - reserveConcurrency( - reserveConcurrencyKey: string, - envConcurrencyLimitKey: string, - messageId: string, - defaultEnvConcurrencyLimit: string, - callback?: Callback - ): Result; - updateGlobalConcurrencyLimits( envConcurrencyLimitKey: string, envConcurrencyLimit: string, @@ -1473,65 +1765,63 @@ declare module "ioredis" { export const marqs = singleton("marqs", getMarQSClient); function getMarQSClient() { - if (env.V3_ENABLED) { - if (env.REDIS_HOST && env.REDIS_PORT) { - const redisOptions = { - keyPrefix: KEY_PREFIX, - port: env.REDIS_PORT, - host: env.REDIS_HOST, - username: env.REDIS_USERNAME, - password: env.REDIS_PASSWORD, - enableAutoPipelining: true, - ...(env.REDIS_TLS_DISABLED === "true" ? {} : { tls: {} }), - }; - - const redis = new Redis(redisOptions); - const keysProducer = new MarQSShortKeyProducer(KEY_PREFIX); - - return new MarQS({ - name: "marqs", - tracer: trace.getTracer("marqs"), - keysProducer, - visibilityTimeoutStrategy: new V3LegacyRunEngineWorkerVisibilityTimeout(), - queuePriorityStrategy: new FairDequeuingStrategy({ - tracer: tracer, - redis, - parentQueueLimit: env.MARQS_SHARED_QUEUE_LIMIT, - keys: keysProducer, - defaultEnvConcurrency: env.DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT, - biases: { - concurrencyLimitBias: env.MARQS_CONCURRENCY_LIMIT_BIAS, - availableCapacityBias: env.MARQS_AVAILABLE_CAPACITY_BIAS, - queueAgeRandomization: env.MARQS_QUEUE_AGE_RANDOMIZATION_BIAS, - }, - reuseSnapshotCount: env.MARQS_REUSE_SNAPSHOT_COUNT, - maximumEnvCount: env.MARQS_MAXIMUM_ENV_COUNT, - }), - envQueuePriorityStrategy: new FairDequeuingStrategy({ - tracer: tracer, - redis, - parentQueueLimit: env.MARQS_DEV_QUEUE_LIMIT, - keys: keysProducer, - defaultEnvConcurrency: env.DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT, - biases: { - concurrencyLimitBias: 0.0, - availableCapacityBias: 0.0, - queueAgeRandomization: 0.1, - }, - }), - workers: 1, - redis, - defaultEnvConcurrency: env.DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT, - defaultOrgConcurrency: env.DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT, - visibilityTimeoutInMs: env.MARQS_VISIBILITY_TIMEOUT_MS, - enableRebalancing: !env.MARQS_DISABLE_REBALANCING, - maximumNackCount: env.MARQS_MAXIMUM_NACK_COUNT, - subscriber: concurrencyTracker, - }); - } else { - console.warn( - "Could not initialize MarQS because process.env.REDIS_HOST and process.env.REDIS_PORT are required to be set. Trigger.dev v3 will not work without this." - ); - } + if (!env.REDIS_HOST || !env.REDIS_PORT) { + throw new Error( + "Could not initialize Trigger.dev because process.env.REDIS_HOST and process.env.REDIS_PORT are required to be set." + ); } + + const redisOptions = { + keyPrefix: KEY_PREFIX, + port: env.REDIS_PORT, + host: env.REDIS_HOST, + username: env.REDIS_USERNAME, + password: env.REDIS_PASSWORD, + enableAutoPipelining: true, + ...(env.REDIS_TLS_DISABLED === "true" ? {} : { tls: {} }), + }; + + const redis = new Redis(redisOptions); + const keysProducer = new MarQSShortKeyProducer(KEY_PREFIX); + + return new MarQS({ + name: "marqs", + tracer: trace.getTracer("marqs"), + keysProducer, + visibilityTimeoutStrategy: new V3LegacyRunEngineWorkerVisibilityTimeout(), + queuePriorityStrategy: new FairDequeuingStrategy({ + tracer: tracer, + redis, + parentQueueLimit: env.MARQS_SHARED_QUEUE_LIMIT, + keys: keysProducer, + defaultEnvConcurrency: env.DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT, + biases: { + concurrencyLimitBias: env.MARQS_CONCURRENCY_LIMIT_BIAS, + availableCapacityBias: env.MARQS_AVAILABLE_CAPACITY_BIAS, + queueAgeRandomization: env.MARQS_QUEUE_AGE_RANDOMIZATION_BIAS, + }, + reuseSnapshotCount: env.MARQS_REUSE_SNAPSHOT_COUNT, + maximumEnvCount: env.MARQS_MAXIMUM_ENV_COUNT, + }), + envQueuePriorityStrategy: new FairDequeuingStrategy({ + tracer: tracer, + redis, + parentQueueLimit: env.MARQS_DEV_QUEUE_LIMIT, + keys: keysProducer, + defaultEnvConcurrency: env.DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT, + biases: { + concurrencyLimitBias: 0.0, + availableCapacityBias: 0.0, + queueAgeRandomization: 0.1, + }, + }), + workers: 1, + redis, + defaultEnvConcurrency: env.DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT, + defaultOrgConcurrency: env.DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT, + visibilityTimeoutInMs: env.MARQS_VISIBILITY_TIMEOUT_MS, + enableRebalancing: !env.MARQS_DISABLE_REBALANCING, + maximumNackCount: env.MARQS_MAXIMUM_NACK_COUNT, + subscriber: concurrencyTracker, + }); } diff --git a/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts b/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts index 2191f16aec..bdead92785 100644 --- a/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts +++ b/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts @@ -99,6 +99,10 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { return `${queue}:${constants.CURRENT_CONCURRENCY_PART}`; } + queueReserveConcurrencyKeyFromQueue(queue: string) { + return `${queue}:${constants.RESERVE_CONCURRENCY_PART}`; + } + currentConcurrencyKey( env: AuthenticatedEnvironment, queue: string, diff --git a/apps/webapp/app/v3/marqs/types.ts b/apps/webapp/app/v3/marqs/types.ts index a283dc3cfd..03a270c92b 100644 --- a/apps/webapp/app/v3/marqs/types.ts +++ b/apps/webapp/app/v3/marqs/types.ts @@ -38,6 +38,8 @@ export interface MarQSKeyProducer { stripKeyPrefix(key: string): string; orgIdFromQueue(queue: string): string; envIdFromQueue(queue: string): string; + + queueReserveConcurrencyKeyFromQueue(queue: string): string; } export interface MarQSFairDequeueStrategy { @@ -71,3 +73,8 @@ export interface VisibilityTimeoutStrategy { heartbeat(messageId: string, timeoutInMs: number): Promise; cancelHeartbeat(messageId: string): Promise; } + +export type EnqueueMessageReserveConcurrencyOptions = { + messageId: string; + recursiveQueue: boolean; +}; diff --git a/apps/webapp/app/v3/services/batchTriggerV3.server.ts b/apps/webapp/app/v3/services/batchTriggerV3.server.ts index fdbc8d9c64..4ffd59aacc 100644 --- a/apps/webapp/app/v3/services/batchTriggerV3.server.ts +++ b/apps/webapp/app/v3/services/batchTriggerV3.server.ts @@ -456,18 +456,17 @@ export class BatchTriggerV3Service extends BaseService { error: result.error, }); - await this.#enqueueBatchTaskRun({ - batchId: batch.id, - processingId: "0", - range: { - start: result.workingIndex, - count: PROCESSING_BATCH_SIZE, + await this._prisma.batchTaskRun.update({ + where: { + id: batch.id, + }, + data: { + status: "ABORTED", + completedAt: new Date(), }, - attemptCount: 0, - strategy: "sequential", }); - return batch; + throw result.error; } // Update the batch to be sealed diff --git a/apps/webapp/app/v3/services/enqueueDelayedRun.server.ts b/apps/webapp/app/v3/services/enqueueDelayedRun.server.ts index d85d1d6804..6cc3268940 100644 --- a/apps/webapp/app/v3/services/enqueueDelayedRun.server.ts +++ b/apps/webapp/app/v3/services/enqueueDelayedRun.server.ts @@ -83,7 +83,7 @@ export class EnqueueDelayedRunService extends BaseService { } }); - await marqs?.enqueueMessage( + await marqs.enqueueMessage( run.runtimeEnvironment, run.queue, run.id, diff --git a/apps/webapp/app/v3/services/enqueueRun.server.ts b/apps/webapp/app/v3/services/enqueueRun.server.ts new file mode 100644 index 0000000000..e290b64ba6 --- /dev/null +++ b/apps/webapp/app/v3/services/enqueueRun.server.ts @@ -0,0 +1,68 @@ +import { TaskRun } from "@trigger.dev/database"; +import { AuthenticatedEnvironment } from "~/services/apiAuth.server"; +import { marqs } from "../marqs/index.server"; +import { prisma } from "~/db.server"; +import { sanitizeError } from "@trigger.dev/core/v3/errors"; +import { TaskRunError, TaskRunErrorCodes } from "@trigger.dev/core/v3/schemas"; + +export type EnqueueRunOptions = { + env: AuthenticatedEnvironment; + run: TaskRun; + dependentRun?: { queue: string; id: string }; +}; + +export type EnqueueRunResult = + | { + ok: true; + } + | { + ok: false; + error: TaskRunError; + }; + +export async function enqueueRun({ + env, + run, + dependentRun, +}: EnqueueRunOptions): Promise { + // If this is a triggerAndWait or batchTriggerAndWait, + // we need to add the parent run to the reserve concurrency set + // to free up concurrency for the children to run + // In the case of a recursive queue, reserving concurrency can fail, which means there is a deadlock and we need to fail the run + + // TODO: reserveConcurrency can fail because of a deadlock, we need to handle that case + const wasEnqueued = await marqs.enqueueMessage( + env, + run.queue, + run.id, + { + type: "EXECUTE", + taskIdentifier: run.taskIdentifier, + projectId: env.projectId, + environmentId: env.id, + environmentType: env.type, + }, + run.concurrencyKey ?? undefined, + run.queueTimestamp ?? undefined, + dependentRun + ? { messageId: dependentRun.id, recursiveQueue: dependentRun.queue === run.queue } + : undefined + ); + + if (!wasEnqueued) { + const error = { + type: "INTERNAL_ERROR", + code: TaskRunErrorCodes.RECURSIVE_WAIT_DEADLOCK, + message: `This run will never execute because it was triggered recursively and the task has no remaining concurrency available`, + } satisfies TaskRunError; + + return { + ok: false, + error, + }; + } + + return { + ok: true, + }; +} diff --git a/docs/queue-concurrency.mdx b/docs/queue-concurrency.mdx index b629834eda..d07e3fcc7d 100644 --- a/docs/queue-concurrency.mdx +++ b/docs/queue-concurrency.mdx @@ -3,13 +3,26 @@ title: "Concurrency & Queues" description: "Configure what you want to happen when there is more than one run at a time." --- +When you trigger a task, it isn't executed immediately. Instead, the task [run](/runs) is placed into a queue for execution. By default, each task gets its own queue with unbounded concurrency—meaning the task runs as soon as resources are available, subject only to the overall concurrency limits of your environment. If you need more control (for example, to limit concurrency or share limits across multiple tasks), you can define a custom queue as described later in this document. + Controlling concurrency is useful when you have a task that can't be run concurrently, or when you want to limit the number of runs to avoid overloading a resource. -## One at a time +## Default concurrency + +By default, all tasks have an unbounded concurrency limit, limited only by the overall concurrency limits of your environment. This means that each task could possibly "fill up" the entire +concurrency limit of your environment. + + + Your environment has a maximum concurrency limit which depends on your plan. If you're a paying + customer you can request a higher limit by [contacting us](https://www.trigger.dev/contact). + + +## Setting task concurrency -This task will only ever have a single run executing at a time. All other runs will be queued until the current run is complete. +You can set the concurrency limit for a task by setting the `concurrencyLimit` property on the task's queue. This limits the number of runs that can be executing at any one time: ```ts /trigger/one-at-a-time.ts +// This task will only run one at a time export const oneAtATime = task({ id: "one-at-a-time", queue: { @@ -21,38 +34,14 @@ export const oneAtATime = task({ }); ``` -## Parallelism - -You can execute lots of tasks at once by combining high concurrency with [batch triggering](/triggering) (or just triggering in a loop). - -```ts /trigger/parallelism.ts -export const parallelism = task({ - id: "parallelism", - queue: { - concurrencyLimit: 100, - }, - run: async (payload) => { - //... - }, -}); -``` - - - Be careful with high concurrency. If you're doing API requests you might hit rate limits. If - you're hitting your database you might overload it. - +This is useful if you need to control access to a shared resource, like a database or an API that has rate limits. - - Your organization has a maximum concurrency limit which depends on your plan. If you're a paying - customer you can request a higher limit by [contacting us](https://www.trigger.dev/contact). - - -## Defining a queue +## Sharing concurrency between tasks As well as putting queue settings directly on a task, you can define a queue and reuse it across multiple tasks. This allows you to share the same concurrency limit: ```ts /trigger/queue.ts -const myQueue = queue({ +export const myQueue = queue({ name: "my-queue", concurrencyLimit: 1, }); @@ -74,6 +63,8 @@ export const task2 = task({ }); ``` +In this example, `task1` and `task2` share the same queue, so only one of them can run at a time. + ## Setting the concurrency when you trigger a run When you trigger a task you can override the concurrency limit. This is really useful if you sometimes have high priority runs. @@ -81,7 +72,7 @@ When you trigger a task you can override the concurrency limit. This is really u The task: ```ts /trigger/override-concurrency.ts -const generatePullRequest = task({ +export const generatePullRequest = task({ id: "generate-pull-request", queue: { //normally when triggering this task it will be limited to 1 run at a time @@ -107,7 +98,7 @@ export async function POST(request: Request) { queue: { //the "main-branch" queue will have a concurrency limit of 10 //this triggered run will use that queue - name: "main-branch", + name: "main-branch", // Make sure to change the queue name or the task concurrency limit will be updated concurrencyLimit: 10, }, }); @@ -123,7 +114,7 @@ export async function POST(request: Request) { ## Concurrency keys and per-tenant queuing -If you're building an application where you want to run tasks for your users, you might want a separate queue for each of your users. (It doesn't have to be users, it can be any entity you want to separately limit the concurrency for.) +If you're building an application where you want to run tasks for your users, you might want a separate queue for each of your users (or orgs, projects, etc.). You can do this by using `concurrencyKey`. It creates a separate queue for each value of the key. @@ -164,3 +155,136 @@ export async function POST(request: Request) { } } ``` + +## Concurrency and subtasks + +When you trigger a task that has subtasks, the subtasks will not inherit the concurrency settings of the parent task. Unless otherwise specified, subtasks will run on their own queue + +```ts /trigger/subtasks.ts +export const parentTask = task({ + id: "parent-task", + run: async (payload) => { + //trigger a subtask + await subtask.triggerAndWait(payload); + }, +}); + +// This subtask will run on its own queue +export const subtask = task({ + id: "subtask", + run: async (payload) => { + //... + }, +}); +``` + +## Waits and concurrency + +With our (task checkpoint system)[/how-it-works#the-checkpoint-resume-system], a parent task can trigger and wait for a subtask to complete. The way this system interacts with the concurrency system is a little complicated but important to understand. There are two main scenarios that we handle slightly differently: + +- When a parent task waits for a subtask on a different queue. +- When a parent task waits for a subtask on the same queue. + +These scenarios are discussed in more detail below: + + + We sometimes refer to the parent task as the "parent" and the subtask as the "child". Subtask and + child task are used interchangeably. We apologize for the confusion. + + +### Waiting for a subtask on a different queue + +During the time when a parent task is waiting on a subtask, the "concurrency" slot of the parent task is still considered occupied on the parent task queue, but is temporarily "released" to the environment. An example will help illustrate this: + +```ts /trigger/waiting.ts +export const parentTask = task({ + id: "parent-task", + queue: { + concurrencyLimit: 1, + }, + run: async (payload) => { + //trigger a subtask + await subtask.triggerAndWait(payload); + }, +}); + +export const subtask = task({ + id: "subtask", + run: async (payload) => { + //... + }, +}); +``` + +For example purposes, let's say the environment concurrency limit is 1. When the parent task is triggered, it will occupy the only slot in the environment. When the parent task triggers the subtask, the subtask will be placed in the queue for the subtask. The parent task will then wait for the subtask to complete. During this time, the parent task slot is temporarily released to the environment, allowing another task to run. Once the subtask completes, the parent task slot is reoccupied. + +This system prevents "stuck" tasks. If the parent task were to wait on the subtask and not release the slot, the environment would be stuck with only one task running. + +And because only the environment slot is released, the parent task queue slot is still occupied. This means that if another task is triggered on the parent task queue, it will be placed in the queue and wait for the parent task to complete, respecting the concurrency limit. + +### Waiting for a subtask on the same queue + +Because tasks can trigger and wait recursively, or share the same queue, we've added special handling for when a parent task waits for a subtask on the same queue. + +Recall above that when waiting for a subtask on a different queue, the parent task slot is temporarily released to the environment. When the parent task and the subtask share a queue, we also release the parent task slot to the queue. Again, an example will help illustrate this: + +```ts /trigger/waiting-same-queue.ts +export const myQueue = queue({ + name: "my-queue", + concurrencyLimit: 1, +}); + +export const parentTask = task({ + id: "parent-task", + queue: myQueue, + run: async (payload) => { + //trigger a subtask + await subtask.triggerAndWait(payload); + }, +}); + +export const subtask = task({ + id: "subtask", + queue: myQueue, + run: async (payload) => { + //... + }, +}); +``` + +In this example, the parent task and the subtask share the same queue with a concurrency limit of 1. When the parent task triggers the subtask, the parent task slot is released to the queue, giving the subtask the opportunity to run. Once the subtask completes, the parent task slot is reoccupied. + +It's very important to note that we only release at-most X slots to the queue, where X is the concurrency limit of the queue. This means that you can only trigger and wait for X subtasks on the same queue. If you try to trigger and wait for more than X subtasks, you will receive a `RECURSIVE_WAIT_DEADLOCK` error. The following example will result in a deadlock: + +```ts /trigger/deadlock.ts +export const myQueue = queue({ + name: "my-queue", + concurrencyLimit: 1, +}); + +export const parentTask = task({ + id: "parent-task", + queue: myQueue, + run: async (payload) => { + //trigger a subtask + await subtask.triggerAndWait(payload); + }, +}); + +export const subtask = task({ + id: "subtask", + queue: myQueue, + run: async (payload) => { + //trigger a subtask + await subsubtask.triggerAndWait(payload); + }, +}); + +export const subsubtask = task({ + id: "subsubtask", + queue: myQueue, + run: async (payload) => { + //... + }, +}); +``` diff --git a/internal-packages/database/prisma/migrations/20250211150836_add_aborted_batch_task_run_status/migration.sql b/internal-packages/database/prisma/migrations/20250211150836_add_aborted_batch_task_run_status/migration.sql new file mode 100644 index 0000000000..f00d71d2a3 --- /dev/null +++ b/internal-packages/database/prisma/migrations/20250211150836_add_aborted_batch_task_run_status/migration.sql @@ -0,0 +1,8 @@ +-- AlterEnum +ALTER TYPE "BatchTaskRunStatus" ADD VALUE 'ABORTED'; + +-- DropIndex +DROP INDEX "SecretStore_key_idx"; + +-- CreateIndex +CREATE INDEX "SecretStore_key_idx" ON "SecretStore"("key" text_pattern_ops); diff --git a/internal-packages/database/prisma/schema.prisma b/internal-packages/database/prisma/schema.prisma index d676484d9b..9373f0620c 100644 --- a/internal-packages/database/prisma/schema.prisma +++ b/internal-packages/database/prisma/schema.prisma @@ -2208,6 +2208,7 @@ model BatchTaskRun { enum BatchTaskRunStatus { PENDING COMPLETED + ABORTED } model BatchTaskRunItem { diff --git a/packages/core/src/v3/errors.ts b/packages/core/src/v3/errors.ts index a079d0d71c..46d1f1a3df 100644 --- a/packages/core/src/v3/errors.ts +++ b/packages/core/src/v3/errors.ts @@ -235,6 +235,7 @@ export function shouldRetryError(error: TaskRunError): boolean { case "TASK_RUN_HEARTBEAT_TIMEOUT": case "OUTDATED_SDK_VERSION": case "TASK_DID_CONCURRENT_WAIT": + case "RECURSIVE_WAIT_DEADLOCK": return false; case "GRACEFUL_EXIT_TIMEOUT": @@ -512,6 +513,14 @@ const prettyInternalErrors: Partial< href: links.docs.troubleshooting.concurrentWaits, }, }, + RECURSIVE_WAIT_DEADLOCK: { + message: + "This run will never execute because it was triggered recursively and the task has no remaining concurrency available.", + link: { + name: "See docs for help", + href: links.docs.concurrency.recursiveDeadlock, + }, + }, }; const getPrettyTaskRunError = (code: TaskRunInternalError["code"]): TaskRunInternalError => { @@ -672,6 +681,11 @@ export function exceptionEventEnhancer( default: return exception; } + } else if (exception.message?.includes(TaskRunErrorCodes.RECURSIVE_WAIT_DEADLOCK)) { + return { + ...exception, + ...prettyInternalErrors.RECURSIVE_WAIT_DEADLOCK, + }; } break; } @@ -898,3 +912,20 @@ function tryJsonParse(data: string | undefined): any { return; } } + +export function taskRunErrorToString(error: TaskRunError): string { + switch (error.type) { + case "INTERNAL_ERROR": { + return `Internal error [${error.code}]${error.message ? `: ${error.message}` : ""}`; + } + case "BUILT_IN_ERROR": { + return `${error.name}: ${error.message}`; + } + case "STRING_ERROR": { + return error.raw; + } + case "CUSTOM_ERROR": { + return error.raw; + } + } +} diff --git a/packages/core/src/v3/links.ts b/packages/core/src/v3/links.ts index 11a45fe023..c853d194e0 100644 --- a/packages/core/src/v3/links.ts +++ b/packages/core/src/v3/links.ts @@ -15,6 +15,9 @@ export const links = { troubleshooting: { concurrentWaits: "https://trigger.dev/docs/troubleshooting#parallel-waits-are-not-supported", }, + concurrency: { + recursiveDeadlock: "https://trigger.dev/docs/queue-concurrency#recursive-queue-deadlock", + }, }, site: { home: "https://trigger.dev", diff --git a/packages/core/src/v3/schemas/common.ts b/packages/core/src/v3/schemas/common.ts index 5b47d99c20..a8f9faaeed 100644 --- a/packages/core/src/v3/schemas/common.ts +++ b/packages/core/src/v3/schemas/common.ts @@ -172,6 +172,7 @@ export const TaskRunInternalError = z.object({ "POD_UNKNOWN_ERROR", "OUTDATED_SDK_VERSION", "TASK_DID_CONCURRENT_WAIT", + "RECURSIVE_WAIT_DEADLOCK", ]), message: z.string().optional(), stackTrace: z.string().optional(), diff --git a/references/v3-catalog/src/trigger/concurrency.ts b/references/v3-catalog/src/trigger/concurrency.ts index 35f5677c38..8fbfa021c5 100644 --- a/references/v3-catalog/src/trigger/concurrency.ts +++ b/references/v3-catalog/src/trigger/concurrency.ts @@ -1,4 +1,4 @@ -import { logger, task, wait } from "@trigger.dev/sdk/v3"; +import { logger, queue, task, wait } from "@trigger.dev/sdk/v3"; export const oneAtATime = task({ id: "on-at-a-time", @@ -90,19 +90,23 @@ export const testConcurrencyChild = task({ export const testReserveConcurrencyRecursiveWaits = task({ id: "test-reserve-concurrency-recursive-waits", - queue: { - concurrencyLimit: 10, + retry: { + maxAttempts: 1, }, run: async ({ delay = 5000, depth = 2, currentDepth = 0, + batchSize = 1, + useBatch, }: { delay: number; depth: number; currentDepth?: number; + batchSize?: number; + useBatch?: boolean; }) => { - logger.info(`Running task at depth ${currentDepth}`); + logger.info(`Running task at depth ${currentDepth} 1`); logger.info(`Delaying for ${delay}ms`); @@ -113,11 +117,27 @@ export const testReserveConcurrencyRecursiveWaits = task({ if (currentDepth < depth) { logger.info(`Triggering child task at depth ${currentDepth + 1}`); - await testReserveConcurrencyRecursiveWaits.triggerAndWait({ - delay, - depth, - currentDepth: currentDepth + 1, - }); + if (useBatch) { + await testReserveConcurrencyRecursiveWaits.batchTriggerAndWait( + Array.from({ length: batchSize }).map((_, index) => ({ + payload: { + delay, + depth, + currentDepth: currentDepth + 1, + batchSize, + useBatch, + }, + })) + ); + } else { + await testReserveConcurrencyRecursiveWaits.triggerAndWait({ + delay, + depth, + currentDepth: currentDepth + 1, + batchSize, + useBatch, + }); + } logger.info(`Child task at depth ${currentDepth + 1} completed`); } @@ -236,3 +256,34 @@ export const testChildTaskPriorityGrandChild = task({ }; }, }); + +export const myQueue = queue({ + name: "my-queue", + concurrencyLimit: 1, +}); + +export const parentTask = task({ + id: "parent-task", + queue: myQueue, + run: async (payload) => { + //trigger a subtask + await subtask.triggerAndWait(payload); + }, +}); + +export const subtask = task({ + id: "subtask", + queue: myQueue, + run: async (payload) => { + //trigger a subtask + await subsubtask.triggerAndWait(payload); + }, +}); + +export const subsubtask = task({ + id: "subsubtask", + queue: myQueue, + run: async (payload) => { + //... + }, +}); diff --git a/references/v3-catalog/src/trigger/simple.ts b/references/v3-catalog/src/trigger/simple.ts index 8dcf96f46e..9172317e7a 100644 --- a/references/v3-catalog/src/trigger/simple.ts +++ b/references/v3-catalog/src/trigger/simple.ts @@ -141,92 +141,92 @@ function thisFunctionWillThrow() { throw new Error("This function will throw"); } -export const parentTask = task({ - id: "parent-task", - run: async (payload: { message: string }, { ctx }) => { - logger.info("Parent task payload", { payload }); - - console.info("This is an info message"); - logger.info("This is an info message from logger.info"); - console.log(JSON.stringify({ ctx, message: "This is the parent task contexts" })); - logger.log(JSON.stringify({ ctx, message: "This is the parent task context from logger.log" })); - console.warn("You've been warned buddy"); - logger.warn("You've been warned buddy from logger.warn"); - console.error("This is an error message"); - logger.error("This is an error message from logger.error"); - - await wait.for({ seconds: 5 }); - - const childTaskResponse = await childTask - .triggerAndWait({ - message: payload.message, - forceError: false, - }) - .unwrap(); - - logger.info("Child task response", { childTaskResponse }); - - await childTask.trigger({ - message: `${payload.message} - 2.a`, - forceError: true, - }); - - await new Promise((resolve) => setTimeout(resolve, 1000)); - - return { - message: payload.message, - childTaskResponse, - }; - }, -}); - -export const childTask = task({ - id: "child-task", - run: async ( - payload: { message: string; forceError: boolean; delayInSeconds?: number }, - { ctx } - ) => { - logger.info("Child task payload", { payload }); - logger.info("Child task payload 2", { payload }); - logger.info("Child task payload 3", { payload }); - logger.info("Child task payload 4", { payload }); - logger.info("Child task payload 5", { payload }); - - await wait.for({ seconds: payload.delayInSeconds ?? 5 }); - - logger.info("Child task payload 6", { payload }); - logger.info("Child task payload 7", { payload }); - logger.info("Child task payload 8", { payload }); - - const response = await fetch("https://jsonhero.io/api/create.json", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - title: "childTask payload and ctxr", - content: { - payload, - ctx, - }, - readOnly: true, - }), - }); - - const json: any = await response.json(); - - logger.info("JSONHero response", { json }); - - if (payload.forceError) { - throw new Error(`Forced error: ${payload.message}`); - } - - return { - message: "This is the child task", - parentMessage: payload.message, - }; - }, -}); +// export const parentTask = task({ +// id: "parent-task", +// run: async (payload: { message: string }, { ctx }) => { +// logger.info("Parent task payload", { payload }); + +// console.info("This is an info message"); +// logger.info("This is an info message from logger.info"); +// console.log(JSON.stringify({ ctx, message: "This is the parent task contexts" })); +// logger.log(JSON.stringify({ ctx, message: "This is the parent task context from logger.log" })); +// console.warn("You've been warned buddy"); +// logger.warn("You've been warned buddy from logger.warn"); +// console.error("This is an error message"); +// logger.error("This is an error message from logger.error"); + +// await wait.for({ seconds: 5 }); + +// const childTaskResponse = await childTask +// .triggerAndWait({ +// message: payload.message, +// forceError: false, +// }) +// .unwrap(); + +// logger.info("Child task response", { childTaskResponse }); + +// await childTask.trigger({ +// message: `${payload.message} - 2.a`, +// forceError: true, +// }); + +// await new Promise((resolve) => setTimeout(resolve, 1000)); + +// return { +// message: payload.message, +// childTaskResponse, +// }; +// }, +// }); + +// export const childTask = task({ +// id: "child-task", +// run: async ( +// payload: { message: string; forceError: boolean; delayInSeconds?: number }, +// { ctx } +// ) => { +// logger.info("Child task payload", { payload }); +// logger.info("Child task payload 2", { payload }); +// logger.info("Child task payload 3", { payload }); +// logger.info("Child task payload 4", { payload }); +// logger.info("Child task payload 5", { payload }); + +// await wait.for({ seconds: payload.delayInSeconds ?? 5 }); + +// logger.info("Child task payload 6", { payload }); +// logger.info("Child task payload 7", { payload }); +// logger.info("Child task payload 8", { payload }); + +// const response = await fetch("https://jsonhero.io/api/create.json", { +// method: "POST", +// headers: { +// "Content-Type": "application/json", +// }, +// body: JSON.stringify({ +// title: "childTask payload and ctxr", +// content: { +// payload, +// ctx, +// }, +// readOnly: true, +// }), +// }); + +// const json: any = await response.json(); + +// logger.info("JSONHero response", { json }); + +// if (payload.forceError) { +// throw new Error(`Forced error: ${payload.message}`); +// } + +// return { +// message: "This is the child task", +// parentMessage: payload.message, +// }; +// }, +// }); export const retryTask = task({ id: "retry-task", diff --git a/references/v3-catalog/trigger.config.ts b/references/v3-catalog/trigger.config.ts index 830ac38999..9a24160445 100644 --- a/references/v3-catalog/trigger.config.ts +++ b/references/v3-catalog/trigger.config.ts @@ -20,7 +20,7 @@ export default defineConfig({ maxDuration: 3600, dirs: ["./src/trigger"], retries: { - enabledInDev: true, + enabledInDev: false, default: { maxAttempts: 10, minTimeoutInMs: 5_000, From 67f2f8b29d993d6e1f90c77fa952785ae57c5b76 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Tue, 11 Feb 2025 21:36:28 +0000 Subject: [PATCH 5/9] Finish docs update for concurrency --- docs/images/recursive-task-deadlock-min.png | Bin 0 -> 476670 bytes docs/queue-concurrency.mdx | 12 +++++++++++- packages/core/src/v3/links.ts | 3 ++- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 docs/images/recursive-task-deadlock-min.png diff --git a/docs/images/recursive-task-deadlock-min.png b/docs/images/recursive-task-deadlock-min.png new file mode 100644 index 0000000000000000000000000000000000000000..423a5399288f95243ec23950b307993cb903cf23 GIT binary patch literal 476670 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 z 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⁢mDGFMZVS~EIK8xLc~7R=uoNs)%q8+&W@Jyy_B`Z zfK4Q!g$E{&)sqCBCu?R^e#Y2p0Jh0XW`@8u)%PE6N7PIz&9<4J-fnXT`M@uC&kEg= zU}4YCX4qiYV>i11E9A%Rt>h9}P_%XGc8Y5I_2b7y8UBoV#ab-|RA6RS=DR5JFjOov zsGX~Lm&eNrcVZ!T<@!=wbdC#=b}pE4a4zDK0afHyu^8dsM)Px@<#=EQ^ua*`t%?m9 ze02N!=xZN~(17Wx_I55K3>>auoCt^?Y?VT>EiMKN<< zp$9rMPK6lbYV&|0s9+_t7@&OS)xxXbJabQ&!nu0c%mBLfhj|SKG2LeFi2*g-M|rRi|qdWb>i)JK$Ujykv&gvS|-%DS8XD8>Hb@|J+Wm$RFqPt}DOamohaqKPN&z z1DbBjCf6H@Lexi<*;kp3M3wUnvsUbbrUa!xBZ*m!wiu}Yg6%aV;yXRV9C_(dGBS7T)VG<@Dy9c2m>wz&7@1=JLrWQ;pA)p!$ikfG zMz|oQn$Jxxde+x^7M}_r<*=g*ZhvG#UZdux)eo=jGjEfVkg*)UY%fV;d$1aG-)@%V z@8I`ifTY-h{j2EYH_%=3SEwOaS>UOrTC2wDFLj02U7cwcYwFS`*xMPk));^-ZR@$z zFO%~Qz0}W@^Io~szmR2` z_u6BsEUMnM@X&`X^D;<8Kr0ac$nXa0Y8iA3UTW*j-QTrH*tUE9feucaxHsGiad6Va zl;Oa&HW5QFR90WIxC)&f|L11E^ud$qi*-<=mbS5^f!+wwl&$BREZ=)J`OqSa#?H#g zkL<#o@44P`vTL0>S?({d1Z8<+cG9P75~Ziit8RZw^*XzcLA+sj;^zNMX?T`2=XPh? z6Z4G3H{O?M>g=Qb`-tR{=W!yK&t;H5j`33OMjGA8W&c*Q`2yaT#2KGH95~&1mo?3r z(`>lv`v_^i#2;E;Vt%u?v+Z;P4f=7(3S-dzB3Md_n7u?ylB0%(qh?kvU2^64_id)9 z7?FQ6TV`hyFA?P|LOc#~6Wg87z!7@&G>@{jb;=ONGyV$KNagKC;fLzEr0Jy0UF@_h@v&b=Wq4y&ya$cr`#NW9Q_9I5jEL z^LZ7o+4m`Nki%T-iHX|nIe$N!P@&@?7*@R25|mddNhpLTXs&n946u+NY>4_$YVd9b zO5TEirE5v%M@94drDK5GO&|#DO12BAgh9|5@ zIa{<|(2ib}4rQP7KOZ^JENIM*pr1<%naUjWIcEBINxcxro!r2EMcBf<8$&;3^4$cX zeQ~Lux47dQ6jOI`)qye2U3_{g^%nCQ@4HC-mZ{>!acP*u(Zw>UT8nom^gNqJZC%{p zTxfCwVa%^1$r}Y-9(yG#8oevAPgYj1hHf@A2e7E8m{jm2_Fa*|M)eQ_Jjwc4og}5aQp=i?7xXW@(&e z&Md(|1B2QwS!cF|JcfMDlzkR5QZ8Mp<(PMB-sDVO3e4CaOJ^&3%gFq$l5^gvF^%4T z6;$^oC#B{fd;2nb^6dQmv~46bNBoD}Rn|1|8y=fYQ}|{2R$uV(;VeToK(Pa%^itlS zL*1$Clah5DIcb_A;c>XiK<2~GXKT~8yFce&FzPYt-POa>Op*zgjh!bhnH-%jk zUQxQMv(Zbh3Od98BGHS~k!XE9mb8H`&p}__EQ`=F@pO z3M~dDv^Fg+W9T|#LA~3ENSGVC0@CtL?$+|(|6K}2R~0QnW1(n3o1Ag;DN*vNTGedK z&dMLwy@tl;WhMcUm*(KCs)9zBZG)4RVoYXn$uMPeM`}g!^c##Ex~XzT8M9v`cNMpD zaY$Kb1$mQj3 zB$`bL(;^$4?j!W1)+!i#IK!5X8&nvGC+k#zxfryIp!gY?r>e8Nj3zu!ZuM$O7DvCK?c3(za`O%iGZTgAx0;SX)z=e{Q4h*7K4!FHG7} z^etmb76w@4rm}in zOxopDF*?U{qYc!8nck@=Sya0fY>NTtMy663Msk1wMt={KH!~aY5t-ZjOv z7!5zJy^{9|&PYZ68h=)ab~ zRDef&5^)o_&zpbq=@Xvk_uJ5r|0SWi;G=9s4H{qqaOvh;lCVAEb52Jy4Qo)kX;tQ>0R#o6FsvF25Y|aR9gzMTc4nq}P7rA_By-6<-L!bETW=2S*|@ho#!oG+<8~jWL&87z)2e@Tc6n?W42+QU z;mA|hN}+3PX5m7S^INI)jPTT3F1~30E_IEtz^zB;-{}8eMq%!ZRVZ@)s7&t(k;gqN zw50C{2CCIb+x3Z9uK((GLGHi%uKiI+{?A0D6g4A6Qb_N(u>X98gq!)ca=P5*ZcX3g zm{~cP=;|P~W!(QEhuhveSF+)j5LUy9s;Qrlj_qQhNShvZ9n=_8#s5LjznI0X zI?ANqK-v%~#{gjO3FEKCugJ2H57;z=(81~%jf6*Kd`bAXyRBbrB!xJQ=SbipSrNYh zSz%Izv}%v#e7e#&+gbk4hcZ=2vZK15p&nSSmL!HL(+eQirmBTiPjXQ?nSgQz7QLg; zCuw0RY$xeqQq+Gn-`s2v^}~Jc@o?Wt*6j_N`)=Rnr7a|3@ICl!rZwuo}IoNXHu^L zb~D9f+{g#)AI*VU-LH&^6+LdVwHx(p^-%@Xw13soXr7+0EXedd0#N&Hm|BwSu3ae}X zQ}ALazQp*C!|@CE)Duz!;dmN&Ja=DG z{Y#>EHaW%1B8snp?Vp||kn8tv8pR3>Vj659eTzDF8(9%HYWJ1SxVbfrdHB_BJ4GNv z8_mRe8c%L38*mlj)gd6-F zrQri@&74xA+bpOssSDi`Gx}HW#syMrQ>vR)ren`_DD1$}X{(d?sNbIe__zK1|He*) zdc?>%C>s1D+#i7IGOhCn-a80n6UkSA!<%VEJ$n%en$krDmR?p)TUeqxzWMHdwzYLd9)dRFD4@a3)Sl=^OB4qdv@kglh8nAb>|3Lh>Ju z2EC=LiatqW?TM3*Nwz^S-n} zgv3VZ-j{;Y!h|!M6NoNW`SFUq;upC8t(xL&!DLUyyj0HZQrScDP_GAoeVzlK!e- zl!j2LLuqRCwy)~}$%&JGq@^swX?oIbr^co?{mVvT@J(5>R5EJ_ko_MW{BllZz3x9l zRw=#M$BpVf(bW5@28!ov7?Jf5}i@bI0?~W>1Eat~BHTI@;Zv}9;W|ZC zYD)+h<=J zbwjqzWZO-DSQGKY76)HG09gDx!EY7Z(7c{H-;I|BsMtrDquU`@K{NX`a(kKkZwpso zI&Ht4@7^B@4KwZxgaXL0zjEC>Ss*7aozk^aUlX1zVE8MUh8Eb3r=i2!RNwnL*e9G1 z*^hs`--f$KdW_A@G_3NSRStUVjaOsNX5OH@v#odY@o&P-37^v7tABN?y}51&7>7`T zp%w0I4iCre{)FVvTUJ&OhC$N99krjbTTFI}325wD4ZP!SDBwnV=zsWYws{;0`~mS& zl^<**pLl;>I?4#Q^;96i!mAbP&e8F09UXz%Op|=(B04=Rrqu1tAL)>@{0<-1|-H?kj3lgi_ zP5@Z8-l27$!Ph_eisMZXSl;=sN|OQfN+)wA;{a9smh|#k?Ji>0$~|OEu@1yZ1e-i( z!2a7Lg8=}l$LQ4l0VeQZ$2zv2x*x+#z=twC6q*pO zT19z#0F`sFDEU`N1N~#vMy=(rcARDh>d)mbGBz-WGo%;4DB-|{?CDskyj=q#>G7am zFg)B*>H6C(b`-wr`8uU7%q{;zMTql!9Bm zXH3L&LV^1wI^4T4(S1>3R+}7KmOG_syDnYJ>h*MbMSYbIY1NFbY-evVK}5fdM(}rQlY{tve)86P}`T)8W2AoS7AE=8!%nNVZS?&>der1^aOUQN~7_Af8$YV)UThw&H z`6}^xJ;bb%&B4hP?>}nPsB}~&WOly?0bL{~K<(5_W!!~RvQ|F;Pen4m1xlF{5n12w zYVPDoS%*hAprS31t47_)s$YN%C+`zqE#eP-z6bWazxnajez(pqOUa)McB!ALl1#h> zg1fYltf4FwG z+iJ)9Ujd=Zg{umnt~iuZ@i$Lf9z&v1 z`WA&_AfA&=CQRy;QS#ewTtV;+weC#a zSL7(!-k+HT3ZNtIsiuOP2FIyq^sGg?L1zzJC&cG4L}u>7_u%=^p!GXEv%{jM-*tS? ztFbScCL6*XWn{7s#9I3C^|0*DQ}#>Z04LM?gs5;y1^1EoqEnzO((GeS=D7Wr!wrA$ z(ctbf2rYr#X<8cz_6V?-*oKWoT+vDC0GK^+{>$|Jmu&~0IR~1^Sy)2-VNCl#@#_87 zOA5Sy{m0|NPQP{;jXQ8{Il4=n_)m5Jl?l9^>%-d7kzU$YlfD+Ppo69>ZJABKved{Q5qAnct54wBCmFASr9r9}i9>uOcy>q{urd1;dg%sd6F5Ag`?NdZ z+v^+gHGB1IZr@BbDGK9otVheTdWN{ti@WAgtMzUlR`_HM%S}T1>z~v6jTSLaOVjEv zxB8OX0HXS;NQoyb8{*yq?eS4kGAaplNYu{ zMlQvnb=ePqS3FusL%2Hu58*5P6uHQuc6p7(vBFQBS9dvY51&DGU4ADLX|VkVD!!Sd zHY(w{qj5jwvf0^+;(=u_7UP?AXLBTt0#0&1j;AqBj}gWzx`kAPNQ8pVZ!ciyp)SGZ zBV#V-Q!}Izip+hWAR>K`>(5V=$wHd+^ST#CNGfCQL1v)gLzD4+TQiP977aOk&rXf= zS#C`A%XST!qJk@Q)eWk3jn!_wx|LTDHXf8Nkyh7no+wcCtjy_N6ptm8PxP@0UeR17 zAo!lmC_fooOQ$rgT?ui^jbCqkY?ThoWGqiFu{i4_9GmOAR^OgLInAQxudqjdXuXha zZ6jG|MUObyP>2rTx4Z1~=km4A+1Rl^f08r3`q8VX5D+xRf`?FJAYMBs2@2{@ z@*u&U1Jt6mQ(ih=e%@T{6UQkYtUvcL8DW!wsf}F6)Nubt#lBJ!!nbqUa1Ejly~?`c zuN!@*SWsMns`$qR6Wd;TVjtFa@a>vF>Qhd&Yo`!)`D8$Pw13WXIFD830_pb8?|7oV z(U}JY|n`HK!%37DH-D{S{;Ws({7C-5{QQed^%v>K~!XNGid zy5-z$DCEhR+i6l;4b!X&X^j!>6F%*lnAoSb%o!Su5%-%q_vASHsT&#-D%&1M@%ZZ` z1`k1{Wee&*@AyCJ_$*EicWLUT0At`2b``Bg@>*uL#5Fdrz>p86wJ*OC>va!k5yJEQ z6J)+EjEbxHBi$cE$ReRSuYp0Nz^ifh0`bcE(W1@u8UYKhNvf6U8jh2uH6t+ph??eMm-K`?$CjC^smN#66Dzz1XS{mk{F zCJsr-ZH{OEh1EoG1J`mCE%4^E97J}Y17TS12D7i95ySHw-4#D!%2KLwGj*6LCPsm1 zT3M*tA3!vsNDi*Yw-VaCt-$ZavVV`jcU*_Pex7Htol5GR|bRo8+c3>|{w;zJ= zWB@;fWpL``&y$#L-*vhZH!`a_f6~HYC=VL-)~g+oV>o{5{N1bW>9j25310wERwrw5 z8T73A}Y*hZIHLds|gERB2D#g0bG= zQ=`H59Yz@YI+fHu)|$&X`STIBz+R!l;9(wyj=5Y=;J3f_g7 zsjt8Acgz#!4gld~mSsXH zD>I>z5j=SCJ9(`-0m%k{r>4;iAD#>knl;@se;^ zf+^?T0$LLOeIw5!lY*Qi?Fz#ko8dX62d1M1rN>rNu`-zMDZ663Z3`)v|3d61y3s2? z<)#MlQ1Tu4o{#bMP8@JnetwP4N#=ppF;j*0*HSQ9DCoeY%Zv-D^s zBS5`p)v&b`>AJPaW8BC)qM|m%mLlz3EUN20*AL;018_27L%+Uwiu2O<@Eo;SrZCm9 zL@%pGZe)E@YM)34u|_?9?g^&lhldty;P_pH2XjI}F=hNLz;K?11@VQ6Xz zB9Z?TbK;XMCJR26`Y{*6CyRmvIzEGUed8*TT(^{~2?uXbrjf!+>$J5?{51l=$_w&X>T3q~LI;TRMBo*IU8xdpI zIljWR4|!d9Dppuhf1&SDIQG316dC@X-G{p&+lHAR;G%9#~eFD z>#?+YG9TY!LQ>t#b-3t;@8q`=^UnrzWD@aBvHPuY_X(H?;1yF;k&larP@*7?t5g;v z&~sj57hzcmnYpS7bv{D)dMc&y!Zh`3wN<@uS3W{$XDQ2UGqIM(mX+C46DJO}QVHSx zv5Wdn$j@wYg$gNsZtlGXeL^Nv)$^SYT4B$Rfd0r&=am8|(}HlvxQ@w<>gfimt_PmL zSb7aBYqwG9S+(|#`IHdhNx0J<6HKoD50C( z986B)!%H&!NrdP5HFmt81*PRd?AiNJuVc${?smzxYK?pfmP4$^<)JdUgVXgkvWyr! z*++xZB{!{=MhhJV1!{!h8_iOM5o5Ms?!NIkqsyQ)@}FeMeLSiOEnf-LkOQ5c>Va32 zrZQeVi2?(2ASgd~@6UppLB}ky=KqJPGjW9S`~EnIvG0^U%Z$eQAcRmM8AD~FrYwUo zC0Z;gLNxXz#`=h;Fe0f&k);@l!AJ;Ag^DJ!l(iK3U454C=l2ghGta%}o_o&wyv}(l zxM7PA1#dhXW^GJinzhl}#xGHJ=FF0CyPZ{cw6Uk%2Yo{4o<)_v$%to(HWO1uO4p^f zB_Cx^F$6|&y0pD6JM3gR`&ZXF#Dr1C`@c(_;_t=$R6g6bWA;GykafXI2WS7H=H(AE z7CeOc{YrasWHm3ZeA@EaMR9fJEWhU6O8lPubAP2f7(qCGaPQf|sFmu93!k23RFOLf zG}Q);(5})G7mM^|6NsC&d%xg?Z|OcYBWVWXUmmysooA%Q(V|@srz+z1u4Kp`E#$Xc zNT9#xZdlBb$(BbwK{eb_sNspW{kgWciovMnn80AY=^!*7 zc-Y}YlsS^*{M268?#JOtOG_nV>H&(vX75PGtrL1d(A&n}*@>)l^;;Yv)*SQC_ES!m zy_n<=uIxbHxoAE%TOwgJwTXmrjW7kS1Ps!l5^b6255nxVU&=_DX9V2!;^41u7`HGVTvxC&dBw4*o1mp)6V6MGzaoLkI62ZoWo-^ zyG9${cw=ICTA$qsJOAl;*a!jo7M$&WLAE)3PaA*eQVmR=+=RMx#dLbl*99HcF=PJl z;;&?o?jrmLcoCVp7009OOb_HOpdE=?ePT_I^bVYTNM;S0!$ri1b+^Iw{w@46MQ`qA zbFSe%?B(>6+q#Ppe<^=Q6*Mv`Fh|Gm^i!v&d!kZ1EJ~={ZiE;c3NwgAziG zM(+BDt8J1imhEKU&xAPc4jwJ+F_q(|k6hZ)bM#b?+}I7;9!}gg1%WlOgvYw=;_~0V zu#6|K8jAT`jj~f5={44(htF5SiaMAH595mx!cFJo|!tQTTb>J zD}^uPAx1`lM`9FKk0;1R1TBW5o@ptOz-&FqBaht!KZrsLGA~zfmtL4F#$W`U)cgj+ zMsC?>9}nWijdgv(>v~0#BdUo9b1+%PRA^|I0m$VwxL2IUV&ouuxD&y>!?F);GEx zBE%82DYUOf9&=-gag0UZ5yg7p^lbjE3!?|x40ZIi=|#wfeQ1z!Q|`hMCyQ$K-8+xc zZr_f`)Yf3z!AYH^XFbW>cZB*wj?sqid)`y*v0}y#C9lsLr>0wc zWrlxu<)XfTx&IP$vbrk*Uu#(U84}{08847FHK$@%UkkJM_b}v<7a%*aQ@TqFL;Z29 z))(QPcifwyrg6iEwmZt={SUp}GM`1xGg835!JS{Ma5ndjvNJNObJM{3%=+)z%kwHM-4^2;uiNug*CduM zjZ0oIBEIk0pDXb2qLjM8)tLvCGJDdpP#)jj9()_Xp6K=SyPhThRm-F9HtpgPzRsHD z$Sl__)71-Kyb{IoJ&aE5V<0UhA#dza_pe|2rQJp^qZ%~PN`a`STR#&NS>YY`-s|-} zc_4DMguId{?=eYb1hT_rH(TLkp=z6B-kOS1bpt_<_6u5Z{mFB1wx|S@69ck^T4fJ40-mDy&Z3iqZqo#NBUEz8ob*~x$b2;TcH#OP6J?Avkd)q#@V3@D160hEcT7d0ZsQ_-;xM)dZ0_ zOw==cLEm+sGjqPY$m{lG>g8sYFlh3j{fjFg^0rvZ7yD?nZSI~2XvYvnRqu`UK=)*i zbl5C@UfcAS=*2iZAe^$~^gxOh6aXQn-)&!78dA6}bw`2eZERE1{+W;@$)e<`=f~?9g^9q<^YfHP;H4Ia-nRQ@KzSL(I#A43(>XQ&cJJL zqDICeGbogqhTk?7OmO6f@dzC$kG7*Q+q2Pf+Q>9=d;FLEuRb=)Z|b))<*in#IS^2C0=c~CldAa$ zsr&lYLVz%-z4%Ugw8pF7m6sNU4P|_nC3d+h3#0gA4ck%}$fm9M2bW1lF8lb8>EGmp zS>g?kJPHG~BFYXf#|XdaQItgos;NJZoyI#lF$ii{ z^7a-c_2iL5nZHaXle!(3LU~Cp^EG>#)#580HZ`4{yelNW6P~gAOrXUSokT3SCy;%=6KGoa z1_UmrlmD0g7H&B1Lir7SQ!AHNpTZvX0N!evrzAtaQSDs$s{IzoGFh9VExoQZ)buK5 zWmF|7Qpfq43z+CWeW-Y0eC+2XYxQhFax$%&law5qtKgg_KS9r0Y7aLeaX{W0#0WF- zoZoT%Wp;y|KbHo;5)NLlBCZ{&JZ;u7CiRiPKgT?(hgjJ18}0G7X|?-y`Vu(`l!cd5 zG1tN?q$3QkhW;tI)%#Xli@~2qdA*Q;vdL(Pu<@4}Ai;7G)EvAqH5#U@8xlk5$Wmbr z9OOFKSIt4XK{Qg}hPlpY7k1sM;Y45q>~?09NEsmCR%|}9>=HXA!@-QcooU9)&D@uI z@;$7FQ9j-iLfLZ7S3k3b)r zG|ihnw5nKieAdfl`zvsPj;1tW#6fz#KlPShsXJ2PJ+LsZD7i(+c?Jhy_pspTJ zfqby*(WocI(vgwYE^zHmk$nRBWKzhchbYf}kp{P3LqWhWx@(4>sV2kdC)m+GLU zUSQFIUyuQs5p(Oj-ojL`eQrw{gXi+T^|*YxqZ=?^e&%iX&*a1hf0xDu6{zp^2B>xch1$(b9Zyw6!Z$*|ru8JW&-i|$9 zxo&LPELcQlRkM$TT>L$Teq!uVRW;9e9aJOH9pqxu#k=tSm6=4_CyELt)Qnc}DD;`u zOVyCpzz3U{(%eko3}NwO-^;_lolG8JoU!BT=fO(owLWZQbFgV|qtOtqKDRFYMiIBK z>nVY1f~wC^b<`tWJmbJf0Hmobjr#Ta`eW-s-N^X^wOkAQN8vW%kep+W2A#f-{UVR| zZ^xUwAx?<>>PyxW?Vq$e)9|YNyLaT7AE%!mVVDY|)(la@51-rIsn#C>IfAJv@_Tkc z;|I&$j%=x92@X`=z6|Y%L!G}rdq^{p_~j)VJFQ}G-H6fGZQqBR7M4H7##&mg-}~&b z^2SOxJ4xhe*l*{W9fki`-h6irsJO)VZzj-Ooj(#IdpH&hnM>7{?xkLHyw&2} z+Lpap0joLEo^Vw=W|@$brDU`zlarm4J=eP0CFDz(?fY84cliM~Tw50{^MN2e8ZqCY zet9kCi(9~lWL@diSl#{DlXJ=Ohu_x4uj3oVo1Q9-jg|<@MNS?Xn4)lDsGL~C3f092 zDHowJ!B-%Y?^`^=*7$0_Gu&{H?!OS(9e3)jKUN5)B<#GCRJyJh-aL|xVj=%jZO>e- z=vdL`hio4Bo35EgNEz}GYR2{4{AfjUrNge1FO6|)19jVPTrVPV0{!$-Q5mC89xnjZ zB9H4M3{HB>Cge3(J@v`ymc`rU=fS-ANkZT=qKRealUFE`G7*#bqlB9#t2?OAJG)r?} ztwPz`@lIvwk-D9qEROTKU0akv!Ivcj5QieQ~^* ztx=^wKMFe#TOJ0@;NdxM%)Gc^>1lj|d<>rL4XwZ#QbGKve5YFgE{gN_F}{UZ2-!u@ zukHJsE)%Bk+uxPX&6FVBu=L?!zO?ST&)?C(1g3MQwUTreA~@cmySjuHMb%mH1_jw; z(I0{!D+MfxFp^wI4vk0eq{e(+9oG-=#t8m$!qUeLt7Cb$1jP=u&x`QnXzpt}z-qR4 z+2(hiU}_GJIT5Qq)i^=Yg8eyzc)_6^oXy7u1Z1GG+H9zn0;J1U4rTwZU(7Viromoi*5 zq&;Wz6yP-V3HlC?g&zy&02AjLt94TN@50RE3cDZTQK3s527}THDMBkBgi+qWgnu2B zh4(Sj`eP=o8RAzl2KIr3|@) zE?kqu^G0K^PJtb%!oX1NXG5VZE5DP zxt|~@fxIFEcqExSn(oT>Cub|1{eJy@T`W?fk0{jgb1a-n6y#DUcEpOHbScC2y1TH| zh=szBU5}d8uza~0dWoR`{|UOFA1%vD`OT;_@R^Wt%pK1NgiJi8UtQ3vdv-V7jiT-o z2cz81ycA~g`?Y8Vab;d@*`dH}=R^P$s#(5lHc#Fp#0j6;P!jMXmQW@gt_`+-Yn3Qg2m z$sUn?UzjQ7vMqm$n5CS)RfyFRP;+;%3D<<#GXX)6G6$;n+_^{V+bkcSl18y^Dl}^S z1qvlVW+U(G^TOTRuiF}9Mt7uciUPF_V-rHBI4l0@Q7E!sxQhaqcj7aXxfpA|a#EF&)Tray;&|S$i(s3tZk94jAwfI$m4d z(JiavXu-e4KomFg=1^S-5BnuI^SK-XPTtd865bI8f%S=3c7x9(RZ1Z3HxVbdV`aW% zeyG+BYP*^j*LWR+Lqt#)phJL@Yh+(e zM|)aaK4+K_xrzur0lh~2L6rD7GmpYU{CWsxb8uSdKI7qLwIJxiPR}!)+wh9>3oji& z)#oaG+#@d4Zg1pf@A<`}nvOl%8g)Hb^`g@N*G{g=Q!p|7!UbbnZD72Pu0~gN&RzRl zVi>c%(oFN>{mTL8uzVOIKt4FVazd(vy?&oZ{6_xaBXOc6-;1fD)%`L#SHkTcUJMcq zdboAo+G3jn%xr$=B}IGICdfgpqPPXIv-Ur8HEPgXN)bP#cIS=mm^{|UP4*1cq@YAp zd;v$gN&6(F4h(6CF*}opVovBGw^O(Yk!lHb_rE>QZmy5jjcl#v+<*vQ?uf!`Wn%=! zZ?H|nGu%qU@y{eack-@$;Gv#_xR5kuLz+vXR(Wcv2T_#d*sb32+Sk%s9(xsTdBv@6mdD1d z{!N@**|rV!EwC!8?)i%TGx`oe>?zNYAe85|s615*YQldSyC@l@G zjKfq4;>)a;jW!JG&nkn5ciWhb$8TYrIa9A}Ie0_d_+xuYnv^o_mKg2?qv}N4CaOL( za9iA2Pfm{u_hLJ~qXCrTOt>=Nm%=zFy)r_*H^#y$2~egRCvStp44sJHjJ2QwVKZW@ z_&nfJ+^t1OFLvl&SH#+6R;c2xrz}1xg>?>ZRG< zW!BPY+W!Dsw#qEwOki^f8TxwQ6M^mTgGCsW?ddwvUqhDb?c+j2L!JcVUYOr>3W?-HHSWU`G>rFZ z_Wys}7Q(zZUXS|h98{|m_lf~ZaDElmAuM2rr)ro1S+Bfxr3h8+98|?P88YlcBD}Go z(GI#ecO-4Y4_~R1o1j~7@V@`-1Z%081M7!PoFtsF-3gPDD&NLVa*+06Td^GQ%9ZQ$ zINq5St^PwufXDeSdh)Eyr3lBbUOishhB4#5DgJ2t<+$Z^w9VsHEPQ;8g!@HXzLS zAKvU9Ro8k6vWcj~t28`O=!>nBKBU(53jzZjxH7#_3gkK+S)EKyDo*9NXaxL08`k_3 z&Qd(=xi}f=lq0_IjW?(TDByD*+lYa@)B&nl5btTED zB#Zw;6oTWHt6Yuf&m+s^sK{wi4AnhNy)hGq#Yx^t;YCdAsPbcw>Z<+Ej`tZKOJ`f& ziSDuVaZzsQ0{^T56eq?v3^{UhR2|13e~@)>k-^!1Ks2}my_5#QCvT-BSHvrPF5hCcc_O5aDEw?EE{v8U<_TMc{U~AV4y7FIu z$}ii^>7VT@9f+(u!ZJ``ZX=BF{DXpS0xIeb-R5`7F?Ds1>d+d*vCklQ=fEcG_)seE z>!Y!8%2W_6oY1#xe#tyervXtRl20I0kImzQ?QbS}Ll>^=!E|x_zb^XE19Sh1(%FyG zmA2^Rs>(A{7%TV_!lbJ%?e$9!s>m|e0O!_8Tu}C;NxOBLz;fW5V>{tNSlr_FGY;n4 z3Hs*0#NExnKu$&qgJ|(rK8caWijf&0`^GR##c0Rm)eXEcM1c>Aq>=4e=jEAF*S=+L z;j;uchjm_MpW@k;6g1^YvgAM3vElC6n_)9Evp{SIF!%Ly4GY9;NLZU>BlV>~`Xk|nQtbeDfv@dpA zNB*Byjz2veZh?uFvN19U*bhkjgDry00!}#Zyr*Z}RgFXh*05BKc+f77yCY{9Bqpl1 zA@`hm$&ivkeYiaD${rE$kN)_yjdW`PO^HwogH{T}gM>*ptc`Zweukwy(qV`J? z=)7x`7l1x8rE2 zZ+ZJ1t)o_fvM|z@5rN#Wp^b|ZoEN2N#c%0KzMf~|OyPY9YW{q&YUK1DBoDEz#b+~t z-K~sF(GsAE^gpv_yRm1)Su_k~xHO^e1abA79?V0aB@{Jyb7nHC;RoFX`-|LXmnJ^_ zKHBV`Zs1%$3!eXfqVs9NEQVAMK3zbgoYLZY2z+ z>c7@G@hUj&or*5yv~K8a5J>(_vd7#=dr5ysjIs-gJeoM|Q6SDb4cP019|0LeptSH) z>X?VoH16tMMxyi8G^ZDzwGo+sTfc<0^BF3q2yu{7$Y}XC#nx60WBuyurO2XaBH80D z@hOo5GJlfde<=5aY51Yzh0$Q^Ho{|0NO?jv0P+FQJ~`CVUznOVFCjKR)>j$=5tI7pu|BP5YFdUeqpT4quSqAdy3}GGronvd+il9bOx8GFF5d?41Lu zdZ#w}PX70J?muy9=;$Q<6_WP$P!LosnpsPz>Qcm-@CIEGXEoa7^|c54GqdND%*w2X z+5^`EdmL8y^2sYJU|$Tlob<<{Zu(g5DGoJ~hwijA@7Ub@KhJU_O&Cf|DRNM^S@zs7 zLdqi7O_!{`Hl*b(b=VUZ^vBBxa(T);?4(1=ck&^-mY%{xDqU+`yA!8Rj45ERNt+#J z?yOi)WY*MZH;|5|ZDO9vO*~UASz^cWpVRc@@=EKWF#XiH#A@$Xu=tDI95R}7VPrX^ zB)U#oY97!aDe@QcM4^S71v1wbsz2^q-7LuLbidEtzX^8mB#Fm>4C)BG;LZ(1YtCpU$3oY{vBx zgu&S#x%0=LioS`FxnVE1;7wee#z^4af2x`xj9k^FNVQsj!6(aHQhiIWY&U@w1|R+} zI0oLq;&H;Ix}B3+0+%U|VLJt^*nBHzWN~m7n7nFpY&D;Oss_;@vy8!>*^VEg zYxPUAXo-1J?X7C&aJHMbDErI}#6tCJNmi+2nL#>E$4=d+X;dlXVxs>IM8DFKc5}(2 z2gs)_8G&Y7`oRVCxAFtr#n0WpKb=fEl=##VGvR6R5>0DF^lMNeCbAeji0l|MI4@6XL;L2~{! zzK#NR!1JN@iF}U#THRXbfwDFG;dC=NUw)%4YjOO=g2oD&S$BO(v2hrB- zS`gUvoG4A@U14F~kY%=041eAVJD|)?NkGGeL~y6XeME zNbfEhDGPl~(t|+|hA(7UJ*L?;2fmV&+3m}d=1b2gDIEO#@l{8S!}px9h<|MH#vOiG zw`u)%|Mn+shqeUI7+&iGN+#)+4xqMahz(d%gnUuPpv*cSmYF3a8DaaI9&R;|;DSRV zGSVv2vdNffG}1>v?|X_k3qoW{uuGy?$7K(yX2$(q3>!b5Z>6f-t(aJF_h+~d`jDD^ zRq>QC2pa$C^^k%rQwQwjFYFom-Mo@voQn?DI;pTAUr^4Dqk zVwSSY$p)P4vW3mq^})Nxn;ZgSOomB0k*c*H# zO5?oFq^;n!VLiV^nLUM#Cdb^&!-B*U;k7%Q@VbQpI__-9)Tb^!+18;1Au9kE{=Ro8A~I^_eea;-IGh10D37Azfg@<#>*9qLs&X7FFuU zcsBBYp(CZ59B_&*?oON&PKNo13Iir`S~$g7A~5`^%H z(-9!^j57n-2I(lJ(tsKx3s1@G*)a~B+e8Wk4&QM*`e-c8$DFr%TCinD1LPB1d9wMP zP|Pau#5^3N*4v=OA3xhfKiU)u8-ni`0Iq0-cE zD2MxbzURP4LSJ}MrnBuKXIioW7j^dHqQ^cCcGp?=%j;=F8Ds7iq4VQ8yRB6=eJqf) zygdK&m(tTZ!!A;7;Y**QNUN&P4bEZnKM{d5nYXV4!Nz{krD!#ds|{$E<*lG|?G}BY z;6&oIs=M+XuMJfEO>p@xtm;^N54vM6sz}gSmz)V109A1@JtG~3HRP?94~A6Pgt#&w zKUsz~KQz^qJ42b?J=m6E;4tUD^UII6UwCzezQRcn9z1{w$o@MCIw3p}3)%zi%KYGf z1v@BU$HXTs=GUH(Z<=6FV&dan}dP#zAG{idvi#^VT>@l>tlGG)LU}70_ZI@ zhaKbmU%+G&)Ms@?Nb1DA5%Z-lGD8m^Q^}p@D1f??@n}P6GUkNj1~0psZ-LAQ!s~*W z`54)U%0YIbx4vI-|MK8_{o-75@YpwawJ|dPu7N0oFXgUDbrOXT_Wn=AJl>S%=EUJH zT-t~sUk;pu>=p0bDURykb(%+-rM5+8|ATn>SOdD`(6~=36~cdYdB?98uE1ybodS{iC&n8xlqS3 z!h}1jXQv}L}iP#-^Z4K%RriPfTPouQsK9q^x(ab5956Y7&LL#d2>DknuSq0 zSw>7vvt0jdbE%YS!wDD0z7%vcdF25Y`-A5@n=?m^me%+4@zmHNPnlC?A{4=QQ+UeS zIo%9~b+g09g99GeH|yJb&Ogq55yES{tk682>2tox<8%12vYZnkFD-cfWD!Gs^h&-^ zCViJ@^6{1v?+%o{e!ICrACwBdWOytteF=TKMsEDOsq^(-`a`S88ZI)VO3fP2xt^fZ)y#ZGT2<4bNX6iJ zv)Zf1Rj+|j2nU{5rVpRT5WlL}Ebrr>juGmsA^S?Tb}aI%CL}a zuAP*L!OCjC4xWcS#omFcqbdH~BL_mI&ZOP5os|5e0J~Wsa21+~>6|3{b7SJ|yF+4K z-(UtN-rgBKq)qXy^UM@0pLo8Achf>JEj;!WIF8a=g2N20i+NibiHLPQsk)k9@>(l$9!qfJlkA`JW_%hsTL~eXkIsK@Bvo5^X7rK< z<%3!{tHYS1{#)EdA??H|dxgK;y|x0g=HN9Gm@7K{p<0ON{z}owgUhd+24Pl(tPp%f zv*YcDZP^w5kL0}W)PS6LOE#@tAd$l$A~DJi(%A&9FIr0%lh(@^Awsn(p?rQdi{@X8 z3IYuTptF?sc*QphO}@&fjmBN-$SZGaPniT;1s4Q5JMhCohvcUyJ5FNq&Fee8{MIi9 zcp);6!^girS7SoWk_mOvY(iCR8c^Nz;J~fyu*I!R(10C#KU}Gnull1YT(R2-?7a~O zxXY-YOBiu?abFI$?UL88oqH}mpa)9bX@~Fi7XO$uXeKHs2=QIz!S>;{Kpu=5EasZ( zo-#?#KdI(R!(iyH@zCL894J5YasyY_bi(V5M8vM2WrHqzE}DlLM6~62MzKmi?CDe) zSQq}yfu!BD;q?QRR+F=t#FFoTp_CiIu7D9t#_x!KUb>^^awqBXrIl`x(Io9FwVmO; zqt|!1PM|iiJ+%lm*6zIM?3rp}mHY6P+J2uY$-CaOv>F+UKz6B>C%*ZpdfnCXeHu=0#304H4Zd^; z$V2#J2fjYuvI&moY(5-xK(;;pkK{5@Zw9wnlH8gFcf$H}+uoX&mpYl=12Kz0uP`Z5 z@bmGVAG-76EUXzl#-N7#4BNxJu}9fnv-Y&-B87UzP>!Nd+PY)9Zky12}ICccP8*NQoS zesIFmMV>3@?9c~$<8g#JY|H`ic^WNRx(;jR+uN@yq%3<0e=V)*)Gf%8 zX|gzRQ$+uJ`-c54Lg)Z;k(pFIVkK;h4-gc-9y0aze;q20O-`Glw)U{rE3YO9>olM-v zu5FvPJ8iQpq&fcx+Ev6mm>+1s1(ovsoDmtlea7Lt%i2(mkic;8Q;^sV@5^`nWQ^CO zexa*L&0e_&HN-j}Jy!)&B%lamKS*N8J3EdF&# zLt2B29QC+Lmjc-6$knKhQ!f2RR55YE~0`J6ET0%JD_k zF~Mr6SdjTVwkrgxi@em~@}uLkUm)DzbCfcgEY+kZt69cko`WV?qaHs?DtZ&R`0Jee zF0X8MgWq=@-Kflq_s_=^_KiAZtGvzMWwXpFvO;l#^FJVI_4@{8VtlPvwLtcKO9&U& zP8vL+fKvfwQe|`a&GUB$O=y@)L~*K^svj3B`Ntj+b~guhZ(4kOe4(v?r&~*9MsN}7 z!~@)?ldRGpsCq=4ytk#`ywi4jiOY?Zo0#0@h<*_Svvq9W=ykH)%t0ej?!RsmdvKco z;xTEb(lNIaS~Cm>LDyYb53&Vx2v>>KD22p6foaZ1P(M0f7l+#lF9blBe=Mt$cKr+= z9M^t|GFB{txwJz?O4cQsLOQ!?PkFF86JwgIt46^m+yyijl!AqnY?F#A~A*EBVa z-YR-VFLA+EhqlOL0Z@9WPn@N2#&cveNt2SDP{%ff`JwXYI)7m_IN7&E3}g+qWB^nr z6EmG&q%Vi_TFLEe8BeLR{hT$;;z41EXSE2|&QPsjp}etb>$wMqrE)8fY-j!6+eP&} z%;}7yv)VCSs3#rKBdh6W2gCPVW{Cd#(p=OK=(}@)h&prZ>7(F)B5iZMeaCVfFQzAO zbWwNS?aRL%&@4|Kmz1<$eimQ?KQfRp?_ojuF1>!3pIUyo=C?! zywR^G6(!E6^9oQ+Lc=nX$@;QuoPS=t{VH1d$glV>#s!0IgU@2eeg^{&%9aMam?~#K z?~2_ka=A+6PH=xNfT}s#(&v*G+j$E+&b*A+(?N-l-;7OyFA9yQH4BEp=cT27-8!Tn zwQ#>nM`$}n(FoIRB>`=bek-^iA+KdZcnWbW)N0(j>XWbZ{k7t#TLKC; zfebMs{7d>uD7};%_NIfpWVYn!@5Ece$@kgHeHDwSy4 z&)w0;Urtsp($J4RlT|S=4HRCKXW;E=y%Kqa1u?5Zy$dP`T->UEqA2TVvujX3l&XxB zb~}Z@U4spPiZrkl8x3>IY?~5ndG}t-(g&|N6+);NW}i+$*HNPEc=dNy5VMTdYfWWS zUj1^rENW`krBqUF7diNmux;d1mYifQ@S(w?O;Bx3O>j!(4B^Zaiph&G;Mc<|vFoSlSq2#Wels{!eYeY2SaYS(O$kg> zGSh9APB!nl0A`^?42w1TYe2Dkk>jKgRTcwPoK5ieyy?e?EA#qQ9|!3WFwtc zJuLBadqp{d;=E^xQFX=bS^hju#M+rXaIY{`2ySU&mdmmp>%n&N%*NqVv;tN})$hEg ztTc-6NBLH5xN)_sOzt>Bns@YAq^rEc`7vPtf0u2w$Z)*5)+R z52w`WWD-B?QLPV_LV5!QU_?{ZgeXf$Vb1%!r-g9PAGRob^ekK|E-b%cSrc3u!cbEY z2UPz1FikhN;(on)zk|U(*Y!+ufIj z$&iww<>hf0+)KsgA&KMh?+a=DXwQm9Z|$F@k1Nx}q9zp-skaV47x;&r?B2v2 z+Btirs&qMRw1`}eBZ9m_zd*eB`kb!D1=U`Ro(7~ezMbys zF5pMcQnsu>1Vu!c8z7pqf_;DFj+=e3FXIXVd>Kt7UiYpZGDEkOvszXJj=vcFzvuSr zKDk=b^|k48Vyn(XeR2vrKhWON97B5}e1I~{KSST)M+8OwZarj9VqUKs94Ja6EsNv1 zbbZcOGBeydG@lWzFyjT{s^jr2D9V&wo4d&Q_oC~}1_ zcL*{AOlXNXBB)#6B%Jrucy^eFT1c+56~bPh2dd(b3{E^35EK8c-E_9dJ6OTLx93Hp z_eQD_&Wxm82gg7jck45uLY$RizzY`I9Zf5MmYOqYvN66k3|VQmPASvv?rug87D0+dDk(+^J3p` zSRQwKE&zspZD@+38|9RQc$1BC&2WgiorXWS0NVH9(i{|@Dm7Pbq>x2ysTv8Vie=UONhXGz#Fg+WqMB@51G7iViVf}AJ}pr;nYRLzYi5)6kZx4 zHpq03t7sM|4JZte^N!bGh#*R;k^zSEQx6%Msl)C51_v#;2%rq~y3>o2Kwdym$$yN< zTy%D6PXVwF%OpM2(ycT1#Jtl1PSTux;DhY$!p=L6bAkUc)G08FMU!ak%sk+qG!52Y z+?yYT#GY}xw#PC@A9P1H;cMJu;y|?VDBluvU7g-i2q5V&9M4sTfXZt_Ht$!hm5?HxOhP*F9UsJ!*ALKbSv;}|l^OO$bJ9mC9IxRdL9FH>D`JRwB zljzV_RxZ(~_@DCcv0MgC7JLRZI=x5~VL%{MB}OS}?#sz#$exBNOAqFv@-FBme|ddH zYCiKqTh=*&Oesdj*Q;b1ed@TlJl3d;_{#BmP!G*su)8|hrvBZDsJi_H-3B@6sZ7JG zrxZSVN+(}5|Bpp1g+R4fPz$R;1Zj6p2lG zC?FP=k+G}9sk1&zktLncZ~Iv|qna2Fl+c>Vn5O%;a8~Wae-F;o-Rsw{1GtG8&N$HA z;_80&WDAp}4M$rrnTNeG)(Fy`=2C^lv12fkUUIQqY~cwQFv(K2Js}Sz3t2phX=`(8 z-hs%xcyDLY)0wd)-v4n1LvQKtJY_78w=@fKV-sP&tMPSO_C&Hb6eO+_8!uzdV_-s% zepVumS3N!`V_Ct~-)v7&nL5mv@$mvzBe^B$TQyFI+ z>fD~3ziNW~&z=u+2E}s$D%cokIJpyg@R<~U*S0h-=8}vPv73aYsQNj!5DV&@jN-Gl zqG?&W-$Ne#D9HH!fl!#vQccn5ks%Z+DXf%qWaP9y^@$3F7=w)Oku?{?f_Hx*CF9dX zcKjDB2;)MXZb^{=q_%uaUC3GBw6HG}oW%VIs9@+*f%x&3XRZ%0L``Y0S+qpviPgu- zmbv=W5){Wm%y&ZJ+?%7OPsUR?skZQZN-xQwq=O{#IvBgbfBfxyt8@t#*T6K$?f@va z^)rF3<~1K)v=XzLa6@T`{xB{J;tFbo$#(4o*&xj8_%VVz58dCX8@c9Ak z>g13wQPFIJs~OL#U1xmygHzW+rm$B~|3OWLBq14(GaZUd(EFGST-c95*7L4{Yq$)) z6jUA$v=HcI=l-2AU#aAM&<+Thr73k=jKRzmKRT#^${l`4o-9h-ru61`Xx7qb6{b{* z(02vzq|tD%>eT<=|Dz&D89jqJ*PO(U0fy#`D7D$XY@g8ynoqa{yJho5{Cx#%-dypT zy=j@XDqB&8TvzMYjSc`Q-sO<*`Wyyk;a%}*L_fsC+P<2OMPN0BWGwFET%k$*u4X9wlC+#r=;1I9mk;y*~v&hatG0=<=xwUZ%l|1`0oHsGT;1%%-A5|{s3%Vz^Uli3>IARRV7`Ft^B4N z#IHZrMsR!uu6Kqs_Bs5nen&oZSCnP0psMKR)DkN-FQ?9CoYE3 zx0Tg>TN2Fvbrm>3`J`;w6@sAlCYaz7{YQ44SB<=wQfW|D;$K5(1G(L$Upk!39@Q`? zLWJiJlCp78Y#ix~FZ36K!hUo?G(|{(EHiRtG6amscI~+hu}pTtD~(!|^IyCdz(@o| z1L#p~2Z+A{pfL=wnsrLeO;IuI;>re%{mJ<=sJa7Fg*tS0Lx4nhy}w&mNrbtM~*~SjLECDCw71@P(OP&3|bY)|B}4_ zJfv`(_CtDFgEDXh2ie_?1IbZB3n$_pU1RQE!E{dv^p?&nCuLSiJfY_M_ z0Z;*6-s_SspSm4l^x zJ^M^xDKMW|i0L*2u+0sB@t0{d2F}M%ZWW4{)|h?fI{T*a>-A|6=}r;8=wMs2zPEuU zCllIKpd_gzwhrnQ#Tt*d(;&@Vwl1}vE-C!1`{JyIT1%_{{4b^^g4-M3HsgIHK%EUp z%1d7rXLfz$A)QQ`ZUtwtwCmAhJ>)G;19u4%IFuEQf2oSue(ahQO-_G2-T_Skv}1e; z1L&^*E;HNt6*TCgQus==A<0ZHzQO4GrSI>1rIm1jmS?buv9MSWCeSgq@)`5IJJKvD zv646ayh>z&Ff$1llo!!WK*IdblBMcx9_>*Y;J3|1%#53t?MMXC^=-xlJxQ&CIZT2*8 zCT#kPP@@Fy>(6jbFm$>?lGP2a6D~cgOHfc3XwuR9KeEm;AgZ=)*K|lp3lb7TC^1M# zBLXTgz#|Aq4=tf6-Jqa!*8&toN~EMkY5*mN&Orqwh6WwF_l-X9_q}`XfBw+rnicoD z?&~_w8K>I=3<0t6aEc1R6fyxnQiUS9E?#) z@$;Jsa_LOkYNF@(?q|KY*ZrkRaBqEV#c_^R1`?X@{L%XlsoU3IqMI-3kW*$5%WLjT zBYwuJs<7jEea?cRP@{{mN+~mgXfddPvAu=zDUf%aFMNM*mGDOyi!om*y-!$u0>rBM z3m21;Cf@}MViX(4a)5${DinA!R-BY>(8z#l9E@AP$AD}ONCEDvvG~H(Uc>TFTi$|w zO{%G2=P(*t4+>yKfEA~uJ>NjR!fIOD^aH~LjaC99aV0Hiq2sI#vFC#S_`ibxzgK{( zB4os~ZtrEqq)?|oX-G(v48C29cm@l6z`V)VqvIPU(Msar;>GgY^+YabZNt;Mf?>zeBiB!@min+JJK$q&+qyd&1Hq;u3=WpQ8b(o>{eeZ zLze7Nj#y=*Dp)F_OX9%i*yplnsSNb3O@g*cSIRn2mCz2xr^ciJaf&1D?xZp?Gfm!5 ziORjVeKhb;0)dm#XNx&5#-;kC>=OUEd}eo0@;O%q2Mz-(q-A*)zlyVgzEB^U7+ze% zF!a=33N`NqI}xgUAP)9?yRzEaJ6E-n8RZB>j)d67P}_xgh&XwL*?Naj$D_3S+?ofvK!jJ}1`lDfd+^M;Unri@T_!W6 zn^tcZi3NmUId)u)h=G1zbHd%|F6X&KPj|GvWqv?Md3}F-7yjLVI@2Cc2 z=;Vx7wP_#~!Y`2n+XtTOS}V8YvGWP*_L(&|gr_s1#~0}oc#agGWU=EO-WbMhNXl+R zCfpR=xLvHogB*`eOYzXz9n)*+;c$N}=p~}9vlsL5jYR!74%T1{Y;UcNUX&Ae7B@l? zaD_m}aNevqNp9>P9sPA(JU9C=Imr4}U~TR#o3yk}F08CfLvvQIsvkrVv1+{#Pj{g2 z*@1LDa?P!@1U|T{pZ$dt&kZTBfH3R(OGJk+w71!BiV=6yx(7X zsK2IZVyahF()%>n$T{D*&u%9k8GS<-dB{B9d9}P>M%6Qek{_CFxGBUx!F`!sR^^ z*NGXMb^F4gE1dHjP7&1x(L`tw<=&1r_1PLt5G_0|nmO?s{ZpL>JMLyR8M>8$y#v`1 z%n73AQi9}=M_`(v1y$8;y+@+^?rPs_=!g;1OyAVan2>;Guhj;r37dtfC(!nQ`s)dyb^XlGi7$_skh0&6G{$1pJoh{;n_=_h#B;72|M$PmA+|oRKC0w2g1?kvBhpnWYT&d2{yjSEQL$ zaM7lE@5h_w?x*RW3Uc4q;m*R)V7npIHq?X~))kwS88@cNMYZu~7#YD-rC zWn-vUx$!=WQlywQh0Xa_dgqHDoY32Wu`46KUh^y5%cE-m*n%E2AX6vr*gF*U_6rf= zyKyjp4ey&`8py-Q{N+r1?;yq8(4(G1X>}++01q3cYeI-kJZ4<_VF^m+e7DQHUH6WQ zo%NrN(XRUDp17AcJGU&b-2C=hwmhLn&Hjs3}=6nh*bXR{p4@czWH5}GksLj&dM;CD3LiMA zr@v@mR^E(HGn-XxC&1z!6$bW*Ft0MZ6v4co4pGX>2?jF1bC9bvj0PGZd{IxQ(nD|s z-FctIO^pjuLu*;p?~h(7-eI0q=J$%+ST-s)OB&K;P>d5zuR-f;OZ|0|~r>g6#?{iXz z!RpJ>`W>UCJk1EPvri^Fxrm5R$t`^IG21UDa?Z-lKC6g((H6g55SCi5=(&~cT9D1x zg&B`0L>*srKx4&F);4wNnd#qfFwxnc?pW(LHvFay<%M8~_$1yCIv5{}g zQw1F1GVBQwK;xh)>iw7t&P*~YIi^WT!#=9{EON80NL8>tU#+t&b- zu(Uw8VuGIpo$BAX6u0S$lDAJXj8vPNIZB=R&O0*7v}Kn`lU9BCsQWry38^^g^BK<#K~tcf#)lLMD?Fb6(`GPI3EZYcU?RH@DoYM@ z3%U%}STT{Iz2OX)20U~o85F|d)HVO|iePqMq{qAWOjZ{fszw(KrK&nS6fl$BIk8@T zwOjR2M~o@q#GVJck!`KKcinE*mp+&^;dnYO6JHbCH0XUVpesm7`OV!^$OB6N zxfls=Cahsh7(VjhkF-cDSx@YnJ+dJ+f-C1OWEz5Uuf}FK z51HSSh8RFfnMGx>9@_40(i0*85P0m?e>lJc-U*$5nV@XIRm9x{i2@M?TB!m z%#H5UQQ-Q(EqXeTpxndqCEV?WW_I)(BqqK{Mqd7If2a0(&%+;}=Ei=B5{*L^6DKUCj|M|FB^)YpE;JV^`pgL0j#0D99#=Xk}UAXxZTWV%|3Q$7D53| zYn_y}rVSW|czll26?G79in z7`^GpLxj5T`1I?`Lq|!2&*_d8<*emg+siLlFMyr3%xvk1V($rcUEd)AyrHeT#1n?Z zh1GX(p2~9UX&QbdKqu-{m33vR>YS3`!E-fibYh}hKEgf+^zqGIJL~E)r$zrl|In*> z`ve|Uj@~h>QtMX@r8Aa%nM!*5q2ndS?fVfWBB`&NRChyT2!}LmK^-4>!-H>FyYMOe zs4AuF6MG_ad{wceh!%x%I-~J`ul6-vqyU4?JRUOr z0pHF0x5nBjal;+^sVCUr^k#3q=Yhk!Y>36>NH;j?Jm)hEpIlQ32jY*I1RuTL#erTJ zOwLC9ZTnHE%fTr~%$bPHQ@>Lq#EA8bp$5M^a=kgwJHUqs8sE`8&<)1Wdd}Qr&E!LR zs!2L}b;nMNsRWWeNWFAxc+x2DYqE3Hdn!m}Ik1ww$KYT9=Ker9lFD~nEAukV{A)|3 zdavCo#WS7U=Cqa`myw%Su-ud;h#U3S}s@yZ5{)?H6^xF?q_5+kZ6WKwIw5~Pf!OQl= zXAxv~q>#qffGs&4$Q=cW{JNoBjuly!u;ZyZ`7C^LA(Z?Zj|_IFmgxtFgZp@9@oe>zNqyQ$Az`m$;A`@wMIsvr|_Aa4gIM^mCrsy9KYbB=$+&l8RUiu zjqd;fI=Oka?b84#0958ulBL+us1e3hWFkK7dw|ujh?#5GfZZkJW#do+t2KUi-%yeTVMSn8QHGh<6hH`!v*mrvbOl$73 z_8xq=dc>n?(EZ4asWf)}kWx$2SyDR-!%+pd#;?NsxY69@(vR^Vfs9-ir;ON)KyMJH z``+Un^+&mYz~%Tc*Kxr)2_GDJx_fNrqYFdkEz`lLITQVx1{{sPLDY}gzU;JfvKw+zoM;3N}H>h|=h zfEA7jq0e^BKG~*2Jz-2t@21^Q7@~==>fKRV-fe@0u1YT0c}kE#LX&ZDw^1x#`(dzp zP`u>pqFZE0St0jA^C~+iw6E1!7bS#VdcPb!naUtobp*+MD(0|wa*)$8(8@NtB2W4* z{Q{e;Lh7gA)nH_RAohdzUVu|j#_nE=4!6U3&-A}xNmv~D@hHt>+{vhbf@Vq5xb(J& zq=St^N=n=K{Anvy^#dtoC3V-$gT5wiR*3(IaMg9yxO5eudL0qX zPew7_NA_X9!}U|v3G=Him%dsT@pW#*kwe@2BmN)kwftb)`?5`QomppDSQ?na25>(F zaXbtu>}pOh@S53dm8ViN`%TR!EGhFuWV+Q9`2Wl#C2S}Ql=5s0leye9WkxO`l> zvU+tJ$maNvl}5Sp%VC5CzOa=vW`9$D(X4z*`S0{wBHb)bLpq!ap7_uj{wSku#XtH7 z83cvbfJwM3=!|`l+wYtPm%_`X+-dB%TzwxTZR(HHfw9AG4j$HZgxESL+hSN^> z1^Nsdr4<&g9JlG)sEY|J%>VN`Vd5(jUl0waVX>$&UCZ8b=E>|*&COFJ4L107Qf_5{ zC&}}lx6!{h~YeC%|8I&ssA}H0C|*Hio~5FuJZ1XcOrycmZxi14O&{5OLgCi?76)#{>!b zIQV4KC!?TC_II#Rssfakdsl#w2%WL{K*|9-m^BlE`LPDNCDl8wH;+;ZJ|}B$4YD9N zM&6q&f5mfi5%D&nA|wDXE7h7Oc2^n?TwSswu!o2+ zJih5Y9RjFfY=ttQym;qim(s7Q8?(&tPFarxdHXKK7H>!VN3z0=+>H35+NE{5)DQ<} zfq>eAscdZ6_40TY(`lo3Ym~u7?z~w;a;cir?`9EprM0$9s}&zch>*<}`&YkkyiW=$ z)nff=ma`J>E%p%AYV!n5Wi_|fNc)(xp~C60^!ybCdw&$err&qK9op5#bAKDEZ8p`-yqcCU%FC%QJzh@%CK>KOrSmAYU$wEoj2Tg#W&uE@hl1R9PW zZ6ga^j=O5~ixzmy^{wG`4b6BfIaU;31q?>m2? zT;V+z1S!6oal(p7-0Sr%>z@OX>=cj$%HEU zQ!0%v_V-LaC2wcvzFcd;O%%s-QozA7PKR=LuqrQPontk=I3j^3-qwb`v5<0Hq12mj z+q;&?b-w9p%Uh+l9{JUsx`c>FDpRM1mZ{$e6>ra0z|(M@+y$_i{&eemA{=n_$#>a; z5`Fd{E~ip$Gw%!8b&I*o>@`W>lDPYL*wHy~<6HfhdgO%To7ZJZ%GG z2y_V(j>p=j4t>FhWy?CbiyHup1Ur%?NS_a{)P@)pww4I8v(>K{kG;o--0v?WPu?O_ z1C0%Fsc5&s`8(sZIu`GaS{3YC?0ZtmmHTgE7!{!LoL*}#__sTbBt)puGcF_;P#tns zfS@>v)1L}Zzj%WBv%Pg^7b!n4uL9oDyuR7q3?hVa;To=AX*?ui?}Q#7i~ne5@Ogmg z%UwCLg9yPP_o)CJ*LqzI>#g=-T%+>bcC}deJWq{T)|U>tr6PrQqlLE{n(Hs}b}^%m z{B}m#GnjF#UcF&dXH?xmvnqXy-$}=|Ewz+Bcy4VgYq7oYwcoLzoL|2DDC>3OJu=5f z-b12qk;!j;aeevadADve%#S$Ux2-wUmS-cN6se zfm3BRc)5Z^v(9dz92fIdD1U5SLfA0=<*I}uNSz!GNRkA$g!^Pj>PZeKb_{NL@LLvj z5DGO}GptBa39Dz>R(}0mYJi>l=jr)H7Tl&1Wdc*7qPF{`)_u0{tHirzlc&}XKh3?j za7^gWpZ_TazLP(L=%>_Oryec7KRbdGxd=4RZ&gq_ru{=blY3)!r;lHmAnxjHc$3oa ze-va!)JO$Iv*Wez$jfE_I!O(`e7MYAj;S(_@S(#cWWW75vJ-*trjmCY8g?vzGL2hm z9qmj;_Ysq%bcp~0kBIGc>;6{L553k=I2{9igcAeSu3I1hfZ&Z{K!#!uA>S6E+I1)R zd?6DZd1m6ArX9#pv~mwW!Q+Iph~SXMr)OT}qReZV4N}LtwNnY1netwl}gr3 z+qVW&NYM;0g#%@OmcCT<1M$5sM9rVxt!7Rx?)B4?^KJdJMec*^X_{F)9_~LHqixO$ z_HlxdzylghDG_b2C^QrLo{+j3yv^UVV2zfMqeEsy7qP{jC!3GegX~UmHU9?VT>`EG8U<*tDE_ z?RxDca?1YEu{>A2gW@gFZIoxbYX144v3e`O!y<|Uy)OovVncUbG}7nJ9OALDCB%9Q zRvPdZL>~yQ$W(q=O3v0|7bB#$10~`xEL$W5DithL~n$hX~FEYztuCzJOp()OPnHgf54qUxGI* zOSf(N-g3Drd4==8G!&nF0hx^!KnJ*#Mp35hS=_~oT_WB0padqnt51($#Zi*Efsxl& z!no|zL%UeR>mBlsFL5c|*Z@|(1@YXsWFsRlCpLa0{UHngJA^L-@GUG5EdZMx7j;2Q zS`=X8f*EY1ZZ}V0<;$=lIjA{eIZhCB0dUcw#DrUc!xf?G_*D}tHIwqAJpR@Hr3f0- zADO6|0D4>#a15&q_z9g+F~A;R>B`Z!fI^5S@B|He9 za>ysY_=i)3YTVAq>hKHR!aE=8=yLmIrgQ=C{voV#iWfb|fyT&y9searQ_)&P7zPsN zuUo!ed(QOR`Y_E(TU`*{I`4fxa;LtmHw=^LK3lokNl>8Izc4M`UsjR~i{)c6Gwxn+ zR4`Wj`d<7(=`YW|tBRq2C;xbt$rka!O-RdEO*QO#xrYQc1a;DaTZkDHh#;`@@hNfs zLV3l`j)+uzl}6yFbCI3pApqF3-REXQOobE~c1>ApJPv07sg)qcTw^OB%c2tCV4w}9D zydj zUwccoFJ@&UgLHfZF(Lc2`dUXR5$vKhp`1pXCimI@C_T_9D6vW!vblE=o?^_P6cU_n z=N0i{SvufZ?$MoPmH+SDbi_3@Jd*;NQxiZ{;l5>~k^#5xV?tOt z-nKoCUP9U}q;dY@aYj4ejbAGme_qcbRWOe=(zNO23dYEC$rjwJ09+;KVZQ_V3sApc zo$SmT!hZ!>NW!1;K=8JNlZL8E-Cn@$*pOv+^11-i7j+4dwOuvu2Mt-n@4N~rV(dD! zhVKYMYBI!0639v=-xtnrRCh}hAmHhiycPSH7b(jIXWt{HNeK`~b^bBU;nANT9zlXU z4a$yqazd&li)5w7?j=~m37PEfchH1@T)8Zh$dXMn-!?|52L#eO3tIIRh3JF5k>Amy(X)q3eM;$5DfPV2|a-Jin7zL2KH0Je|^AgV@$@ z!|Z6eaW-^L@bZxSgWU-a>jPavw0{WtBd?MPrO?)nDH+n>3q<#cR`KQk_12<7MP7se zw-clQc}6(TUqOV3E;={9(g#BtgOULZ8@ePsZtN=@APcp@&Xwk8uW$=7D2pnY0KCa4 zn^S>5Rn&vjEp3LE*T>Yw(A6?nMvaNXFhj1BPT~({2M-ZU6}t7SkPyp2q#VJpjKs;n zPA{Pc=`dp-l_3JrQdW%7YvH*}xL$6xMWxB706})Nvw-<^8aKT^Uk5K2K0?HJ97{X6 zVzQ0WwS0wEFYyWt?}=+=;Hmyq)t&zb=WAXs9#nVZ;|m`rVG{TRj~_gMVgwEsza6m# zMro`^Sljr}7f?g3@!9+5%R%bvpTU^fm=upxdcXTrR0j^*m&|u% zfBeD4{5^j-7g*m0Ay_5%y#0B8c;`~<_PLBUHk3E-CPzz=>KXcwS-ce3^f|b6rEhU-pYpFbkihxM zD^hTHGgg$TxCvYmWomK8hi)}BWs8IwiS-yedstxU6`jfZNpMC>R|-9y1Y7C?$^=_j zWxkdIw(|*G5XlEVIygyYPL_Wai^!1kYkTD(VDbZ_P=n*cu1koZyWJ(v(ZHz3BMns| zq?}Ywy6B9AXYVW=f4GX#3K11Xv2o3ZUF1^~`&SxF`!=n-Xs7^%23f*c>sxd8%g|4Q5F2_}D>uN7pG!xNXCE_Yl)@5giY zwt&U5n8M9~;-K3mwZD{9yjUK=DOv>#(j93qPO%*Vj2ss=Rsi;&mx|~^>-opyBEuXK zgiKzm1EyY}Bz%9vvP^`UGt}WDq*-}O%1sdjONxWz%j`C)~3@FexeS&1gy#0hn|+K-bT7i z=RnI#Fg2!s)xCI9(lqB+2dJR`Ypc-{&pGqilvQs2j!~LHObOfBtpH=*znY7#y z+l}iS{O&|Djq#%28t|B0bqIm0N0tmdbJ#{+X!{>^>VJ+oCkHjEdcx$@r2H>}p-Vrj zT4hhmJUfJLVQWz}FoKMXt~8*2>bkU6>jg^H)!Iaqz~MJyIMA7Q|3Vl4_b2wNSHKe&<9}32X0k&F;L_ z`fm~WpNGwD_&-%CtWr!13XCYx=ej1AHM8G`7~*j^^xvOu&isw>Ea4vm*pmbn035aHA{4d${tOyC_xC@k~3%ce6 zjDo9Nf|XG!;(-j-hv6)8ByIvoyLE1(4a=+5LMzsA0G-jj(on<#;gfEG=f|G`4|;m| z>PWP(ps}i@W_pz25T2u+)Di6xhL1h_p~3q1)E!YaT$N6Bvv}ens-E|Vj?pZVmFTw7 zDLBun^#GW&v{LT>whjc0#P4n20L@{FOUWNW4)C_vMM~&w^yg+FH*Zm z@Wr`RrSiz##+_R!w=M1Ze;^~pU=#*Nw&Okff~l!B3>e{JRZn0wK8J&E>5!n)%FP)Q zML8G&qh&Sw7<+3_4F>>j!!+gf%bY>bUQit~FkPR2ulz~f+AoE$P(!P9(fEf=0)y3bEvOp@|roU(3Qjza+SiMAGWC`b1 zQi?^6iiXD3g*&sh4#hUK=j#&iMIc_pHu?vGoST`A;)yLyQ_R}Y42UhsA=e+Ru_8dt zO9NM+mxDzQ@yE%(|IH#5WK3ZPXt!fKVEw^Di83=N;HCP^3IRA;uYF25IgG+L#`&qD zuSNtd&t>;N!gL=gRHNs*32uF%_h`nE=HuDQ!j{Fsqs6Hji{%^r;R6=UpU!A1#_A@@ zhKO+q6J-Is1HLgL5_>K}<6c^O8%oop=r+m2SHG#T#YcM-3x@T4?O))CYDQQ+JpH|4p>W8a_Vv#VSZdtf>3EdNoe7^08_uVO+(3Sf zjLk3`29v)97MaE5UvwDa_Vay+wC!UYAE07>t7Zq!r9K|o*Q{lt^bR4m*HF|#(H?If z7r=(3yU=i!PsX9e}6Dw=r`eOn5 zNX?h6M+cYzsa<6qRBa62GB@loX^o`fRSXnqIg3yk6PIFX^_bdINnD0}8iy(Guv zc=tHlxi@Hd)=(pB@2nliQS-&J;q>72#DeInD@y1a&0_DH5Ost`WC8W}KArhDiss^v zTMi?>2LcZRU&}26uEnolN*2wl9%vAxe(Ch{#u=}yqRNQ>>4s>n*GVMp!i-UlvoZnJ%{IS74pKUPvcBL_Lb}D% z-gi`DYS0qHfp} z=a#K8!z&0fjTK~Me0B;PWvb*ygDsIN4AsLz)CFLpaT(6M&1w_^kFrA#82Cr7nHlgz z#kIjm(a(9&|1`@3_5N0HAgu0GZfD|1sb%q$i>ued8{qSf(=onp?+nO9o^s2zr!hrf2#j9q}-kZo80{mya}kw6o9;tmAZ;_RTG z7Uj`jg0#{b_~MSdXX2vrgAar&uFNp&wzDH-3g!*R;?+3WD29L)p0R&$(TO>wg$o6_7Mh{bNy&Fs z*9(dj^vC*cp8GBF5M0lbb+Y|o+wndhC%rFj_UK5N07so|%9hHO^*RT-?+UW5 zwUI8k`&%zrv%DS+R&;heAU1I#P|xJ-Gdnt4pEO_0PLIIuDjsw-ZI_uIUTRht$f#b< zsqZUfk^-nB8{XQ_J{3z$EH=9f?|cOd+N?@oxYvQw%V+5V^NXs&>#dz&|)9IMu-V+%zt!OFL@U*F(FITx*0wF7H8X# zKMa4XFpic*$x^Vg-(}`x{`jgiu5MG2wQeYcT&t{E!dtgDSqpR_y*M_v3}|hlVgz$~ zb*2*qVaW=d^hD?FTfC9qS-Po5zeZ;@n;)dPO49=*?s%UM(*lddOU7jYUciF}RhL&| zYN)I!?fkpq7Azb|3Q~Tz8^g!0p!0v@kK93wh1CDJVQB|&C9oiBzSVZ2Wj&CF;^%=c zuD@B3h;R3n!RYBKGgdQ^wDC+zU*I2$;V1C&p`)O)qcf4?lTBVXDMpY%9d=CQc{1SQ z@bI6XtXc^tDV!@hEsBOkDk^M<_j?Sy4moS(O2_CYx-D7-Hi7=9?d9Ok5#-CITCg%l zso-zX%swaxQaTJ-ikdMgwbn>&nwdFi_1DVaJd*2$PMCec7Cu;zC!p2?_099Sx>EAZ zO@+qhzPs+ymWt6Il<6-)$xrGy1Ic8D`E_v41K`-tHEh%(*Gl1}z?oFGq6HQI)Pkz2 zTJtzTyus=pD=g?7Auk;1>KqEb_kkQK3*cg4orjcBjjeiMb`P=zd2G|V_%sq0hRV*a z)Sb54#Xo8Gv)x4&T;N>DZ~|*!{SU%|T>Jw1Ve$7&CJm=4N;#3t++l~mO=@v-<>Ww{ zdz1ex;GWxf(CE$D8i1_{0cN|0`*RNfuZk!&L9$rbM^Zj?s-8 zC$)PkJb3gOPbju1LJ0N9@x&LSv|-B$#u%l^CZz`^d@bMoNI@-)2k~+J&XB zcKPoo$}RS6WQ1ClY5m+X{W3YrMq;{LH&hO#QgH*IX{pxEx7{+hx`p>iEN%J(b)QJODDzPn^1s=}f z@4t3*I5g;Y$Y*A=CWmUP<+CH%N7j|3J1TAt5))z{IamVd|NjhMFY)n-#xs&Dq1#P+ zY*ITo^|)JXsSX$`^WPe9_4>kZU@UB{jH&;~Q|9pam~%f_EJP>lZDP1N;prYlj{xT6 zi^{tFKVXe=+I>%UP%r8y*>bcAvbO|FBR&R{xrh*%sV1yFX|BfG`YsnE+Wp$gk=9zH zp4Fcmd^9+ww?g@FWYoMD>-rvUMKoa)bUsCM=EW^P(fl2erQ&C{rILNFN1T8P=2_SHK=vOauE zE}kg5Ol+e(r)H(seDpl1!ta&r*nF>-MPoYy0=TMi7WMC!c`S@>M051ed_{j9J-u=5 zcg)5QK{x5mM=mpS$ezmJEN7Rbwg*z~^UEr=FYzo_1iub=Jl0mkocy_Y|W00grgRk@;;RmJU-?yC@aVv0O(_)Jd85D4O%vu}(BmB8Ma3|yP z!SU%<&=SQ1TIXk{mUZaTF%rj$rqx?NTQ-!=Bw^V{wsX?WM}I-6hnzZW)*! zNr;Abp6~CmaMCT6(;`XYq~|O}NjuT5oN+GhI$~Ho@|QXS4#WNGxbU{xAKHw#sy@V* zThCmV=uhGGt%f?`U!j>=_!ARF90y_VEyXj{2M`D*t+bBg9MoeacUSPAA}4x%4a{Qy zucqAz0|vde=KKc#T=yoKqgoI7K64)bC8|clm!rwS<22qCo65afZ}#3beP&_rldZoy z9;9@S;JBgU*XD~Z`~6ku&ZS9@VfI8XdrFEC9>!YGSa7A?di2e6+v4R}Yl;MsaxP&( z(%BE6gTg%q+eBZ?qr!{d5bmmpWE@yYMF!-%t2Qi51zC#qd;e%AL;3HRqH3t zUTMEw`z4NB|E@1YGnTdS8>{J<_4pbJ$Ty2Fg8WWdxToZ=IX$BOTg+ut4aDML+jQPS zYzHkwPMO@$)&&42HDHBGq{lgRbw~GyLHg-co4V0gbdHwBu*%QvY=9P8ebKt7vT@>z z{h}^D_NWRUYoD`RbRte)P)9>ZpP?Gi{Z*hPdM5Iu7mota62$TNs;Jbz=3k9zVt#zY zh@;tb_)>Mi|1EO~&_Q>09n}u{L-$dXQpgLB!oEU6HH$g#V;Ds&6?aCvND`s_RszI~ zcXBXDO_Zg{UZO$#K)z>pI;2gD9efcPc3g76qLlOB{a3dL!|tqhHpI}gqB#(}jFP}l zN{4*Ioz=6S*UMPZ6p%m*T){;#zZEY?s$|y0=a-t&aJDOP!ZD-WWKGP=#kKtAeVQfi3xfYpKih))oGE@beITDRX-CLsc07t> zY#Dw$qo#D`bu~!6jBaGDKZwmryGqfOP?>I7V}49a1~; z=9J&fyXX#hs&ycSVdn7{(6m_fW*HBWk+a_g4H~y@dvt!j8>`+d%{4J@n<%wM7wy!w zuO^`iP7;Eyo{fj|IUA2N<%t=GsO5K86iGq zmkkBmjnd`1U5Yu9=7thNrWPwa$-KIIsOSXha4zVqHVN5Q~@ZQx>hZWfS~fH!oYJYvQGji&^fsVBzrTV)#u_tKx4Wd7%3E& zzz$d;G<#yJHc^cfBNq&RFFhzQ?|5I|tJo{AV2wxl`kK4@A~&pb#o zrRhqI!;~7R;9;q+6$i9yGG#P&?0yM8u`6!ly#Iy&4;b?AH2&Bp_~3rKq@~cXAWxpr zmSC^Pf*W5EM&QwHCajyw(XxV4LYPgDVt~MYyppqtIBS?JR3fvz+`jGynyC1Ey&q3* zPPi1`H<@Y8(@Nj2Unb1^dT%HaoA`@Ss8jc^H@4JNBCgaf^sSwPml>CLi75|%mLXZ$q zG=at?@5L@Rc=T@WPH$Q_w!r^h&TcMmiy zZjN2fC*8`)l2+~0u#8ls#aG3#WKV*)>wn5tKRyXnj0y2^S~p-rcD(2kc?ocSuQ*Go zKn#H=5vg942q^T|8WqUF^!tVpeZh0I9C#(c0AC%s8y z@k8hShkJZ`JqtLI+>Y0`UpS{+euDDXD{tXb3LjcdY~b(fr>;-K)+?817UmTu$-KSz zzfzQsPf+NvyyNgCPV1^RQn6%Q#8tVIH!BwE1tspvU#5<0e+g}IN;@fHcB%Bj_5#iA zlYK?oTG1BFRg$kCrSfj`ENWo$6@OEW`pK&qtV4?TDW!cbG>3|q*0CVVqK89doHZhx zQ4&kn#SJ1#*n-sk;8o|Iw)8Ln717kN0XG-;A}=Os)ZG2FX5N*jsSzT~?I^wb@ZK6% zl}OWZS=Vt6-payjhstRWT-F^_RU4kTmiT9FsQ%#PAm716dvR6gMT+fE#l({K4?r+~ zPGI_@H2|F(xp1A*rzu?DqYjS4R%z-ueYKT^4{zQR72P=A=P}Zf{&3|6{|!a>=17A! ze{#6fT^-rCKAlN7(1i(pyojTCd2Qi+u`iDq0*@MK2;F>>%`^X<(z zhJ}hj{y~y#J=bes!08a~94sdJ*)-8oDS^Z8szX~G6q+vi{Ji_?bM3alPuNdQpn6b- zo&hx;Ff+VRlV5d%7uy6h*Ct_`d48&pL)Bt>42{^;(rH>kL~WHG)@TGcLduju7Oc(dagNn$2{ z{_3n$`J%?N?Z5_HhM+(Bf7p8OK&t=#|KA~GmsNK5$T*zrP!!=%W*zh3IFeBzSs^3Y zn`4)8WF_aI%#M+fgFnr;hn@(hRqJv|YaF3UzqgN(#=Wx*Z{qcG$~}tH7s3u>j2BZx zs+2;BA_!{{O`YLaO2p1mu&}r3e>1(oiaw1DdR&#=7qsf2&JPvxy^Vie68(6rIz(19 zWRel8_0i4LosGj6x+zZ!zwCRq;$h%RSj6mq(MBOkg#Fa)7VQQN*%+zwkj;KQyBsiV ztVQ@#v8Y>G&LCVD+l?Mbpu^lup}5zx5~fqJ#d# zN_}*lluLd6rb0+CP2M1*;Ou2P3gZLLK2HSUG|)wEJzLPPqFCqM)CW4?) z52*7gxV_0wgCkG%BlwA{5spGt^!-D4c0{6rIMVwoHxZLk#8*wb3B*687b;9%toBfm zPZ&S|V0b4sFE0KUoVxu37I=s!RQh0Y3*A@Cyz#)pf+ zVh3je8La-w2S$5U`u^5#13MDLvBPib?jAyQN;HXQ&hk8g}_r{X-{qnVLw1yIp6 zg2>6zJ2gSj;52PbzhHIjIeOxRv!sd2K&cGytu)~LLt7lY`gCScu})P4LF`#qFpA+# zf}}|Gr^l}$(Oc{Ye@#}HML#17&%~4)Ev=rO*qLy6=b?p>)Rin}z0i-wC<9RcW-9JI zrJOZXe_Dh|#(R^t?MHK$ehN^x%#^*oVC2TOgdSBvuCZsF*1mJ5MYVr^Kzd{D*~qkd z)&qt=Mst7Z9Fx|_CLB8*X2oR-xp!2D(ueUA6xKn2jN%qdbFj2hs3nCZN^=KNo^%m1 z@EB2;RBAdYiaO8c8a?BVnWlsYCpUhimn$AT=xc*ED-a8Rm0K@}MK?;xK190;80vitTmEeBI4 zjpKx-XZQ6)?_Yhi^i{ESci$VozTtm;_-6gs^4XvD+E0EEc9&Zhzg^_}Wtnz>PYGH6 z-%?0{zJ?*tz}V{RPWQ_C9`o`l(Txh3cghhFak<7WS$)d^6|O%yt&X9s8vxH~$w~$u zu4Z%++X||Y;XI4TYZu&SPf7-bZ8~U&1kT2jEj(NW=Q93_@{7=>uQ)>bPhyEMfcSO0 zm7Ro~OrD9zDNqGD6Xf1{sJb(N8tzB(j7BcmViKeV7?}TuT>50RyuxB5(o+1Uujk(W zm0#j723|aohcjdwhhkPS;r(-Wa86t}M7lyeuah?}K?vYNu{S8@fD9J?Id(T|tKS_s zG5+(yDf8G$q~)l&NEMaEP{&vvRJ0lD=JvT%h&G1@c;cxib0Y9e7W(_BTdlGc8AWo1 zn=YrEa#;(|cYX;C^i|Dsu&uRp)#M8syV)bT@o;XI|~fW{*1AI(I3xPPEzI0_;SzQ-vyp_ajAMnAZIX7&jR%i`}p@4q>dtI+XsW76V@Mt zpbhGnqry0wIL{t%?`9?MmiD2=ohPMn^%tyr2Z}ofIUOaM7HZ@N&o=wSn^#x<83EtTR?Pq(;b^cUnj@E43J=4 zQ6$g()G|QICxGzslRhZY(=s@^?*#EEKU`E`_RN{HgNHYkOp9ajIwSI+wX^g6fe=lM?#DsPN=c|{QR6N^8XKN zVrSb0$gmieVN@ZtK?Ws`mj5b4QUs=$z`@Wu7jussze%bF@mwrKxvUc{3yb>z$o+pm zcFJmpT@%-35f3#?m6L@LwFzdV!_Hi12!M#17@NN2xZNDpIFxY+t3=m;TpK+|_BQ^@ zHl6W)nwMlQ@EZg*g75yM>C9)@XEkS(qiW?3H`g^^52~G8< zhA`|}?+gNR#`D?7qt(}krRpS|7@0PQ&`4IYi3Fug{lKr<&hFbmO)fA2gG{WMNS@hwoQS^v zeo7H-FuXqk?SyV%h5KHLy+~o(RDo$95q6y7+y^#DD)>$|We$o^`HCw2xkJ6q%K8d( zlxcJVtQ_d)`N{`R6h~J}CE8zN(3u}T{i9-ML5fY-_(3zdzR;Xm_9D+el%Oz@vf|mN|3A$VTV1U?^ z(U%KAE&VL3KBaCuZbcoDMT73XN>;doDBlgh^-vLgsfjeW%HOf_XRj@7Y0Gz0?sJ4z zPu)<;pwKuroYFnU>$g-~x)-sJ9m_doHpQ<}>SZ0y&11~5jDDiH_a8tQ4-t1 zIo*4rFplTfqh`gfZj*Aiw}}OQlRTT>NlShv&!6iou(wmdQ1F@pn;@`k%{0|f^-Akz zHjUOBgnW=Ll6nukB5>F?Na_GZGU2tdm%vh-LMeHI%B7Dimfu_#hFVNi7cVQm*2(E> zWSQUzJ^xLD!in7=Fgvny3LAFbiP<;(uup|Y(x5YiXu*2}=X>G+Gv<&?a&Eqq<6_k8 z)?Lbg0_}BBT=+}HD{;UGm>fj#1;gsQInU@VSP0A(by7YkuF%3N4v$^1d8>QcX0CYg2A5f znu=IHl!4#elK%78Bjd@pGRjWQo#|5LB8mgd-v`)5-k{nIcpn1uCen6swXx+xp z2}rU-CiU&J&)T?IC>gnQNFc~*L=G8s&+lr_-P=dGy34M=s8>(_jzn)uz|F*3%J?w( z@{u50ep7}T-S${veH=uUzi9=Jn!b}@lDpZZp|JAsPM-79(BptC@5Gr}PIK6553I2@ z?eUs#I!>tuU*n@VV_I%;(B}o;C>D|)ob`s6hUi`U{h3A8nRCBzrmpF8unj^$G< z*$h8-%)1zkI~$=y>4Ci)&j}(zxm?WU#a@2hIqPC<)(KuneF}tUbnpo9t>v=YL|FCY~sPfqW{56`l4U z@IDEVNTK&BV_0jkw=(IC09PK2gC1^apUW4fhp2*S#Vl=ff_e zGRXxfAFZtD>YmwP~>`bsfI)DaN zqWV<3r8uq&3Ui9{94|Mq7H)Y!&N$pmyvCz*zJ}fcL_biXxE!nqV~1fNl9n4j;x*$G zZWw1bu=guMSjVak<@hWWC($Uvr*%6(%lrKJf_ZQHHz$Bo{vB*O$gGpOk)-SG4<9eY zCrufS3N;kiJ)WR)0_*ctX8~d+h3FBgq4ym!O3m~&Qgcm87pbB~@m#IcAOY_mXTx>S zwl#{1=Ai-sN;^_^iUipUoy-fy0#J2Z8)ckRuw=X;nyn^v#%cHn6&&~ACB1XjZhE=9 zNzMBUO|3(ux}r3;8eqdqIWSNbx2afiSYILoG2mgYPWT_DBj^*buup)wK zs#eEIveh#}i95;D;%G%*@Lnms}CsB2Yz_bRW+o9g?NIqYWd}1aL~2n0U_M3)KVeh3QbM z?%|QNT(dNFUpB1JCjJQ7k)1$9N76}L@3fTG@i~J1fHWt7w4%&IK%zP>x})U!1ohCN zw&h!A`27w<#RT7WP^Q85Aps(f=B7ctGZuRudL-Hsf^jbA*ZS~hy<`9qo*V&Z2kgpV zq#)C-dW9-w2NJ$YEdUo3ZSnC~7PdG*m<7iA~zTTG*8h=X!fy4>Pm z?Vagb#ohxpZKDm@HpS`ZmhI^CWr1UT5n`Bq0f}3^;*A?3bo?0(GBoI_H@OwBs7OZ9 zUWdI7Sk2M9swr1qvgF z`u8yXiy0`r9%LFXB3HDErIVT1fs)2i5kW3{DyY>s+3~95?$WQu9?hcWsLG1$xBi^w zMK`@x?Kkk16??*QWSI1x6;1K3rfk|^C{d>kk_}Do&-aeoZZu9o{tk!#UzrD=#YbBV zLiBvr`Mx0-hs6o9(zZf;l&nQ4J2*|BLJ>VSxn#rpySv|5u!b~v@t`E(K-+D z+^@LpWX|R*ovt1nCo2?D@!mX!=nM)DW}>#ThRs`AAp8diPK$vxcc37^eH-F+yu)egytLgCF5d!3353qjw5SpI@UP zL;gAb7)y1fud3#WX*QBdFBxJp##T^J!y-9_>=jqIG=wsSjY|7^V)m~@Lc5sZE$95L zjdz@sap2;HioV%|WR?LHS>1s&`S?nD3y-c=ynA-vEZF!dCo4q=tm&Hv%LE_JB>EnN zh28V;!7(_HGRs5>wRjOLD z@5LKdxx8Sr61BAq>8H!YaN)3#fiz&A5-&1h3@uaL@sv9=Scpu15OgRPlv$w5ehS_S z;Dqg%$NJC#FNdoyq`KZv*9Ui~@BMKQ@vP%l;tm7x3{XFlPW2$c(OcD<3jD;`TL(yJ z6cvU8?L@iM@A3QDQj?7UcJ{AeCcAGifXUYqe<#D`TT~|~NA>H3RsoKUO%qUP$rR>`%chvpGeBS-L@C52 z3n+e5X12F)$LW_i%Pwgo9;G_WxiPnV1Jc<_9lrD{hYo#h;uh1hPD%xoibIf+De_?W z;^}#}w;1*s1{f&*YL`SE1TTHG!sXOaS#*jEU3Rwa8%p1Q>-icJfyh{>3reUa3{W8@ zWKzrSS!c&ZMSQgIrKL*Wr&dpsZ?QjiS%(6@Q(=yk-yuGwEMki%)#-c}tc%$Znq;>j zdN>erF{dWF5-p22-d6hnjp#kuyh!!oV5lJz83)u%3^?07ZYN%8oY?##I*pSgPe0(( z%vaYD?@&9j+<)C9q+a_r*i69|4}snkl;3>L_AE4lBD?uHbyyq+jI|S%$ zY;PG7ro>iV^_UE zLU4^39FVNUCUqbzQ=v^~mrzJ9bH|JorCVO21ty->z#NfK@n9-4L4)Eld_n zdpwf!lAzRSl~jEZbAC2D=M8&k0C+X?#@t^mJ_hhG?=}U7h8tSF9asz4Ee^H4JW{7k3aH)9k?lRvt_p@E%`iKil>0_9gHuVZ}uBR905_zK(EL zBv+Fncs_Lz6Ug9xvrd*G7S9i)?cl4`G2+V(d(t``isGsqS$$Y>B$fjrAs4CT?(cv2 zLK=O*eD4Z*iDB35Hm~bOiJkn!mnEA2{!5;$5)k6^nhV|g#%jpN9<1a_F!mKgn90>q z#vh#YlRMx5ZHFSX^cyh?tmIZR5NsqZjc|dg4ziP?v@nNc?Z4vl4?*8uicu>1zO2ss zX;iGP4gx;UFlh=@js0;Whi% z+jlLuJj4)Go4G`7Kgwf0JEteNTtcZ?$%80Xi4QLyH(QxfVM?x;09AAy1WreE^Q?>|oXV6O*)Luz&dF>Ga5)?J4NzHRBJCXdWbYe7<6K zfA;GL1mlM55va7Ye%o1Z4c)r%r8aKWzVNjC6-%J8j~VOt*_-Y%_@Cj5i!JM$~^tR7M2@iZ}`4ae6!- zEoR{g5ZVLH)e?;oYr1KBzKBlJ$-x2_)?9^8ZKJH(cfvG~A2%pWTzWD4O|->u*7wANU22E}6-~ zX)z8a=sv-V>A_rF(odPDjps`q4SLoQUWC*zf{_sx7Z_T^+6Fwk)7R{e5=%zpi8A+}ziiW=zQc{YX z#Br&@I%}`Kj}I(r(Wq~}H~No1K%ya=4Q1R7LCMe14l${Xqd%N|C6?W%PeoQmM|)x- zE3Eo#40Wp6e67cN(a!gru=fo1ceK$b46LzzaXg@qexAsttia=vHGdqQgz4djXNmC9 zU<&zoH!bAn@HGD%2|RBIJ{Cce=cqS z&;H7dzV;|=X>nintqYXFfj1rclNsS4UX?7(kQ1qX>U>rc=G=l$b|w7W+K0&h`~y-* z4RANuRHwUG3&VkM1-?CQo^t`ZiDFCIj0h49#qGz{1>H zepQcL$I#Gwv`Twl)*5UNnmJMZloEBnL#XlmfG3;!G~Ae0v%g-la< z6e3*oPWY<&!YkXNwUIBCY8acD_n}BDEuz(|;rHDdbWrbO1Ir&l+yT>m$^~e;`pZlL z=hfO=WWC}NukHPMF@p`_?TIaX6tQ}(C@F5bcrsOCRHrob-2G3BhcSxcxVxOBGi+D@ zeSuQRaeK@dh0zA-G%I=8=)0?@<-p1(jT1M@7XRSn-G3HyQwsx%<^RViG65japl{}Blv^p)=MxcuH-i&vQa>GnxR04kJj zcFW4=H*HHluz``Y*7+P_H|zR4O(W0*u(Fs4Vj z*tkwzC|?L%T9?xEm24}~ViBH=wBHMcXz|u}s=8;tNE+d-93RQ1g8~VOjc4C*Ga5+; ztXi`BUh`c!kO=4 z9@BTnSVr0d=IiUNX_1wZ@=S#4MtauOZESPS2W!FPU3= zYH5EkE$y0;^leJ-4LPgVoR;kP{gCAvW9aGG+k2}$9^Vcd+1zUUUXl)KD;5cJ{&ww^ z`w26f9KjcO-EuTB^ZHND`Aa@IcQyHVI*XZzZQmx|W*mYSO^O7$hxJ?Y*7PsQs~H1# zejOe@P3EbKEq^l|#0X_bv_^?TFEaC%j$Q_}*xOK1I;U%b=zHb+eLUjK@*rOoeT@2R z9mu>Aur$x$CM(IMxRMu?U|{?8UZTSSmW=klZUMw2ZRz2=-XK!hBULky7Bf~50{4`! zm843~P1CyU;~Htf|pS9<6i8Qf`dm81RB%Mx<+eKF;w4wyd3*jQ8r=87nLqgrx6ZeYW83 zc&;0iI6kcqcn=XPiEP)NOl8ll_N!yU_&y>lMMy6|84G{TdN|ddJsJmcMP3PlHr6*z z`1pPIM&yPd(Ula{^a6%5P6d`&v(Mr|o9T~c{=kHS`Smd)V1AzDC}$jcgwxYZtsf>O zrjzXIR4wV$mI`j%o(n;bLG?Zy&U5#=eBhl|Oo6#=$R!#B~_a?#t&$C1{Wv%el9 z81u5jmuRVcAEeaFf0I*t3}3OhMerPBTd>CFU63-fCYeqQ%+$|bmWJTF4x2ecE1Jp} zaDJl#3fOav3XXK7*1$ljO@H|RI<2LBL&lvI9~sDZ`{kOdNN%l%_GbgWB&=T#Wqj{$ zETu>zuR|$MQ`TuJ4Db{Q1rSb5{7dj}A9+rC0hFMMg#@AT)5(Xiln#kwnMz{8 zM7p`ZBcKqcHJbxvsL~f`>)u{zuaTt7Up?+(^tSG^a;4II`EJ6yNy*x}kS5*G@o~`P zW$8}fwQ#oTGa{k{F!;Hs{d3%Xac1I5A7Xa1ZF<H~K zSVA5&5Qtef@g4Ct0%j0nmCLgw6Z7GumrIq3g!$uv&ebHRHw^;kt1f%M`ym4}H}7Pd zv+}vG;B`Op6YpL4;C??x@+Z$Yry(EIUh*)8pY4S{vvk$u`+?I!*-bD3D!6l~HN#id zs3~=|jM@k5?phcl6Q{B9fMkiA3YrVXW0CzkKH_G5!^Hxduw$es5O<6-C4$H{sl}1} zD9Rnk8O7dhnPB+V2f-@iv?jjW&IRxiv9PsSim$>y|B)Q#rl0Ncn^47Yo?{zx-PGt_ zdZOyf4$i9qeUQ-yeRc>MqKnI5YC2i^jPp9s>7KMZ)Lm%imJ*klUa2S^blM|75}|pz z+Hl14leb8fxpFY{_JDiIRN5Kgf;&yHh4kvVhJMIrW=}%3kl;p5VV%6G^OH3{ELKCa zGcTqMQ#^7Y(000L@X6Yd7^lxQEQm zTLU8iTGziHcO1<)7gWYi#BN?p4*mM=94*=}ldAJtb6y{SY82Vt6iCoNZ{^z1V{&t= zZwc01BJ^=uvrg4PEGqQWvt&HfOYZDV-ZaLB6(U*1NPxT6+4Zfq$sK;`QtZWdRq z^$6u{Dr8Y?(oq`KKj7l}C(n-`u2|B|maZ&}mwK*dFMn+N2!f1c|6yL0Dh^Ee%KGBC z|1W`)kWV#u1Q?keMw)l-Uo;p0v8E$%-I6|?i-2noNEP{UC4v9uHyZ#9v;$3nBR&nm z5`=19paGV}aYF!VWt1|ZWIWKimVJXm0iP<&|jaKsn6W2@{Wkx1iIVPgPY z09O^@^>Q_<+EH|XJ`?D>DH(%s`KKP04!yri{;XhBm~3un*;ED_3cLP%2#Sorq5L=!!q=w|sjaVJV+e(@7c zK-+DEk&>+{h3o`P7K!cwze|fb-R~RhCk)H{?Vmztf2;7p=wS~zjN3kQ`D=>P^U#IvDmQwR&nDJReI0dfZ2#?*oaW-5Xh-Tk+gyU`pi9tp+ zou>oFI4uk|tTH)`ZyNbR3z}`Za!QIe-M2w&)6dFoDr0+ce7TV0Gji?fYYlOE(oKy5Z!?hIV}H!hru=8Dy=Kslo{*;rZ3F2Rf$j zuXQjpq}nGQPHo7ecr6mo2%#muV%U!gd${d$?mALu15;O5ko)WG3pHj|P`CF!=*)sK zDzOoaf8V**guu}LT7UI6ys7G!mgw*HuQWwDJW07fU@!r@8)v|g770Uy=hPMnj^7ZJ z;Dl~g3n}qKgE^tRgJi!F^%q#15n77^@`nfGe(E`Eb@r<_E^b5#gHP>{{-a3itjVnO z6wWI24i{uJr*a3cb$F;Q7boD7dw(Xff(DBp2-|JPuYi2H%hpet_Zcf~HdTumo@gN1 znQQifE5YAg|B`91-GQ<>y+f$26W79Ireqto4YSrRw4Gul(-oMD9Nkp-003QWEXs5s zwLsZ~h+xR>>`D-f1kF?%uhq^7uC)>%Vg3=-B&+YVC#++`hQ0t;w=GaSGN6Lj%#Cn; z&ZfMBt*0IfylqL3EUSh{-}Qcbt+agB_<6bJWmZagBJCLx|KA>P(l4$UsBX zwc$Rm0zNZ4v@KxV#VftnV2R`C(p!STtw!{r&VpLy`2Dn^kAR{5~~c=gN&}XkD)U$pR-$a{R_hs_SAJIPr!4( z;2Rdo`{)GV380WyNDEylJ5?a@(}` zXv>e!_JQGvp#`Yz`YD<_&v#9m9J70!*Y6*HsJX!#UGJ23=anE8&ZX{R_NuvH#;A9& zv|IKn!GnXproKk<&naw|1Crj;%Ks*}zIEg1P?s1r`IXGYH~KkqFY*TS$FDG?HC@>r zBxSrRx7_FdhqCWnM80H4q^|pprxdM5BxNz>#!DZC*`9iz2GSI5*s!tL5elDEfN{`m z&>j|G*Mmj@UC_&iSgiyO7{dDrKU@ZzBh@(P3NN|P3MK1|=7-|tm@l!h>- zpA20oc9~|GF*D)rN%Mv`wp2DQX0((D9lEK>S0DdS^t)l4$IaIe&|RP+M^Q-#4=js{ zjKocZ)c<&Yw$j=pT|cfPa-w{{Yo?-Au;v@>;)ir<^lVl9QE@rN$BOHMOzo#%yqG#) z6&U{g{l#>C3!MIx0!UqdcZk7PG45XrPmr^g8onz;XCKFoc&;{nLopsCfTP99T^mAxvq+~Fn3-t?r+s^-5y2$BD{zhq{ZOQd3k4nORe#&?yL7QHp zGRMJ~D|wm*&A8}fE&v~?H3((6v2|v>)0ZvTp{V(pU!;6XWX^+gTqpN20 zr%BA}eXZ3q@r^aFw;r%g!SeXu)Yh#w++oG|Z5g?N*j)8jGNh}=jtPKWff{K_ha?-G zU{;0-m3>OIP&ci6(VkvRnsDwoyp0C}kK<6pnPk$NMa8x5GCn_GBjmwMcGRT;TFiKV zHtcmLUOEb3BXBRL!ig5gB%?RJ1(kL&UpMEAs#rBXx}oO@M~Lx}k(CKTlu!iVz*Ms1 zbBd6kW-U(J37J|+l1vz^e6Ra5pu%lLSu9Gj_|xr-slLK87b>(>)Ykyb{rfbUK3BLr ztp*w{3+u2neHMP#SkqK+c=s-4Q}e}9r612W=q7Mllcf^{?woTIF4X2t=^Pjn-57M) z#wQx6v86L&j`&##5I+eLx6iPVuYDAR>wGl0MCJ*CD)$lP53RVwk96dJ=Yt|%vMDP{ zJB+X+S{Qs`hoI}l09rw$MDg@zr^&oIR~o5C@RX4X(Z7vZ5|0CcU*f9LbD9mpP@0@9I}75yoLJ?|fyNnak&OePqc zm&rXj#k4R@1$rl?R#_Q(Uk~HA+2StDSkeK_iCuRZU- z{Iq`g&VdAvHi>& zW~U~%D;?uPYID>SNL%Yo-9H46OSUot7Utx!q`1@uDh_}nQ99=!$no^~tS2sdrG4yp zr5BJ*$Sr8>vVq_7c@{0qF=ouWJ2A#`hk5%u5&-YOALTwMjTqAlC9kr=dvfq@GO5aH zFb`&F#3ee39}e@N-7N+WW0qLykX`z zg&+*wAZ*2ZuKbE*`U$9fqN!ES59X-o3aa$79_yCDNzT7-P7u1wPqe%{*fGy40b&bo zL8scrkE+5(4=z6@Y3=BcrnFP@z1JvC-avw(M5ctyfX?XeSLGQ`s1ac*u~x#y9xjNH zi)IbT5ziuLL#DJDXC9nQv@8W{|C(L2?R`xO$-c+OipltOPTS-26UWgxKUN+QR(kk| zB*N@T<#(jup2U*(4TItNE;VTOErQs^I=n)_vA^#SELD0FR8L0YI6*-QSYI~#458XIr#q^Ry=5(VF1-xBfIhhfd{fvb)4dr*w!y9P zqSl8Lj4AEyu?tl3t`>KTtTeLxg=_2rN^>%Pf4g zNlfkAC2Df)a);+e!!2!Djsss~4v|8Umb4HMdcY>p00n1;*O57~c!kLD-E|qQ@!JZ= za6rRW_!TzXiySikordm{H*S?RYn{e@Q39S&@|3S$LWQn$xcxJF`_W?)#+#ahN`pRS zM;2cP6y*36eSBxBL@1qt3Mdh#jbWzw&K&XsFu(7n$C1sguW?})ic%B%JV)-F8c6UN zg%u15PpuFOL1vMEtI5Ax7Be#KowD}TMus!y$s4IVzryAPHt?^d?>8Kn5a;p}5~Tln z2n#@2IAC+PorXqa5S`gM#7C^;=L%Q9m061o=%EnDB<2QWV~ka>QLNHtinvpT<8DOF z4=%uG3S2(0m+&|~9wxD`(4Vpw*I6q!+VhMKV=DABU(cVRhp#bOjt&%v!4DoFzsGAt8A$_$M~Z{7e_q-S3}3hxI5W--K9xu;`n z2fVSUz-&Cv9cnBQ!Esc4+_1}-13;GaH$u(aQqVzwGY8tT= zmTi}duzj`;M67E&ZW2kZpL1F?kUsfxbHy}&4%a?V6GG`bi{x%bFXcZo>w;r%Rv(BP ziQf<2$w-6#PP>g%`t6e;Nc4QbHd>jo5G7rnNgUX?!Y#IPtUv3@Z`XdtR8T3qP6_A9 zneJq+{P7$AU8UB`)3&9SR#!=%iGzWb$GIz-Ho{mp|0mnC^6_h{kAr*K-k7_n&h)=< z>bhIivsU(#yIhXJ2xnw`g*|I9o`xKaoF*MH6}>yEenY)xw8b|;aj_avhDclOcI@AL zTeodiDO17O5*peQ&=Zk6(t}jXSDTRRcmlHx^ytdZ@$)GxEL2)yj_fb)t(3csoeh>a z>lVJrc(8F4F(G*$6UV|ns!^Q#`yphV0C<01v=*ktSPxaeNs53S#v^&5mrYaUH2n~! zQ9soDvF@FvisB1#94A^h_%Np-o39EY07$aSLc!3c9!&{|-%6H?KUGkwwZ`08*nQ|M z1z$n_iVJTVC)}iDJ@}qXRo24fF$F>G>0vHM(C*$w40lNKhN+$(tXav09>}akYLwSq zS$iXR$CwEQ3rUyEcqXk=$WD4kLFvk+d!f-whY$Ep1tyFdD>$hJ6@nJQ%{b2ljO{N8 zBB#S7KZzQGcCU-hnTcE?N=ugqL2tcf5w?ib2ta(KCZ+X%m*(_7wgH<+U)sr5)+~=Q zcJ1%1w}4s&%Bni8UFr0TL%kCUq)OLCIo42N2A|o~#olK5TLEs8q@BANKKBJiP4-l4 zUFRr9su2*vTVh729EC7PJCPL)jJ#9(7gZ1kJ88zI*DlZqwDV>|-$miBgejIif*}kcX z2^;#3+@p8OawruIHJVa%@xFfjT}Q_$9-MNXn5}VwRK+J5#O)&zyV=vxhDUYyMLm>4 zXM3e}Vg=zNN(4`=fd%RadGG1ywT7UL-A;qTrqR)b7|~{Jt%4t~l{B@yJ`^ziOS$kD zvLbU!58bg=dnFlLP~G4+_NwmV`Hvw=ezg_pZPt$$zpJ0RD(|aNY$&hV<1V~)$OvV@ zN+uU`lC~bC9aETA6d&S*i@>FmGuOxNBaEC~of=YvZl8*6Lhax>S<$;E6d%pMsz`Nh zKds%!gWJfFPqiP9bl=5YvAckQd=Njm*!10^p}F&GpH-F9UgOz@pxE0of4&-!a^DP4 z3Rm~2;^-8-!;^RIsfy!ZWu(<6r zg#|{LQ;Z?%GTa7aoaBn6Nc-uZdrRl;{P0&LR2z65oE^cAdGJij;T5fYsWJvElii6g zobx`%NTDQ0%I~IpI%jxuA-vrn#H1<7q=`&J+$;>>fAKMAPmp_MK8lRZaXWXI>24t% zQl@i%h7HpaHl&HMHHL-Cy7b&{YJ(d2%Oz&UUZ;~w_rh=)?g{h3);cV&_ABb386{w3 zGyaXalc_(wKuvXs9wV5nbm>hUu^>AR8mXxPN|}GE)4(KR?n?&MsBL2IsDo3VRozPh zr%G%8LMf9EyB}Kmgz{keAxy0Dt?lz{$e!|oIXmyJD?*}o))5Bd>YvWhU9c*VJfwyD z)!5fDy{(RMCrpn(bYhPqq*HHALYWZTb(`S`(;&=EHgao>dualECL%CE$_vxeX&LU0 z&DiP9ID(Y-gXV=2@_%ez8mqEicRWji5252c@|d3K}NcjlVDVv)nsA1l1j z&AYz_ERR&Tgd^4)^Lq`h(Z=((9Y{DRdl28@-VfSTzK@u!M7?WDM=Vv271zCur%@3M zPWtdFsb+AB3jLIH`WV4;ib_bn(dt58dEypbRAE!TDb~!(R*JUEU~>-0Pmu2KS8E(2(agZT?jF;pE!TbRh+~ zrSIME{#ghBq?XjxMU6hiG9TjEUH?owbF}5rAgeQU!Xi`k*PI;0qLp>~R@T7GC%(OU z2inForLj?x&IpDSJ3)6g`7kDoqkYJu@Dm(+0Gm(MkMbg=p= zJYrS8p>NwyiQ_a+m3O1}`x4SzRvafWMSg7LPIi)19}q+w^|XKeKK5<=wlGifi$|`y zVeO1aMj^)2LtB~448QO1S~tW&V7!u4uv5l%Il6Y-FWvrS$CA1tsj;a(Yz5D^FWN-h zU5={LVUf@Op-aENMvd{)pG=zAwu7v#{28z!aDb$UIm^aa|S$^j4vfeM!!PYXcb;`pCW} z&LN^RHI$}db@A5|N$G~8Ue#eWms$S<7-eN+#&EpxSt5M242-kuo#Zw@oxJX;K?7f# zN3za^OhO9o`5$tf*k*miJJTxPrr8@SytNaq{w{KNn5c;HOT1KY=}*#4k3SOx-uy0^_T=)}? zt`H$A1n^Nr0te(Ee@qM=(Nt4$2WRXp;^Z(k&``nQ!wxTbyOe(?JCdj8jW0|IripUl zt`NrC`|aRozdfdj5S;BufcTE8V&}KL0blhSlcp=JSQ-Vv*FRu_b#+YRzqumXr?`oX z<-N;i#qTM+=n(5WJyy(4>MDtf1Pit}lkZnd8VDkKns|D~TJ-+lP zUTjzUDcf#r0X#Chf{_#_7$ps&9$2wJA7q9@vt#R^1%mM8$O@{>E~D@qv>yubS4btfl+HPyc1ob}lM*I5AB_!RD=wX9eagk4a8vjRrCUyM;c?$bQ?Y5atq$I7EIh9$T)gu)oyZ9=L zKC5@D@5>YNyB7;xfBO451<@uE&+&QBu8%O> zZR10ZY#Ha=Xc{WHq(p_EYpy)9b%hn1&@o+af)ON453Y73$PuINy0<`MWp}gAW11eF zv!?GK&o{&b>Bj=C#^?t~+;`p1f zt2fjx(2%)=A6ZSX@)>6Ir5P_}^*NBt?d-XxmhSZAgXjH+q@25v9KIX&Yk1#F=4yGH zpd$;%hnhew{08x1TR_5s00i%`|LW+m0W=kQ!Z(ibEZaH~{T;a$YQTnVR4BWYCAHb8 zg^|!3Kc3Ss4>wFLW6IxjDVtPa^Ne0q6eg#ZBnT4Oz%3vi5w8C9y=4EbvLenR2B=rx zu;tnJfF!c&OBRE$1Wy!v?3Oh$)D{j~@W^IGnm1l_2pJrIQ6CT(H1d!pCoR3x7%-xefDIEg%oq7F4k4arC69-2py}*;gmt2_n?V^K`AaJjYVtODAY0;G zBmA2wu0x&Z;WV_7~O@nikx*(G`+f_0Skfg~^rAVoH{ z{3svpn4YwW!;C~U4QBM0)qQl#vc3)del7MarXheEyW+U4E>6FHz4tiAv?uu^5u=d2 zFOI9_&gw%WFom?wZ+tSXGI+^!-!OfP96mZST8j1-7Lne-4Fs ztGskz_B%dzMlSv1P1OA7!QUM1YyexjFzKMGRyI5{x$MVv^XnW{PUniP)O~Vm*wLC^ z_y-O*w@(Q$eZIO9k{H<(DQs2nbb>tfMF(1Pu}Qo`q(Yw>yK2*Y z9*N>BAU&z);|Ze%+gk#)@^>cMWYH|bd4~=0(dCi{>||JX z>(X2M^e%auaAkSV#F6O=4t1$Z{|$Rjt~8pT$=q~;b86(Ld^|3`6F1;E#-;xPFIUD$ zBNCVq7QD2LcE)CRuCHU?qm@FIfbtc&iC%Jdm$=h6e>$uQBbG`+OlVFk5;@cJ9CtC6 z7sBdyl{0m5=hEP|p}0HLAiTf8eDXCZwKS(>UH&r>#S;93GDgCcB)-KvH}mxM9m{tO zn^mO+C%1q9&cBR4u3x4?dtR1CkJO#YNn+yQ7*?8Ote;`tW~cR45qEdf|9_fzX;x1_=roeS4u0c7kNGrviAX_tjGsz@IP!k|vgPO->K1;i3?k$L+ zKOqj7-xR$3T}BQc45_(zQo}9y4ql)|eR78R`g+{XTzZ4rEx|h({xn35iK}N%w8$JU zaE9kmLi96nKhn;6v(x1!be=dr$6T7?#^t!dE4|^=F7!fv;?;1j#$zArU0dIB$Hj_7 z$0mV>qVv@8&oTL=TNl=whIY!eA|#QcP39SV%Hh!dAizEUhcM={Z%eZE^q4-){^}PA z0g;N&550Zhs)7}6bh)LcQ47bd=}?-0PY;91tjP$2U{U;6s(V1DIXWK<;IBP;@I|ad6lRj5DS70hE zpBlpk7n#p9`glFUQE^u~{+UVTll%rT>iFXxnTe!rmkX3?K~RS|zgWn@W##L;Euw!B zW#ZHppzLw_>ope5no|ckpN1YLk`h)<(-EjGpBFK8b~7QlL+pO{XyiDGkijkLk$s=v zOF-EblF@SNI&`vFZc}M1p}YjnUS+8CFQp`!C{_V1-2jO6`|#%Qh)4Cz zmI4qt5o1H00?F(Tccy=4AUa3`K%DzSy#PL(qw$y{MnsyMkoy7Vwzo6DfLFL)eSr(A zhQ)vC9te`hRT=pf>Ft#xbl%4=g>){{(;X6h&Xvg~i=~gFZ9970Ltr zxc4W+=SAtaD=g09aMkcp`z@ASj!jgxV8x+N8=63h$X##7EVybd+sI)GIPqi6@1G@8 z9b6?+{QmXH;LY-q~PyOvU| zb=A`n z#UyOI6ByQ8Fl25WEM44vl{pXTau1JxQ4y(WY5zKOyk`!r005{R;b_f(WssILZ@6)k zG*Jz6^YQUHfZ|k@Qw8W^B~rihFm9k+epS3xqi4c)KZ{FOKgvh>)5SP-_aE`=LI6o( z_?c}% zBXKNltw-igr(s{rmmJ{4>zk?AkR}!jw6XuM9ln5Wm#(-BbGuaX(l32Dc&XZ|!b>DU z`>L9*tA8ADpZKD?RcfqDClc$+lVbGt3<{)IodQU^HcTxO*&uCfI{m@SSrg(|`sWpQ z*~RFDT2g-t_5Zz$0EE{LIjB>+?zz`*drqw{EzVH%hOwc)jx~mbN-VE){3`$zGg@V> z+4)g-I&^YYNn=E2Xz(q;!3y8C`1}<#()lGiMQxZM@^m3!SJ!XLhSA1~Z#OYpQ^^V8$Lv0ybUNZRXl0dbN@m7ut z+<|#85bPknF@ecs4b8n-6qjVkw-Vjt`;fv4fT3vO*ZJsc1)E<>e#Um1Qeq<7^mx_o z57vKCHH{+p=v(K7yGc$G9$6eM8;iNhcRy-8bw4*QxVh1kIj~cC;TcOq244BidNO9U z!RrS9gXu}xV6)7$z94k<5A9`jPI_=Pw^-YH_LC-Jw2=R-9u6+hrz(4Bk`xtc@gk4WN> z^i@SX4yp#GGEo?Z%0$QfOLbG!aJy1pqmCMRVoaUvp?^D9`hg;v=wqBAh-R>IZV!gD zh9753;8>@*adCseH5T8?GTmbxt)`pYv6k(cx%1sATtRj!NUFX3MIeBNQ=hl&7Qk`Y zH>>b=oG3-A7xVDe(49)Oy8q|RyL}gN6#b8YkwS-fRdF&p1vtbEe-C%3DDqel0z67 zq3*-O_klGaU|V!F`KEIQ*T|_+4C*&$-x5@Lr*YXBAe1G72Nj9W_RD9%L2y44|GOs` zh-eLWawkg*PCix4f_ad>smts~#AN3&swpyY5!RBL=6^z4`v(Jr4qH}xdt4Mpq!SVZ%H+@J1@zH%9PKpD*i^|X+b2tU! zw!l&Ii=>>78lGkam^;YWDFnt28{hi%@;sBZaci4OSwOxraIFF{vfX!y2NeVu@`T4} z(Y4jKG^x~moMAPSPjckGsiEbM1@Qaj0r)5K0W~w{zvz-wx;UrxTv(x}vUgfLHdUWy zLGbp?q3XrZ&zT#(FmtDzJ`>V1Y8Wh8gfH0c$nzWgA!1`u(K2^>0(9=cT{-bR@hC zjUcKu)ul(bBNbgs-B~jZl>^v9W}Sugz3pgfZ5XK-XEpudkJy*6WVb)pa!0pZZMV-2 zuB>Wzyl{uCs@Y>NHl3i5P*qHyFh9;oL$NcLueC`~Uge#-67vJ`_L+H8qG|f;0nC`u z)3^`H33+Szm{iihFO5YxmmQ;b7>}-~qmnl5{NWTs* zIsN>~wz<6wTGj!*zfWNww-gXBHRx0g@stPWAHn_H;9L#JQv_;OxMj93rbfwltm09s zB2>*hXxzo#UDP@)*mlgBpcf(@-U-koWp9m$#RthmLDkDvBK1Ab2Grz_At54e+)I1b&1jkkr?x?6i6kGE6>R+{Ke zZ5O?;shLbk=30J4yMPlPa(nmbzQVR9E4MHy!as$q&f_Y#hbmK2X?nkcJ-^*cM>*Ns z)4$&E(1inI0fhaFKObi}&3BNS%c+-V%#xyV@0lN}UwgP7b8c3#Q6=fKz6N1uwYK!J zw=l+Mx%&YpM?yrNKBh+KSb5`*M7Gmew~Ia#t7d2KIlNzdZPMsY=b7as8mRmu%5%s8 zxP+3(@3ke(gk}BGVifutrMTOhjGk!!F9x*LW~|vxPlnarL$e5czu(4<9CWj5M~_%b zRbs)k(=|votCf!sJ=Do9O23uU)RSd170PDNgj9y8{&HmMTb3TYJPkF8(qX^z^|0r- z)|N|Cd{e5)-Ws-!*Vg~d(J;JtG#s3_n*rbHB-jUdeNg2b&j}N0RQa%n+@_15Pkc;@ zcu%c|;?N__^-3v^wt=IO_EFLJ_SD-k9pG%962N}Ln4hdoFC(+bUuBof4k#S zI(3j(0LZA?8Bme_Mcm6jb;5blT?H_0Y@>JejxY1Hp+UB9% zd1?dJT&tc?3hY3Wc+%`eljIP)r3xWJPIjm9SQ`^HN(jBaNbdr#fo;J-jz9`Tflk4H zK2aL)J%wARqQl;DwN~PHGPs4G@#=g7UxTTDL#K$Ea2r+F_`sR(>4dIs+-h!4lltqb{P3xg(sMI-9pxBj@pp7s z*cqw0Twc{8-h~5})OYOFNo6xd>CD}(OT$GjZTHo8OSFUqnM9OErdg-%J#;{uB=Y%u zP)&Q+J0#~NH#*5=;`|e(-g2M4-;~mH2_O1oBe`AFCe!dKnhHK|=S_+jI{MZ_g5%gS z^ps0HeYue{uf@HyG3yP#b9jz9^7F@qzPhWwCXx*YGT0hvw%gk&4i`n3eSX+^dtSTn zhz%|8w&aw2M{c<*HNN`G&544&RfI{>UNI?ds3|vnY2y`|mRR$x8*CpHWs>7APlf$` z-R>`-`|>&gX4jCh(u6w`Czn%Bo^ma@2c$jAsX4k%jwz0r71}Qaj|`t<#KxP3vUEJ@ zdZD;keg`Wo839H-hM!^ZI){ib+*c|(!lNOEEQD0grCx;>{y*!p@KyuWC?>8(PP4o+ z<jm@%b-lhNAO zSH+_3zgFCiW7yG-E7dU0(55(*%qk@7;Aq;fND5q7SoP*QC@ug8z9Qw?#7n<_$mq-G zIRw)NS2|+lgMT$6+Z~q&#=@fiu#M$0@stheBe#7VDABH0e`2mpSAM_(u4PjdznqfsKe$YBs zA+0Pkjabc)d@j+ESsrNDZfW1g)I3LyA8Qf#v)KsTrxgV|MjDywRjz@GZL32W3iNRj zgbhO0=?#s;k<`j-YprxNviBYA8gXXS+t&Va4q++J46%G76OPFx?Kf?L1iLnFIIJS% zAJ&B@T+3Sf)Shym4tsgEi^bICDDyP3SqHyF2i$-Bw>i-zwxR1@!`5w=?A;F_vGRVr|gRHpSd&|(!ZSjNCP+9 zy~>4;!XAsd_S>HIsmq1A6=@Fwr)YF)N$))};_3Oj89~NiMtZera%x8j=slh&3JQk9 z*-O_a@v)fPiKR=lG=1)}FrgAfrc9H0lG-(c@3dT^(Tt^ioJtk0?MW2Z_Vt!3vK{ zXMYfr$zv*PVrX(ohzYuONU>rf^t26J4!t~fmkg52<3e#H&;K@Uv(8;_!c<54!08`v zcg*Ov>!$M60VWEu@X-f#mU!c8$qzKa$0chrMW{|;U-M`*}(=+Va34gpA&sAIgP4c3n>mOBQy+ySVbD^BRJ0XKct2n3Qu9blOZFLJ-9t% zpTXp{lpl4Qq_ckDLLGV?{(j9LB#xFBN3)l@2LIIg84}tExPyAfLMxkY?)TcLO?TiF z$nsdmlY;gwe^ILs@w&V3|7J<5@9t?30fQj_I0lo6V3B8_8R6-yugxIs#43am z>;f-c-#0@H9Zi_a4l#%6^{S|t=5v*7wp(W!b{MWlu%BV~T#@Lv$Bn;hm3=SThxjtU zjZPvsRfBcre9o-8ZX zCsi9;BCq%9BW>RC9-q^uybS3UA9eukFMMWi!hTab=H8@dT_z>;`^gL5&)V%GutAYn zW|X_*o!*tpnaX1_0f-GGsC60%+#!D@(+(B4!}VjUBp_!%3a25g$XYn}>NUywK?A}0 zTsXl1HlIWnj$A99hWB?WPgf`{YK?ZLoX2y$?NMK9QW@e^`+;8O!|!DPaR>BC7*UOO zN?(gHU@3DHT0$4N9Zut3USQTtmyRuZ-1g1Jms-dzOWxGkf2sZ0e%0&G1zHk39oEn< z&*vAN&lrU4s)-C!su?n?tkvmzBK|nD*x($WsT>m z`whnmndFgIN@K{}wX{Jw>c3gwX-Jzop5~uX$BZ|@U4j)qecj-C^n?{=E*!b7DJ^Z%oW5j z_vv}}_bVVT52Z5cXnJ2M-R_z@jUeHF0zbP@5WlN#R$Y6mQSejBe*HI7%fXR@>n%Ug zbie5czm#_$wUEq5@-QnyTx2&HA4U|d?hO4f8e^BU#d{o?`jEQ)@UB+tYcgsOFMiF; zv-D+V+UkxevVVKBXZyuOmA1N*qnbucTjGl zgN$|@XwrBNUK9YxMBL3p*96wcBlXx~;oRv5eCKkSg~-;fStzvdRmBRJ%OtLMY}^0X zI-&h^jeUA-6U^+nHx#Z!PJ7YAC=@&jXNDfxYVorO_HUwYoWe@VjQQRhDzxkN;C7bj zv?a~uwF}AzH>bow;k{Jg(f#0RNzNP*3I0sg$+9h#oqOjZqMxUXJ+ZjlICmiE^=V*R zzg0#RTFP_IkdC^N!y46LUZPyOjQq*+N!T^ZrJwwYptubpw^en_>oWG%gjWw>90v4$ zBE_y+RXqKxq4Wg-go&>2?&&?GC-_FJ)(0<<5MIclO(n{trU2v6G)P=eEcaUXctiS4 z!*YP(?(_J?LOi>Yq0+WDC_Svkr6HGY1AAl_j2MxBdMdH+i{sYE0qA{O(3vJY&c4#6 zT~Vwi%Gt6Xf0@oF*{xD=iaDlf5gLvS#Fs+ z*;IZmc8#3kPyTAJW%|rZC+uW~-?so#qcH9F^Ly&NY#tE`DdlqCS9-V1<WQ89^^-{8MgW?{i%muW0CUOFpu*(PyP0{znJrm#@Bwx!Ha#vwF)j#Nv-N&u z2<9Fy5<7LSby6CP!kYs@1N9`yPa^-^xg7v$rJ^2#oW;%BV52IFwV#=nq6}gZc!4m> zx4P(7=wh!m;f!5TAzSjG9XNX647Q4}hCTh_Q0T=_Sm$nNxH$|_CQ>b3xe#Ca$hb46 z?4|fGGO#IL5}lq-!Yf)+DFf>%f7(9X(7L?CSeo$}Hkd4m_{_-Nh*b4Bikv+&gI$*5 zb^0CGzfVtJ+;p7+EkwUNHZXiR?2QovJ-Ua^6V}Xjx!wLfWWXX8^Oxbo$8?Teu5`>*0@M9wv5zs%&~*m8T~zIqpG zP=rRBTg^!Smh75)#=hElYeEcK`WtSS+UyK&+T_lZYu{%?z0kHXyIf#pBkDP=IcE{& zHl3Mt+U(ld1veTa&JTV8b`KjSxJ#Jj%q$isuGwUVGUzCouY9l*?&oSWr}kFt_2FtN zdQmAy?YI5zSdRtmIwLI6VH+tYr}&3KmEO3lT_yzp}sGB*W@?TFLZn3o|YtYr3+?c zZ0?3N#fi+`Zy4Wz;>c?1RH6oR!zNP9e?L(10=&&>JI!gh~GgmNsYL>!%*co$fYu(!XTb(N;E1qw9?O6|;K^{DQ?S^@*FsJnxJ&-5-<9-QXEK@cfHw=D#t!%hVLI3OMS27ua z{~{^0ptiv{-#z!J%2sPbp|`#NDRCLR|9m$c`VbU*p~q(}yeZx2q>969im7cYC2&>e zC4aNz4hGuEU}{tn4gDhnyS~v0&MkZ~g*rgLfZsAge7KWHyFWncYjG@);LHk?*C%2& zehI^HuhwqZ)a^YDWxR0)i1__IQ*TI7GxK#tFEiCt=?IA*PFE%@HjAG@yM{U1eLaI~ zg%}3IOh&3I!o(e9C=mo*8EIRkh?4kMxLwKx{4&tkCAO1Pi%z~PLJu7Si#}_}2SUki@OXZm$QSwD zHXoQVlH##)Zc+|$JPz*+BXO!0GKE^!IzdGx34--Z)PqjVva}HACO-?|i2q?H{W(JT z9U`ezU+bS1$0H(|P5%2OgUMrL7gjT(Xa(J{tJj2-$(=3uO@L@axSHEt=apUgD=FAOC=wOD)558V(JfirgbN^4r zl=n>cDcob=pvKj@y^CHxgJ+G7sNtd)<&~g4__~_|2H?huYNXYe?=xVpbK<}`c#e^< z;s(?;1_iRZIrG2@mH+>e;cJ>&cYR(`&2%Tei3c(h_bFjaRcU&tGm^-hsiJY zMFhBT3ez3Z;tpNB(2+M&u0*Wr7e5H*ZP-O;aB)Tv>OdxiOItR42q99JYhaaZ9k=8+ zMt}tFe;S%<5@^kpN)_^}ZYjlDx+h!5R={_d4-DCCNDZn&W?R6oBh1bQqZ>L<@scM` zQ=a>#^>-QlxMD~fqDf}fj+@d}gvgWsGz3@(N zgg+-N{t&LHSzUHf$Ftb{1sC#ax2o_jtue#No{viMlCi2UrJ;}{p{t%9gkw&g{wj1| zj0g}mq{&tqG2fJOzg=uxO5-QEZ;q6(xget{Wn+n>QqftD2#YT6FE_eMiV7l4MefTp#dyo%Z zz<-3fg`e9B02~j>YoK zuv6w=elpP^g2+#1BOzcS)X1)Wu4VbdbC7yA7mR;o1|N8jrlWqG#?=7lg24PRbb*G+ z!H@oZUKk?dmi2;FqJt+lAwycKt#gBb?=YzyZ3s+RDqFkr4v*b7$N zIxBo!7OJ=@;bB@#SCGQF!UE62Ywrk9!?P}&**72={A|jKsD)DpbvnvkxFkzeYQ}pK zP4Qo1|2hwx|Cb=SgfPxg;ozS()Y#`qWMZeJL9B->2IMvXA+#9ZHR=K+NnE$lGsm~r zJilGQ;%k$bd2wWH!FIZsw#GSasAD6682na&6nh{Cukt1tHax9cEI5`0e*3LKl@bmp zE*AtCdpyw0{8+nd#Fmw`>oPjLY%6^zCH@N8;E7khvUcyse96H=VQwZw+C;en|np;;|^5DyZx1u3NICs~lGybkLA z(G5UMmjX9=aRG)54I8}{N(pm2hb{;=s!Ktb*fblZGrC`2^;7n6o~oaSvkU z$gX}F_#<4aF(PoB&52W6pv5r(l7J|N+VdtQb9*9yO6-6fg#`;3sd(>WCuM?@ow;ng zD}`^vbjzaPY*LmwA*(aD%=vug^Z+q-tHsF&K3kUg-YBC#`{TWN@4J8&(=Q&oX7|V_ z&1--|o*9`@vVxbkU)t@ScDa#J#2!?W%^uIW9a&T2rM#tDC2r<+7& zmVHvcRNg7lif1%o0JH=U(uj(bbbBxe;SQQUu6mo5p@J1uV)&Lxz~1WR3`zdpBWo4s zVTX2emW}uT8;lkzC#|566_Ykk^yuy<{iA%AA9coU;n$V>oel2SFfp^I@ccwRuG=dH zysYKnHOyzD!lw~#2YSko@SVInGI`kmHFp(ObgeG!v46q)=E1!Ma*in?{4E8rnEL~KEI65FFiGyYF6i7e^Lad>cm@Dc=n#{{#DEg*^G#*NBJS4OANXC6EY|~`b9wh-U{VI)r1p5jQNH-? zP1_&_wN!50vq&u`)m#$P&@jEKwBd01x$sjWH*==(-ginrhF_BT@3|(fVGuCEtr|RZ zFaUojk$CC1mTnlg49q_|4I(J43`17=>W5UJ+HV8oT`Bs=!Kj*t1vWh(x9B_gN|Br0 z`P{FD9~8DR9$~A};hp|k5v!fWk+eaPIoLTw#xJc`U%#-S;z%J&Qz~(h{G#Hk@lOn; z@IP;0&a!^FO3dJ~!wH$LDe6=uJ+X zpkM`MgE-dX+K7+RgqFl+=$DEHCV1&{kG81HGZ$)v5~GcNrrbcu-Se+axHi$Bmv^iD zUfxE^w_iYK;eW3h5mavuIEYaIf=4KsB)-)1AqU=F=aGnPjZ0Pl2pgcH12vj-&1E6O z5xOP}fBZ%qt=_I=SL-A`_PhyIY*hTVvk?gobRYTvC|op zJUd=c1UsJ987Aa6_|imFLN~TJnF8l(&Tr<%h}jzo)u~y12l^scM-`b zAVx>a3HE4_+XSz7?Fw@xfTtnYG z{OfnG4Mu-YQl|Wiy8f5pA&)+3Vp9XtM!BqDJaW+TLTdN^xz0Imq-3H6Dcq*Cg5?7` zanGrd{!TXtlXSZxThACuB8$s{y*fqI5~X@yGAP3af1<%OC08-l(v`51^XYAz<%Ae1 zw)`X_y=yTw0#(@a+$bCwOZEo~k1J*3N^Dn%b zF`csDl>(w#t`g2ovajtBn7y7biK<``2?&EWjBpJ7t!v+)^2`VB!ISF=S6jYdVn7cf zkEP!}DPa9>uId!uoc~=!0kt8@g;r#u<_b8Z3TeMCnx49^rnjN_e;kc*z1C#p|Rhz`ibHBBl; z^83@}sO%~LS5*&qQf_CoaY2kAU0PkvdfnGTC5aEypSWSW=WIsLs; z8=^qC!BkT=6aL$!PhbSL4m84Ap*YsAmpsIqg0Zx3OgJYP9c-87XD>9JXI>uA#X<<= zHZ%k%6nAh<%TBp6mBiB4;?3t6wYbnFgV-D@JhKiamEI*`S%>}N#E>iAVJjUrYvb-~m}Jls+M_Nw#Fdb0pMYQ785of9ab`TZAxJ!8#9V0N?TRH6}| zgQ@gzQp?9H#+*oAWOGkU1A~c9?C-KKO^z~X3VNUVS|f%d=AUN&|8u@T0V&OO&@_Xm zw(?c-CdxZDyC=xkQ8%DyLy9rduB2#bDJdxNhm8`n#6a*@xm$Bw@)2rBO@#+NYdz?bZf(EM+s)@IpsNN8S*BH;^bN$l+>UN<7W z_BY4odt#*T<$A*{C6=0enev0iE`nho_!~v$(fbGCqmG}RC2{{lV@aUd2Y6(`bSogxd0$)5Jm_H1?@ny0)&wb)*K=PGZ(3)1Vt%U9 z3H?#w)`Z=J$@Kf4^pFMOucV< zI-gm}edKLEv)3Ymq3$s3Ta6K3WY^27QWvYD4AUPW$3{hfQKjGq$($K9g~H1NyL^*h z@#*ram9qSG_gnovJ--Ayyz}cJ zM@#0%TPWnt^`<_4D1Ogb^2`B#lN~bSZUp5EBuPD?`yRW3-T_F(ZFy<(L3hh#X+aeV zln*4Nk!XW?Tomdxhimfv=>fM??cyd1l=SLyEp)6PqlXM`ai03;+xLe_w@|~2+bf4u zpH&Nn<0pRCCf_c+qfj2qZQlKP)w4qq9o;psI!z1zLoDxCU&dh~iuwVSOfDCIT7=DN zqb?SfSDlGn1t8-=^d_Y!iG0uF>^Qo6X%LRhsZgiOm4>&qxMLGID+9Fyytb#+U|U2k zDy1qlncMGk-!Lm>#L)ZquRW+?UvUpj(<2euR%s>7e^~=6Ssv5FP2kre8FNcDyBnWu8h6~_;?zHi!AzyS7zCHCn_p)XsnL+@`$5^ZNmwqO^@$uFBUt`6>L z`W2no5+h+eOeR6evB69+{aR|RO-3R4mkHo)?MaKX0NUJDx=8>*82c=)9Z?jdqkj}D zfM>F1A#ft25^YssgE9;LDtQ`OM-N(u1S>FBhJi)SO>^f2F;?96jVdD|YSX|orYTzp zSzk^9eHeWDo3-yLaTP#0WICo6-6%{4*+1|yVD8$?Y1)~$)vfTQm@3In?R{J~2+R_F zETzo!Z#z})RjFpQkJhb~7Sn-kGHRUrApdmJXR{=r%MJcbw9$7#J2o8{II)EsG>?Xu zd|9`>DvrBmrQI3h^hvz#R>cM!04By-UE_*^o-Dbs8NqDyqKcqjaSw*_^SwfBsEic+ z8U6exijkNefIA8Ol6q>=NRtqn-;n7SAB_`ZJ~8dTU6{EOXNmv*K7cK)+d4CQf`55s zWzG7>NjxuD6sGb0p(JeU;lt@xwhD0c9jwNQY6OP}f7$P9|qpNGtpzAuDw@bzS_S}e=saEEt6BcKrfWh^B<)b59k!$wiuc^Ri< z@w%jTAhsx|EY+LF@T12@GzQdvok{j!_2j-d%!l1St_euhX#+NZ4`j+Ac!;TNR@Z;}!Fr zZ-2zcow`eUofcwj{?fCt!@47pS5g{pa^U29_0rauF8fng{F=Y56chMOTi|)2&!rL7 z-bJ4zi^p1c=tQ6{6fRY~|EQ5jZL{Z)%R1PI^(xgQRr93Dg%kX*k4xoT^IQn{(9ar2zv zup+5Bt@eb~Wft5hn*@|Egfpx-?N&lxDLWdhEkww`_N4sg-m?@XnYy%|6Vl0mVK6sF z`V|dzGFWgk+*=0YfI1N%Qnm&EL!VQ%^tWv9=R_BHznj^+lC`&?MgbS5V!C5;j*MVx zaNZ?KYW{s{{WZxq3%4M-FPAD zl_dy31~cfBsN95?;WZ#YPS7+?PE|;&bx8xw4rxe`ENRs$vAVJYt@FIdw{;3CupeQ2 zYWKosug4|YasCPojw>a|tMOTvT!qY=xtG5-Au&wNm5~Wzo0)ha)($q6&EN|ph|YQW z1{nNP%+D$xweK2vx&g^%_XflQ@RuIlxR<*t(La>)!D~W^zPHswTdsuvd&2K;l0qjxMhU2}b z$2S`teTC5_C;Jn+im}~}^>@#~$tr_MNpne@A4m%9DSle;*a_%_QvHa*i>M{}O20t( z_sLz$A#(&b**P8_xWVP6w!V_jiy_?B8dE;`i7RaZUMvP;Z1$UxVKrikZ{7Pvgipne zfZx#n1Kl7^sRl?7c1@G>KgJS*0h^f*!*-`Lv0B&BN)lv1^#;8-@p6}nqs<&_xRtT{ zE63|^a=33_d*=0|Gqj@nQv#cy%sb4j=i5z+Xl# z@MkpWzGjy@>oFAFLXNZ)WN-R>#~#w9OSaaVmJQw@CE(HMS^vq7eu`z2V*16I!6uwZ zaIK?!*~nl1t|_kd*HCz`O9y6Mq3~txZRUpf!3n7WrIF#~Acdu>K~8#McK9%z4w-Fc zl!5%{Yf3IL|q_ROzeQ-925&YFT{9-1b;Rxp*i~@Oe1Q)#ZF% zdq!t4t`t~9Y#(#s)Z%#6{2Sj>5YmDVnor|Gfrxe4V)UEJJ)z|kp6}D*hSA)u z;pR6-B*kmC1nzrK!ShsWGW%+?sct7F^!3eehLwnNy+5$`0Sy#V)oLrxZ(hD)qwDW& zMJgR{jf~yvpYLmPOt>x2Oink+yq!C4SyhO>xr*wGaZWIrpbh1kI2@+N*z(gt3$>J$ z%y&kr=Y3qn&Fm<5HywVgA4EBQ%htjrk`a{DN3Q%OyLxrn}7X~F_ zr=~n?{0S&b<-Xx;PCCl)M%y7Sr!%EmH#GjJR@C?ddig9~Cr!XX90_9eugA7%?qAR1 zT9N=mdn^g$ETf&=UPQ0hQ*gF6O*%`0tBJ#Y%+pdT$mWHtS71K;G^F{-o8j!}#4b0= zt3b}3WGj;7Q3c^NE=_y>YmYo`WSkwYT(ch*NY^C>a{`H4~ zc=1o6FUz3)kB+rpbyA#yIA+(&5Hz3W8P#Yq}*wwI9AZ|mSTtwlHMW0G&Vmhp>J&_J7B0=@?upB${g z>Quo4uJ?dHT&k{Te;}4eF8|%O3g`3#eMA8M5vdy|O((UHf;>lb*IQ1yJlE0QGWC#F z`dit94xaL7f<`1{HCvNh&N&@!tZkGrk`bO&53}8a8?tR7Xi^;CFrW?VFb?P4X^oT$ zUxzX;vN-w71zdHNr<5}ovFm8|U1%mD7#Uyt1gQsNc8Uc{1HHAMh_SN3(^WGSJjyLB zqa6V*$W#hY;KY(O`Sz+0 z3XQsanPFSwMM8%(FvdeR;$*6yFkv1Lk;j^u+Osd>BY_nfabW+0?KSPIB$Y3h(CfqQ z0gfhy{@u>cSMSxVMmI-FfYJEkXHV@GH*?rgd?FPc8!s6`A(z%u7hhk2+@;aGL-z_EzWJEo7NGdUa-W3>2ui>;&e%0wHv$TB{M8}D9tkrNGM*{)}WP;BkOCsW9BD@?m*YqGTMVwB1-BU zE@8qhQ2#dLMOqLWDSwmG<*qd`b>aZIbR5jJd6my|1)GQ}s>9`MffB=LF+w9P0r}H3pSmo* z+5RnsPOda^DhK4Z26*AwoNqc{zu5WpE|FhYjO9eiNE*1!>h`TFzWFA)gP=hARhJYI zq-3k-Ueqpa2}*eum-^Q>$stp=EIkA*|hA@ZfoI&^Z4z4>4E_#kKQOZUNzTBSHmn>Gy_sz%V=U^t0cT=`Z)bRyQa`MIR?mU zg|ASpu57#c8oSGz*F0=b!NK8+RL4ja3W@Hz$?k~W*w?hWlo@46 zmr9N+iP%}{E$(O~b0QkKf@+`gRIYtDXTexzSq*Q!s}fxu39f@2=l!Xp=!rL%MaCK9 zk)zuaE+)9A1EdxAEy*v;Kfe8m|H{Du(gW{mw}za=72+vDdAkd;+8+R3D~a35?F7`I z3USa!WN~lruhx&Bk`(7aS(gm}(?kg%*;o04se|hNGP_CorrSk5XwHf|RmNl@P~?f% zoY+4FSMCm+^;Zrn{13m|lIAyUW1zj|UGvr{VJP5k{WGhWH-`O8T~U02n!<+iElp`; zxMYh(-(J$oPPgx2$gp$MgZd-{b~1uDGKdVZ7MB7~H77$h+_!0|f7e&`@=ZsRyDny{ zFrkwr%R1bz(`~egQ{pka19vim&K8la;;y~3@aKQ_ri+BoHa}WjknH5iZ0gzK|9Rp_ z12cHNyFd7pP?OcBsl{)TkC{o|gbsz;BGdQpy!u+?!K?Mqpl4C3e;Loh;ZQDf+lsdzEv=P_=MIQ zdE~2NRGv3J?|383u|!oaz{`2!;+;ouv+9iMRLR&^+&Jl+I(Ayd!ZVDkigy>$bhKzu zG#B8-=c|0kBi#$LjEs6zHuD&c;#FJdA=wglG+dc;7l6OZ2&K@T3RavglrW&jDvxG# z?PWYxt?7%r2E<4JWEF6bO@jJ5*LGLi$~Mo>b$~9M%ho%pmIMc*z{x(;+#x?FFV%pV z-yRx_5tFPZ4dT|noXF|uYx#VYBT%;jjLZ|o)pWxXK1SA9lv!20XisRZykT8Gv<25I zD57bW4Pi5AE;?T;dvIAub3_5+nYY9xT3+M7A4>Xqj{3kmic(+~qgMo(vK-9VCl4ds)q3zq5mv(8<1E>Mo?B(k6U4<7y{)yZ_+~E9q;_~jIVU;9q7G}}0 zLzSL_1;Uw#fUd`MX-xQg$5%4+M`Ev|R@k;waA%2@V#EtZN*IlgFI|q)iLbE}YpAC> zWD=TKD@;A1va-4?iOy|%RpOVHdbOVp?#DijZ#w3P67lMwz>0#wYy*ystPNL(2HL^R z95pTDueK3tGG_36jYK|Q`#9dhN!({${#>U|0z8#iN8Iz|kbDklde1Z(yuysus%{kY z69GgSyBmG~Vi;@h)1+uMlKYx(+L+illDc8u^zUr2+XUNjk>Y5BO%XGtoTC{jZv+b! zAOtGU=S@e#MX_?de%}HM?L$d=O0%roy5|Vx6T{BYS@d}1c&EFat3ojlN=gygxBn5e z@0=N|EjCo`IBQ)(@q`<`Lgm#TtX;-Cd%csyLQ3C<1@3k!9pQ=&ek+;Y>j67B4oUAZ zKf57dfE}3gc6V8OrU(jxzqJ*pPEiLr-(*CjhgZ-K_-yop=m-faU>31!W{Acc%fA1o zkm)B-j?UG{={)KCo#YJykDo||%sTq)sv@RyCFq9V0P1`I_f+!clvJ z(WO-vVWGMtz-8ElKMsMfMaIpB{C{M)*_|?u9m187aHNnr=D~3! zqR5_QMPxfKA`Om2$}x(JbChwMgHSjb4VwG7}^{IV0lz~cr(9^hi;TWUJQslPakN0zEK$vu7FC;51YRC z6J4?-8+mYk2^pq$7(O&2CkDFZj8Bxt34!vpC1EVphI7q}t?(t?Qpz2Cf<3^i1D+gsbk`gf`!$+xBCLb)ZZ zL`)8tQ`S4Td{&bgOrK@`h^6_q_FTSx9(R^@rki%wv?E`~ItdyWr`?LCj)fZ4pHX?yqqEE9g+n2TK^@@aNFWn9sJC{{o zCuUDbS#NZ=2<(jzF<6!~@RXiDn^P1mIKJCF*|8R&$7y=F!{FqUY>C@tP34g>x0db! zRl(V5Zq%8E7zQMg@!7bciJg~fbL(&K<(u~J+r65WSa{BJSZw|6uiMH`jMtKwF=E#_ zw^hG?zH5R0d_KviK)#*Q4i*G<9)5S%%Njpe2egj9*^5)?biTs3m z&jhzzvWipt9WzdY1C<$iqoE`bgJ-M*q~BGi@lou{AN>UWC;?Pf?%MZYNAFxQbv=0r z9Enu|uRH&m<>Xv_)d0tU5P51O$kK-Wu>7^=AtuaL2p_6)QS{zgh+CcJ1fg*7iy>bl z&z#&+C8rMjgo~Q+)f+uTAp_fiXFve2=gjWSfFOCFJ3cPHYDCkZ-Xzbvv^EX^SuB}` z-+3F-bNjUDqleAC3B-4s2}#NHbNb$TeZkG-Kke+Tj2N?6vtw#OPiYw|_a!GhhA$!w zX7Gl+t{g1ONz8JVL%7!jI$n`Ja?Zp73Of(%igj4=agc+cA>A9lsUKX~aZPV^4Mi0soD zs3m<#(ayAk_;gawM7>9y$@b8_{pO%&dr-lZo&cM)PqwW(08rx{8ufU7v4NH1M(L(I ziVi*Lu;d-ezA=nh;}HxO_nfbDdg;UgS^aWi1E?q_+WK*AD*p3=UrI-$>hX?k+|`sT z$)05))gwNwEeyu@z)i-`67vj@nfBA{^5HJ%?9SY|I|B2z!Vzs%Y;dw+)>MA|c_n!o zO4a_;Er?5nuOc4CY|tMErV!M3H-Q==;JBHr9Vi7&FL2*HPv0K8Ix>5+AmEwTaGuP9 z#Le4D-(5`t!=*z(e{*~r5&E>rimjva%YC@pm&v*w9oPM1H@K_MdFD1h|B9^4SWp-} zQ+&c*R~UFkfhr+}e&Tq6{9~2qrwcLx4iWqSzVsgG_8Jr$2H8l{9DvH<7*FF(&Vk%c zVe42p^|gWmuy60M?ss43FEj&FukoVHK;QQByYCZJRKlO`-zR3?xK@zoZ=cO2p ziaqdXr|!RE7^n`AA|6TlO+}=_ZtzgXN@C$+eAP!`2C{`xW%q(H+N;?8U1VSkc*)Tu(Z{&>t9h)Rwg^~&XYv>m)>JJ7h z{paW2YdzV^=qoWpEp_n>eDjZbW}r49XBq^$jT2L|Q!u>my}3G}u703)B8M7W(=fw( z!^-eHxe?^%9-xS^AL;e>)YF&Y^+tM-dU2dNI(VfZ(@1ZxjX7G|B-aPw*V%i5p{U~b zXm{)T_9oU~i*k;gtJbs11|sVb@)%CdOu!g!;`C&Wo8 zI!u(J?(D00*hWChzC9J&u#Nx1B-j0BG)oFumRjqKX*&t!;2&5E+=tlXAYGwlcTt4c za==^<4B%VZa&`oejLP4yZ1;Q7E;2f(^mz5x*9$>0XHEx8i>f~u%KGEa!X$T!FzuSSOs#{T0S+{F$pA@5M%<#c?$*_}Y5E zGugo78gFyd;tEWk_GoLOK*IBEQV{&}8xK6daxs*hC=Q{}$;D6^^B&W*-*R`C)c#Cw z2@wQ|P*(|24uGBUoI&LaBShlzkW#agTdzavUvq-WiSAJLE+y|P>SvDAN6HtF@-hd! za8yYb(Q@?<^zrBbTJzWI-@2Rg3I>?(Roa#Xy`?{9roCVHYP9U!@l&l0{QE)gZv4)R z(r17q^qH5tb@O8L00#L=voD*p5>jl?+#~TOYUUpby{BHw*gZyD8sI9^#)c-$ z(4v?&bf>Dbxh}5VVhVMfP$OwzDmRhnhHi2<&%Q+;No<4Ry9+KoVe&fxI;R93^7kPL z7L_Nf5j#A}1`K_fKU5G7dlG04ip0`2ouuB(W_KQM2Q8SpzZX5WD5o(=){(+2xO*#o zYH$&)U0>hwAJHjAzFlb#OJ?5PR6vxa2il-X#0V=7yfb^21^Qsz*4}zV38$KlY~in= za#{AtnU&?N@T}i!UO{1>{9k-OX9Y$KPr{%y*@A3ve1UK{1NijtBM8XX@(pbx zwAf!8+cVm&~Op+6(|oWlQlwNoJitUdhGPM)(^Zur1$<_ghr`MwXvpZ4Ar)xjK8`} zT~!Omw4Bxa;fjLp(Sgk^>VYv%c;qG+aWham3?VpB!t5OrfPZevN|$J-@DPf3$N-zH zTp$XTz%Gf276c}9j)DW6P^wYXns-kAgySIPihzUoi8lP^97Y`4kBr9-5>9=vF?VY` zwR23wfIA#s%{(Bp#bK^f=Mf-@O2(aThIY#XiD`%S+Jpz}UyMvf#wNL4hGFLGJ{|ld zP*~>iB&u8^+kfbMpo700{bD(XmIyK?Co$juEyz z+Yccgk%xffmeRLG1SL}i&TUqcvW?gWX~<8QHd+)e*i>;EsIp~!?PuG9qjWe8bd~El z0ZD;8OaRmQfD1qcfRKIS33S{P0FKR8Ez&ksT1?u^YQx<8D$f;*0^GW@i&+b1Hx9cM zAtw}keCUPHp+Sd_SFf87=^yC5GCfl!im0y&J5c<}gcUFKsQ0X%xy|(VtFu7hZ zU{h6Y!#I$DBYHNktAB{0uil^g^o|vAwY@lgQudG|sq&`y;y&L0i?~9YqdzwfNaD@d zm#yTDgDx4OtG&@quYvCuKeyTucnLl8yGuLL;ObGiOKpei00?U-ssvvI#lln!zPU96 zsK>A>fF2CO@XuJ6-P=VepY;_u=y~i2Qw!#0_XWmqv%t`*JPT4oDKbI z$IjioW5tYnh^~FUaIz$R2{R=hyHaNzgS7nt>-`I^<@s~RF+~f*9DuU36%(FUYOpwX z_ovc5PP6&Sy=m)g0LAbx?3$4^L=d*5gWy_IcMT>xE<-T(<@jh`94y0Q0dMT%)t>!y zWPa!ne7(jUIS^p(cJYq9h7&&AOGkx7oGd6R946XMRc+-xh^1fHp#!`aki1L;mx&(> z^zrI~500ry=h)80=TToBTHx78!l`=E;Jo+xz*bo%s&8fO+Ww5Yp~M(u6W0*+;i+k> z2KhwH zFE771_)*~VhsZ#GJ{H_bxsTk<^|C@8GKcOwDLcQQ+|kKnJpyp$7se39EdsjM&C%{^ zs?XLHyQ3bLn>f^!=U%vUfI#y&7%+3yNdzzxnJIzeIXW-S$8#1g&CD9%{=peCgerdp z?4jxG-M-zB#Td-%7-~K-wwsvB4c?DyCxc-VEc3uDUR~?Hv2MX(_`1h$4{GY~N9B94 z2W(^NihSBo&uM>Rf=OpLI%Zz(v7tR0A&a!NKFm#^Iv8>e{aGSUGC z6zCs#UOcnSakLi)0Jv)D#b{%fC#pd09Kp{AyJ3!=&Yh1-6bZg^l~CL#<9PIqav@|~ zaNH)Sw=fRYC_iMnioOlcamF-!cy(K?BW9vsaU(lVEU?cWE4hPwf^~vDCMkFLKx+7 z*F9V2n&*C{{!cy)H?Tk^{(t~8D38QOQEFG|e#L1TudiRT4 z-;)TiYB-o80L2t)96ghU%HK~-8ze*k8zLJiJxIcChWH^Cel31(Nt|M1l!`Se6!-2& z??d7FhpU9-e_ipsb%;>xObY8rDW3E=C;DdPc++zba9oD(*`75U_qZ8(v3Ak0=LgpSTdM}L7Bc5r$|KB@AKo0_zP)8nsTMaM53$XOQO;G@R zzvSk7mh%!RmqTItZuiRK9^A?7aM54YemoYB$dPI##!B;VV0d;zY`%)i5|k+hcwu#x zki_#uk&e#sI^&@13-EAVLZghuFMIX|9P{#kH9?_=nU>^^#nt7oQIsSrDmy=_nVhTK zilO$K)m}0q6n}C)2%m2ceRCLTmES7{zoEkgu;zb@xbD^Y>wPc_&L6H7m|VdP0LIZ9 z*63Gtxl(cf)Sc~$bo-7kzUyug;BM|lxapw0-qt-jB#bjOZzpF*dzoer@1Yu*RZ`;t)F8_@UA0VAW@wczKxGAjCcyAHZzMke?v=tB z=xrT7b&2)%JY4GhDln;3!<0I*orr<;CWWc)_FI@<8+qmRAnc}QbFu4jL68jn=dI%& zx*US<@+WTj_~%q;ndGv&HalMd#xXpwN6_Wl-2!~AE8WDJnEDQJOn1*Q>I)=ywRInh z#gBZzE%p6fHLRx;W`R(@RmEiwDs z<&EQk_}WNGz(>o}xQIXCAU}HuS}YbAi6M#LeNg6IX_gy#!EnEQ(324sj8SOJCAKYr z7?H>v(@ZlFn^8A*n~N0>&Xe7bx}E}f8G(nwdcSQKoO76LkU6E!1Rt_AEX0llx0m1P zjYU?sS$$RCft>MYC+#2Hw$=)L1@;Aip8tz4Zc#vGM$>7}39?h20CJo6A|o;*WAUvl zzCRWzz^=v$`F`rPH#w5KiTj-S%>AR~W`7(kr^>%f6)Qcvce}CpG^9MXad`rTPnw=3 zM${T6&zj-1f{@ewxwW zcP#YSmy4n|^eE3m8Rg^T#^psN`nMobVGPoo8AH7RKOv`N_eo`apO=93&j3(?c_0sV z;?CqWj#l0DQj0?H(M)$mDe*A83Ew4qnt~D&rB!H(e;C>&9I{yk_}N-G^77VP+%82fegjFEP_Zs-KwSt`%{hoR^bp$v= zZP=hS+yNNMtbGeo7LqPkMSB^V!MNe1=M~z|bC=3IUE5vpQhD*DVzU@zM)YmynPDz< z>AeO62Joc&w^?at)a6N(a4ZjBHFB#e$f@}Ke6F{xLg=f=r>3hLt*d9aL#YZ=mBxCw zou_`+|GxYF!wzDGKw6x-FJqON<~@t@?dHe5#U{lR-S#Dn<}y(;%Mt^btA$uFioBT@ zr>pwINS4@SxiWYzJ+mbsrs7=2b6Wuwe4@Z|z*fHy$vSy%G2;w^j0HLFt9V)byK{v{ zq0M*UGf7Rvzk@Qv!{84QC^^=v%U#Px7Qd{GM+U$LKsIHPE040K^ahc7^7#3n26@ zZ5PFG_x&HzOD_j}H=TR8lKxfJjMgVEQU&PttTrFG5@l}ML?o5TX zEL&;y*#oBcq%Pimb~c=l@8-c{M^`n8IL2Al|AeDSa;E&i{8-LKjd1Mgi?+|76*jH! zPBa&7($)88reAm3|1xzO`t|<6?%?XK%RvUP=QJ>_lkn%m-GGOyPsn>2+Zjf&wNo4_ zN@WgZD7OEyz66g~_fW69Ld*<^Y4Ax_@a+5)CSXX>pJ@>qEdFNkCT@D(c?MEYMhRSr za^!DbQH{F7X#;F-ifZ_&#F1Er)PnQBBHqAG7wrF=a?*IMlP0;I=p+@JGw4`xL=ilE z01N*}jBrtEc3CM!zwJU6G@g;f_p~+I4P{GAx7*^1KS(@CFG+h4>m`krHMZjn*b&x+ z9OJb0iXbVAm?VCH?M`yJr@uLc?d@L?-M#*|Ja)mt@~`W@yRmmcUun>=+AXc* z#0NUuHWR0H>=VzLVn&K&G_{$?dg(%pWt5NB-az6We7(rSxY(Fz+WLF}5{_5E-#;{% zFwf<BOy*ENZo-@6_O+37)?!m>akKTNk3FM~WUfvE%VR8Ft6a2RVeK z`a9XdXHQ#@I*c4INX2=*f`6aC|D2d|uanogDHWKS;{&sm3C6~kuk`@84ItYlJ6>J zXG$C=9$gNCPkDcHc@_-Q#I;e9FU6THS6Rnt8!R|LkA#pZpeygfjEYLQq_Tfy=H~uJ z^HYpp_b96X`scvB5QY1lPp3 z|7gu*vtLcaShG1!#J4SCH(T*=^tn%eMFQO!1c$lp zCvTHKn^_FCLuR4W?(Ez(Wc)|i6fb7}OxnAx4^q4s2;LzxDPM~#9x6(k?-xaKZMbq* zL(EVCpY9Dy9t1f%GSUNZkp|yOJV>b(my6Eiq~a@4{lYjciILYP!g8Kl#qnEEz5oLu z@4}5bx3f7Wv9R;Tac81wU5;+}wP}6lB`>WsI66WpadS)0riQ*5zN8_@d~=7BNRUJA#4pr3)t*Lx&*22Qa`t>rPa6GcUW6zq!@)s zu~cbGhy*BXI2m_NHEK(Q3)Q;JS{Ux7Tup{uS{UfJ7=ML+mrc6t)F@MPlUikco5YK| znKQgNoR+v(S=*#efQQuM-DzcQN&0g11!|lPJo4?3gW~R5x1A13e|J&)!q@8Wy+ym3 ztWb1Q`GGvdtptgM{_lPt%hY4&<3(Gm1_oR|Pq=KZ1UEUROK)%bMXY{*BT0xz6W2|; zWc5BC4qxZ{J+-jY8ikAh1J0$!(D^hKcHMnl5e{ShLv_v=8s|h|O;?Yw$S`{hsGpGkGEPDn&n+KR)1DYC|)@HWV!cyZKbn?)- z_qs~tEH=fKF)ay4YAZA2NEYsacSj?3$;`OEg0Ezw*qnfXP1?la?%o}4aG2kd#1Q%E zB?5>*cz9+IyhDA;Q=uW2dSsk9rkIv;P&c*pEtZ31JC-cx(6}{3W6^PoZPv% zrv8~*7iu|<(jS;D+7AEXv*YknnmaeRt##{dx{PIOluS<<6Kzs?CdKf*uHvhMbwv?k zf0l_0+FfD_h;}I;M~jO>mXzHG_czmacAf_ZNHCJ37ZJv}3Fz8lnZ1zv*Zj;X=5OR5 zytp)|WPCi+K+0kkDS2t&@1!_9 zqB@F-YAnDBQ;LO<{qrNQs$w|_Db7VTae19s`Cxd_i0!aMG<_TSIJWFjRSWr&Px9Pk zc*{p@y!Pua61fjOekuBzgM zyd*7=dEiUQ{hYuQ_e#tIQFT#|&wj#CUiN_f(!k34-Rz`mo%Qdf zNnXr7Y0U1{g%J}0Q3UBu;g+qO>$W&$_s05A(bUW6NgRTXV5yD{XS?%saYe~xM1}8@WbT;~@yRqi`tNJbe9?42h&Q16$&PTtn9@yO z7=;x^R7WHGax;@_vY*CkTT0_$8nUsBh$iRa*SC9`v70^Np+nYV@a zP+DIQT}6&BT&}%iWNI_g5LeUmOX?SKxzg=L9!z9Dk4u)N>@{dV1%y{q6!h_{G&6^px^T1dRhms z#rS-|nA;?2VzbH4NKUKv!E4-@m$#`8(~G`7jm;a3Edw7L!G!s?+O6JvDn3@^P{t=C zfq9~>=~C03SZ%X3`usWwit|?CMI=uHe@~j<0(D_BI5j)|Vekmiks}0TxG89M^ATN2J$3yYl-f$@X0ocUzu=}3Bv_CY@tu&t|Pe@P-hII&a}0p zAUeZLWN#4Av-nO|Jjlk~LX7L8o<~_e^f%N}Vm*0H5O6(K>q{Lj*!ocqrS^U#75fbT;-!ss^5W`$ z9f_PCF}nem-wj#dtIk2b-mD%qda!$NB$M;>LQO?%JwZ8ieT+7|xN|p=2|CFK1K=ia zZ;1%hR#(ez$LHOhwCJsc7Y4umdeTbpnJi2=b|~tl6}VZ!Du_{b%|Hrk+j+Tvf_i7vTTML}`7$#QCzS)fEw#z-Mi-VCAEOtw-5z)gni~2!^ zgZnz4C5Bqy&_=SduIyR%aQ3etuv=~TtLsRO^bxlPc&q$flA_c%`cgZ0#2cQlP$U$S z^#uuAeNkU+ya+`C)d#qkgEH}z3cw9U zm=RJ=ti_}aPe<&GW)_QF-(LQy)@~gL@7@RUsBZ;l>vvK}i$iaEqbAW3&)ydoQe27* zN`Dl&2*FdEHB5ik#~ic*0r^#Ys3;2~iH_4@!R2X`%4J6GG2=?t079=3RL4yh4EHjV zG`@$RPUO)YU)?-*{1y|GCyk$UUAx|8^_pF+Ar^K7y1*T9RKp~f4eh}`@XMp>2XJMN zp?;V4NEdM!dRl~UQmXVQYQWYcOe?$E6_*q*sXUo_xq)qOZk*zw>cNLnG0f7f>rrMf11RCli^~);s)yW`u8#dx(h^ zDKoI2b?~=f92=Zr7yz~e{xpRcURTcl+AJW zO}R_s7aC9PL;2Dsny|Qoz=j*#_knQlns|_i%W_H@=*UOFlF9<%AslciD&TV0q-m#v*Zt{Fo?Tj zS6O@y>f5Q#P-yeZ<<{VBL1oKENICzd#iZr;5c+JvmiH*%_Ug2|Q;j+mrn z!gtBgJdv2ZZ9IJkpLb{9m>UM8wSO6i+}Vgxjx@%Q?jUDtVnw`R+6l2R3aO{W(@9vG zSB{FUN%T6F3Ac`Qmk+Vm{Lx;as~m9F4ITR|Gx;R+4jXiKP#an%X~>>ou*XR*_jdfv z!Q|qHLd|u;tQ$AANAI6n*Y7(MRC+c;Bd-34Cj5Q~D+qcv%mZ_%d&@f>R&}wk5beVn zaCU)k^M()P2p8pz>&TRnPA&??JjS@2y$)1a~Lc@LM}sg2*qXs9bPhm86;y5 zFHdxL_PjGivtIzgD}ps9$=sYHtZid$#BYpp@=rSb>r})WKEf$&RQ~W~gZ_>cRNW#M z&mI(xiMH<s&`uNH#EFCvS9Hv!c;z}kPX)A&BDFUZj=-zA@s3+u{enNUs)0R==A~e7}rmknZNJ&_}N*Q_1WjTzUHClY3pvA zdqYaU5(V91=5-VjUhtEn+qyE4z0fuuEN{)`LB5I8eqkcJoB}Nm3=&dSQH-=PbP4+; ztJh|F3G1?-#&!BTm_IVfWrvA3J3%AtAQDOb_^1uZnGAM!4xS>cEcP2wvV|!%;uIz| zo9ry=jK_DBz-9bh*{v{zyyBiHXV1sKd-}U9JZn~8$!11D` zT8wgIyjh4_O~quetuukSTI+`K;Cr;$ZFkFU*@>yml!CAPRBH%D4{E;HMB#NN6`n(@``PurhF67I-to(={XfQSI14r-M)UiJeNx2>)uJAo@Zuzv{4YBab$qWfvtJ#T|xjLrJ<&JE5^l#2_z*>g&6a zpWo^y^baV8@GkWAdGN1F$5B7?d+&a+02%#Hlz{8Kq2ebpqp{j+W+ChWDP!cm6d~&5 z!y-bn0&Wgvy*Z`9YQAYV=qs?Mkf-l~s8h_S%y&?dfB(eG7@ds^cGd>VLo#~6VZ&oM|EF(ikt6YM#^g~ci zLSaVEe&i>DOk;u7xeT$nLrYm{oGsS>87zMPY6!6Qr#sjY$@E9r5>Zx%Gb~4lD?EtA zTr<$|B)OeQRtS*2g;R3Le_iD5@#c1!54BWGHt-otMIv#J*X_BEQ%5cCz;>n-4Ji%- zQGzrVKo}MahnIef$wRx=y)ss|yXSeG_NwmJ{eR}LK$S}z?6j4h)XsYrw-p>1jh`ng zu16n8j~w|#RLEPmeqo=x!$sRBH9r~VB7WIq#e?$hVh^_Jv-rm9a2Ot>URYjht_ zjQ!5g&hq1dcYtX9x(3`{I1RX?y%;%Uc1hz1tTkz&@zq+Oa+nYVsxPrNv(zhR_@eA00~O zqth*b7wgRYJxPNOK^D2PhUM5r)z!hlxeAYN3(J7RnwTPAW9ESM`)$ZP?`xXDV}TCd~R~Jm4ogQ+~`#|Cab#pXI{9XQ@YE4$DV(_ z*zz~j_?Hj~dU_q8eQ3*peWM4Nq^Z2ELC(zhn;DoaJYhj-D9CSYoT@4Kbr9aPc=jPOvi^D~L3or#T+xEQa8z30cZZn|ES!$hNgJIwC7-cY>U2S6js zVxK*)^*YW4uRW=s;MH@BZ>h6~oVexu%I_|5G}sl+FSvaAR<5|FY@*Cl2PCP|uuAWg zL3m`CFB8~WLm8Tz+RTX5^{>Y%j3LA2*QIEM+t{YBVhD211+{8pUd*M6ebpd~J$D^2 zXSCr8oF``miCjSNbIJ%Q4A=fqS{b5UYzaQJb&5~yHaV2*w`6mH*;AOvoY2z1DB=@w zv;YWn-+t6=N*lmiZigyRo^t1Jd+Um$^s9pHr^s#|`KJ1hOpn1UsnXYG`%Ka%llA5r z_#n%5G*A=+Zwijzzi&FrMB!^|os^>3D;{1FL=r_09ZK!)+>pdq`3fk1EWL~dD$#p2_GKSf=)dMX zvr_^f<;Sx22V6FaN~mYE5kvU>rTGXqc{hs*6OeorUb=K2$&0yl;TNllOYJ9L(&UM1 z=Qrn_RC!+eFfTu-?^h2#K1u&d)4x`jer>q}8Q_tmNILFUIQgLS|fBm3cYnFFMB+AbLG`Qcqk z!WJ6Wd4?UWRdz6-S{kLw<=ZDNJFOx(m2c8i1RY_8U-Ecg`(88rzV}v%Uj1sKA%|9| z{~G)4zPhl?i!&y!glxMW#T&-gk}K-XN3b88Oz(Dc|Kk2Hyq9rx-&A56SV4{dm=?h@ zQz!Y6XDH0$1X0U!BF}T@u!9KKWdG3CyJ_>%WW3L|h2rrGuRjz^ z>_K#;dAz%6gnxC?bZziCp*1C~6Y~?L`Gl7yB>7#g9-{Kn4o8Qq3`j5gq84C&wp|CY zq4^h?)OzQT=^50*)`a&H!G_su{AoG1^d4HDgFmpc!g9dZ=L#bT#YZ1XQ0zTFa>Rt1 z6W)<+{@o}H423)!I6NQj0O_HQ_S+8x-o%T%fB~MpWCNJ8?Py{~i51ISI9Br|CG88j zdKAtZO0z+$s)w?3k~25b)w6k6fnhOGU7V&U2R^x9&*e~KcMPY;%KHarmc)T*w6A8bkan9YN^*Lj#P z;CLmb^+OpSG*U2O&R=O)Esh%JW~%h@jgmLx`d#V!*SL)FXrmW)GnNYD~)2Tpq)!&F=R^Hsp`MVboPsKa(!L zsPG^^?31v`8_JF8MZ#}!lx|hae0Ig@HcsYcEO^Wf~;&!?4RN#Nkew@wl?tR0vYMK5*C_Lpnd-NqBW~9 z^6^U<`%$VQN&!X}c;el%??6JbTLJgo_&I%iGT zc^W0i|7Y1_mt%G#8G-Et3xzM#IH=Lo+q{^4`P{cV72viBh8ro$HjQ5BFZY@Lh)U3b z->u?DQfnuZ1%ed2YZNZXj)|)0JiSGV5xLr0gPyiGceFH{$Q4WXXe~$rgZm&vM(W}e zF6xRL6YUe9!Pc*k)apP;y^Ovzt);x-G<|m0E^6~a3zVC1Y}rb7ezi}lM!A0Q!`nmi zI9&)Gwzm7LdqDnn)dhn3y{QRm=@E3+n!%^>a5VED3NC|e`jQwL!$>)2OwpE|R;l*p zuS<(_U&q3htL$wBs6?LM);P2i9|k%$a0$pbcU;VVzcf`FhBT}G$+~sL)NHK$7^fjR zmY>Sy3Gkv`zcP)YaF)3~U3Y+rHKwE!M7{fH7fr!%c%j{7u59@Bq6aIYPk8?N(!uUR z=V6DygUco@BV&jlIJecjwDGFeu9OdDVB7BhR zh6Z%tyDOWB0hro#_10)fZ(`3T&&?A@Hc&KKBi9xU@){^07Pjr=!k{hhrI?S(L`%Lx#+77Y@~ z!EByk1DGU;WBbmTUog7_n{<62B+S$4KWugWmPkrRm0VkuyE-cuZt{`zEVyKBG^(Ld@XNQJrGbxu+x8zbKcAbO*0LbR z*7f_lj9gpqOl@i^QNMn(_p|e7=WD{x{{MGp!AVF+`o5!$fP!l>o0|o6)z!b5?;~pI zK;1E*=uvwS5jGgd_QRW^mEoL<@@ESOJ zKe{u0z50c?ee2Ep&<-0eAGqZ{y9>sUYuN>jxJjNsh4Y0s@8^Qb_}_dwWGp6hx1ZP9 z{`7hIdB+A|{h?Kii<>9gTlsgUiDZa;OSe-Oa%zYzQ^Mf<1%yv{RYj^E zv+&OIFwJp{gDd zn|#X8k3P-%>C{lb3uysZMR=HiD5VPQiF6;=wS;Q)UB`Ij@hT5ej|Wp8v6M+SKxl5D zV^t>b-tV%Jc7f+j3PZDx@&%gEN6Zb?@Mqqpy53(Lc)uUn%fKpGcLAeHMpOKs%ZUlq zH7S839euhS=c7A7l$rH+%!Ox}qD{wAd<^gST;Z#JY6&&QQm5q58s{}X66YowEN)x8 ziPV;SE~9gBw!p%^Df`6XUq;JS%pMKY%WY(bpuaSK5K~e?<9q9yCU_P-Gln`kN+~Ui zkM3l62{C@>xG+JzherXyF>?il+x2`L0}9ywxV!=>-7XgVRo62hqPrx6hb~2qCKk_I zup+uvzh)OTKg1UI&O5Dfy5^2u_i=iRF2Tixo44{}j1(xL-5L8zwnlk$q1adk*vxYe zL`P^WT4{rn7r;n+-a71o+VhFRa(g|l%9DR!K`-f3bM|4%JAR}y5pgy{cD6t5H>i`u zGv>MEJ7->2NE~(GT1B1vb!RSazD1$@u2-jZDVj{ou5cpO+?nnrWyWhO{46OaHXf*h z`y~X!YdHj4mr|xJ34WW3sVTo3WuK{ z9>?9Hgl=j|6nFN>%%OPwJ9`f@ey}np6#Ab8|E=yGNbdXhe!V$Y!}Iy1=mqPrR;$zA z=Dx2}B<6{YSubO~F2UKs?%=}uB67pwdh3}_qJ9K?iU55bQr*pYtiQ+Y7um7t5?H#L z4Dj(p*eO!dIF(NHytCyVXtdO2SId;T%NP-W8t&8%Eb*WEGsK%IYb(!UJ%|A97b+!- z#bT-AEmIahK`~4#)LjQr%%n9Cn-Q)jgy0lJ_sL4jVAH(;0QUE>$d%Q5c(99wup3%}biV1x|^{I0iXeo)uR3~2&`lpHYR|gZK?#fXP zb369fBIbdkn6!JHV4%5&J0SZNoZ6bw=W&I708lEPv_wy9DrG5B3ZB0K7(0(5`#cZ& z7dvHg(2;<%*61b~qzNUB)chegGxUgbIC+H7C}Ndi!Uqi9QkiKi%O&$eD}K|~`q2_E zj{O_j0oRgEMfo$k>0jp^*Oh~p);x^uz2ha%za{&>;=p$vt zPu}Y<9I0QppP#rS=OQ2n{!SgQ+s9D(0Oj;|W*yBxupbo^z*L8%3FXKXciv+&!uJUw zmCftk9gt0d8scDMGHvkl!dXFZjgvu!mK4>r&RqElcdsMnaSHP$oM}0V2wkbo8&eYr zA<%=^GD(90GhOHsW0rC%oG?0?Qs*XT@a(`0+>5q+>36HSzDr1bwAQ6%y6o=8wuR+e zEQmXQ7_{PyZv#N2lblp>GFE6QKwe+!M2Pm)E;z^>_}Q~A!NX&-C5&>jmjy9o4m541 zr*#sEtiG-N=nb@beYfN-1;E1Wd=6ktXlU5CL@}VlcEk|-Ar8hy>wX<;CXIgzV@5rI zq2xXbU?t7oMuz))EX^?msB%Gfmz+TL@;^z}VNnDZeY(u7-AeHh4s9C@?;g2={tKiw zqwg*4td#AocM+tQ9lTjY5Uzpcf zi=+iqLOA?|bJIS6%?quIp+0ud)McSXs!5D!x=e=te`I}mG}L|jKZyw$MM!o+vW-1^ zg=9-nG}al$NG8g@Q}&WQ#+EiDA~TlkhO!RHK7@op*2qqN@2R`zxu5T^KRTy#pL5TA z=JQ^z>$P1&8&yB#90vynGv?_a8T0e9lNO`cVH=$C`Re)2n9r3}HBGkg9!BzckC~2+ zk&gM*Lw2KT+sVq;`-R}(JO6gpF`xT=eOp7xOg8m~Lxj=*F=+0J!$}$c$qTrTb|+== z&GzaLQ?(A+&$*1-_quXYS4{^6i2|JQPk`p>;q@m&$KbJ)*sD29Ty)xdO5-p>IUIP3 zD=dBiwW?vmRN5fgu|ZyA^6d293fV#Yo~x;=HL2f`Wc6(xpHwLxFyUVps95h~YuHZe z&=^tSz*m-(lz?=De^we$uy;FC8=77-DIkd$25Wl}*OOc07jCM3jr4{=FkzVdlUxZo z=Se2!DQ>tRv0uWzDHl^6T9T*FIJ-p@o=Y8Q?Bg{AfdQ9e3O5jmE=CYBoKh1wv?;Pu z-pQ&9s&Uej4JQSf&~kQ2kswvNZK-L+)on}1zhJSJqR8_FNNSE)&FsBgd(&dUmAFw}1 z3%R)E4{FkidyXvZGvQ+^Z)G++*tIygU$E*1Gs8R3ojnN~>6;a_lHStGbM}tP@LTrw z#uml{N=GA?(u5ZNS)%_y(dpDlbf}ia0E{sUqDqqZ*+(xLi~SXkkx>&I`6z_LAae=m z*aVYJ>74uzVeJ-OC#F1{u?sg-7UK)EVt7`SVB$3ol!pAvW{NHY0D+NIMz$M zypR>|MU5wBM=7wu#AC3hK#>!rEG>rnhIsgh(;#6wo^Jub;HtKN6kQrXK7I*}N~+^> z$G0s!=OnQOMVv&WbFt?fbJu^WW-M3Mp0Kfz@@~?rOQZBjJP-x^S$}^Z&Uvl`1y-E; zq!*)HC*cIs2Rf908AOhKb#fhnmPVd}tfj9>_ctCbm)`wOAUp7{%ZAb!=8HLhT{;-3OO^E7BLH- z8%=@CdRIxj75&{t|EKH)!*QRoNQ!~PGwiY4jJO<0!|h2Su)`tvSG%e5r^Ihy8`JWI zaOD}O7hm%3is5k^7gyL(ZBSBXHWf(Z!sH;2;{|>R*Fy}jG7S`o<=V-?wkAx@ii6aL z#H6=o1)Q}qBKCK?$}KwH4!qD_|G+~O|4ihw{z$YQK=41fPHU+C`k5#r4v6lcw8%inw`9+=0ZuGg69d(+{TQ|2L3vHnGAjDDju&U$(%ZN>A%&wV_^yRn$h*PmPtz?7i0`Iv5{m>9t5 zVRssg$3G}kN5ISZPDoitNaXA$M^53dC(n9FIAbM<$a^q)8CO~mE8g1^ww)RVDh_ZN z-yBrx-2%tG@}uCnkV@_sE$cv~*7NG4#?A8K%Tz=?2zFaQlSW`bqqMzN&QhXOTk5*jg?OlXZAdX=r;aN`mufSINr0=hK2FUbJ+zHf zVu!Q2A4&nP{=>Sc2s)df^68Vf0p1M;Hhqx9COl2N#hObZNQ7&l?(m?+*kDdSOvLp> z`YQWc`SwpPQWW?YB$SpN9qJFWCdPTfKGsg1BwcQellh}Z0>Nnct4xLpQd-Km63CZc z27`|uK1PMM*$yjgl8#rqd%2$-AzWWo*v;(thHwD~PfZ9&L;u^ec?G@(`@vKY)Mh?L z^ugSh!E~M6#O_}X`KkN*NP}vRG>~kH!eo~x{LH@thh7Xk1i`z2w09X&ZD9vR+QMR! zu2dr38EM&zq>=0Sqc`HDL3b33>(LO!oG+ebmy$8oS6s;bsO7#(vnp%kVH^a$(CH%L zH1VpWDXIf^_MfBsA5h9ydmwDDua(dzOgs%S9de5qdjZrawbz@0d_8frY3dB=1>=_9 z7eb#}Htt+n(S~fal=8&^gM=saq)Q(+5KvZo#Z&F6-w|E2ENIZteZNClE(=t0IwcT^ zvn=FfMx~r$l!IrP9!=49?Tyeksb>xx{nx1jZ-cpQWP^eNaW60}dxP-(yTd;h^06+8 z{mkYd3bPoo;vfl5nUhJvRWVCQx@36IDIyh2Y(m%B;2|dzZEZ^Vd8A*9%^5_+mmSyS z7Mg#f4`R9ZIwqRepV6(EUA<=a^EV!Hf%2t1X+(SzX}#~57rkAzHg{jt*~K;-s!awW zXmXZox_d<-z-B0g%s;n874;fjfAY_6uHYydbo{aqrzR+-;~(p(%~2&4=s$F^P%Mpb zDxNCM(QcLfN<98>BQ5H2OFNU6C(||(g8$Z!Y-b?fJ);BCcV--4znnssLRcyDG?b{Y z1yI_z*?L+f9gk!g{axL!@!?Awna8VB*QniFG*qaFqmY0W@v1@ikWI`XGR=SL6iSn9 z6Bi0+G=pi0;XWtxJ?H?WaH`0B1gaVnQE1{Ima<}c6nHFYCFx~EOZRZb(iZQJ^5Zcx zsK^M9t7i5=r&LaZs<2oLSJ9}0P2eG3pxh7RR0v+%&D4~PMd)T)4(wT${g>l^D5Du_ zO64KW7P))rz9d>#z?Hw+g9hbA$E-!L_7dX*< zbCEFhnCLdt;30=gTs2LI;Ujc_LnU&CR@%YPYZ%%#7Soq})Q(cmJ*8edmtN+Cr?@UD z4I|~ncY(frX~hJnH)U`x+Flrhd`G>63?ju(cqFfbynC4oeTfy_)su4*B)&)tj&@^4 z;c4vBPb4GHP34)C#FO5IC=-pq79M*+4Np(ZB6E`F!s7Y*l7Yd21*SxyW&c!t;8J;T z7r{A0_h-^fy zH^z@P7FN&Qyt1=>(+u3ZsVqX`P~hSaQN~iL6}V<`#6$^ zs5)^Rc!_BAWN;`1bPG^Wr~RwJ)_L>-CB%sRc$Yc%eJZO9ll#RZ?u~FPwaf62p{kBe zPMio(F;#`-3*cO6lJ+{%!0+gC_JR>T)mjdC>?s#_<-{pJ-j-2&MpWm z?LdRSXMHiqrL#IddyYkh+&Py4)FlNKkopD?NDn^rH%LT*cPY6TuR_Fxa8gsEhp_D_ z$v>LZg-OD~wK~S~!t-%0?4~BD`QbqL%q8M6r?Xa7Bba7TYv3Z__l^TIdmB_l;mOIy z%v{a;_c@0N+!V1Og*m4bVqYm_%n=o>25vqj_FTm$+92h#k(-ev=#o8@98wYL^geN3 zU&7Aq1*VW5Kc$Lwm;e>KL674jgDtI))TgP+uY8oR|1gI!***-)s!f!H?p}O2e3c3>?y0Ew4Z&g%yF!;c zQnyg>sANtRoA{$iQh1#~E0-S~dI=>qt~`xnHv;6+)C(o&5#4dRa zVXogT6^lVsVfpeUCa~EjnB_P=Y_^6- z?55Z`rFhzvb8}+fj-)3hv|r6x5Slv*IvzV=+|ZeoRBA-7CyaS%gtt zS+p(hf1hdvdI(+ZKY@TgcqT9D;e8miY15o=TH+AJ(J_(EVpGo@#V>M=LD5n2*;_)9anrBn=@bQL1hzyRcZRQLoh z3`<~}<>1fy>i*le@OONFz*fpW@~NhBr>3I5z;g`W8@0f6=M0ir&R1IzWg9DEmV8x@ zJ|QOn_uZtC2#yhVkHSLh+eBvT8BGnp2uC1S4}ov$>jMR?BLF>9*=5qC9lrswOlxlB zUex`xxTplV&y4|J#>WqCN*8Z#YPP?4k=5Vt`3{V!`U~}*GY|1SPiIu!t+?#d#5MNK zb(DM%syzD@al4h-9(@?5350G(QB)!Y6$W|sfepk5#7ylSn8lTwFG>rQO}u3^CzAQv z;}te?R;G`%>|QU;30pDogE?q>r911Ja1#qE@Z5$#q5AL;`FeVI<%n^WhgyTx;&Nou zzobcBYzQeLk4FZ_=VOO+m=Y?gnPt@s@4;HY#gN-d=d-Q1!GL!4fFwntm{7wHSX>A( zcn6sjLCcCesTiv_k2yOxeta?5v7%oMv}%n5Fn%NE+-8p;nfS9aZ3{|Xlz_;6Za6*r zWojWi>dm9(`h{g%Wp+gQZw?b20~B@AL;y8d0xm^d#c%n~n5x1LvTi-Z#5p$tF`u5g zu%0@WW{eIU{R{LH({v8_H!%0E%2I-c7bhMQ>d>L5g=3eU7x=)P%VWrpug}TS&HsdG^`I)Xwcp} z?767Dn7GZGkR(%-wQ<``6XUk?@9DwNLE#I)Gk5fHD+4+=6-oYHzSF~oxWq&Bd!2(i z1C!|t!N7!7F=YF>(2B5pc5;jY%%HdTM|z@k7XyrMeN$6Xt<1`a+MV=b%AVRVg&7;S z@KJZm;orZ@F&Lw}`%rz@v#u~@rBr1>Fv-MMyBzJTkE53Jhv0`sEP(*Y{Hm!5kzpG- zX<`!OVPfWkQ#ypvLxmYAbM><(Ly-%ZtnqBo!-PI6Fe8;|he zIMwiu{e5Q*gktAgV>2bQqvy$NFxv$q>pJ--N*uYlxeRyQJB$mAv$4jKd$|AdL6J%f zSRV)43t3O-vGRf=y>c$5K^N7rKTZ)Z8|ZmJOhq01oeWckdhX^NZu5FARUD5Kf*-R@zyZx;1h$~i2HfPa#|{tF>og=$PIFBi;GUcmCqNfWCLs@|g? z>dss*SWF3-IaQe%?sdZy)fs-n_oVs~WUuI{Gtgh$_(ORACa5m(P~$KCa(LSU<{|Hb zJ#i~N)F~Ap;9x=_5-4NuIUO_A%u}Y!rV$W9k00Jm`-Q?NyCA-AOdqp-eKfGm11P@O zWMGkzwbZ1v?KCGxM6(x6^>HhC4b(C8JJ0J6xSC@9wUen4E(ve-{D@zjs7wPL^b+ov zSM2WAzvNodY+s)6X8T{6#aocVf5%i048F%mEFqhDh*9BA=`bS>G)Ls#Y>SDxOb0UUv| zC*>Z;i78<3K|sV_ULqAf?XK>m)rM;Vapn#L+Zkl+%M1f7_pVs*okdzj&_Dmr$GbED z)8E5{brTd#LyRhx1}GWrzFK?f4I4+MvvfR}u6n#(5;JGio&xsmld2_Ks4obsjgQbE^Z)aSOUFrE@`B<} z^kZEVUXpk}DwH!Tewq#N6ARxmvtwk5na^9IHQExMyyxrODY3=|#!5vVVoZ{Xz13(u zS}Zm}58t_rAz{7kaUZaKCM;gykW$7jBFJkk83`c5n0%@}%^(%evsi;7PHO}8SARW*n89W!t()=^Re4ly zER=)<5lt|AIb6Tw9Z?xC)FW#H8mwlTxTe$P!ilzDxyy+1t@-fpF9wniQ{ySU5{FJ)g`LV-bhS9F#K`xb6KdN?2M-<%Lob!|w& zoL6(dxew{q>F^cNW4QP-&p+INyf#Vdf6~G(|4*s+7rFt%YdRTnOJL5j{Nf?odTP_6 zl3HdgsK5pZw(7px(p#KcsRGRsAYqII%2%VU2#0KgcuC#clml6ijvO`G$W3N$u9Och z$|%j=j&MGk_SCTk;5hF?(?&x{E?2gbE0y~VTjTG{)H3P2Npc9U&xqn}oRWfhZK2fY z3Dvr?t3YvazTp3RXJ8_8@(Gd*sMUq0T7Ed>pALRVh$&pM^Wsi$V8$?L-JO6Ilc63d)Fl;I|3@I$lWOEHIj+f;FLs=xw+OL{DGYpc4jY=~ z4`XCVsqw5#OXeHV>-TcsopZZLt4H-lwPUF8T5~dDzadl#ddd+g9W}xE9cj~dyW`%cdUnO}n27gY2lG>vHOY%r<;h{%qDv9 zFyC`#52e$@=h@0&$`?{%06VsS6z=CPhr0$@`~HKfm!F*5wn>l%qzGkG{cTXG1?+HCBIW7UK+6a+1y;oCQxH)Y>J@j8FrB%z5hzeC^szlxUL35A?M?HmRx3RP-~nHaVQE? ztSLBR*(XB09EB0FCx3T<*DaXFNFgqV55-8@Bf9Y0#tHK;X_?xyO_v)G+IPq*uUufZk=aCy#bcP*RYikm4eg=~q z2VuJp1W0au)qrivF`Vc)eh*fkECBcAN{Qi{?nvW2D<;q=@$t6)5Ml2SP(%E?;|5oS zhe(LBhK3E^NEl!h^|>esPQc~jU-6=&O@kiF@;aN#*hj%vyUL+C{(|t;X`{Z_|6h+G>F|L_SbXUQjjaH+ub#0vh-Le2+FP1ie;qQ&Ruw z;OZjHOdaRg1v} zx?|q2-iYu%c{;j$>GIz0T1nMQ&GMyIV_83+oqXS5|J}o6-MP_Obp!7yYepGHDd4ZgobuHYaqb~(~!!RV}SQ@(y*-Y2*uUhZ@Y$La%uGN zQPy(f)I>shYZC5LekV$u_lM@C!oou;@dsu76Pbda2J6@lCqtp@JDKmg?^n*AX}8jV zWwSupxDT$rcOqCVNR@VehK9O+3(0mA_*tWy;Oem|pkDba@WJvU=y-WcH`n?yqAq8@ zTVOxWL-W*T*?y>(!1}W7S67ni`9@n$sg1*nUuOy4W;fqyw(q+f_?KyB6;oqpPQZ+e zqEmmcz7f;=+5s+38&^n@vwmY3yuFBUy+LA^RN_!~`Z?|zV^Wlfs=igS#HTiWNUjtT z3ZWDEoU;xy02xOYeg}>SAQY9FZkMgNs{9p>F1Y7PB_GiABE?18%3>wp5msAa@iID+|NCE1LPb%$0NQ(!U znPFa|mY^{R!Cmb`!Phk)0m3miQLwtbYkY_ktHVFw9J*PLoW0nB+V)%&2=k zZ3nFTjN>;63qenme#Z;z})MCxFY(hwUH8z!_a^M zw#wU2Cp6<0*U|X+jzz_cBvVtdMxsOmCX#c3pYA5}XclyaJhgU> z0m0kM?0JUyi+3ao1pk$k5bjlB1&A%A>P}YBG_hT%C9ywpBf4rDIV#ZbB~A4>!lLWZ z{yvSIn-o24T9k!`_)_a2k6c~cEY;6d9oVn{&3MLV@ZPE71~Fl{WbY2VQQIJ0IZVvjA9~Jxz$R62@H&K~u@Z5CO>y0Tuy(1g--R*W z-K8eEuwTnRWDM5EiDhHk99I{D+{w`&qI|sHZG{hEM|oF* z>WX2cja>h2-Bnn~MBunUc-J(Pd~O{v;Aa;y_ZH#DPQH~AtQVzc1wV>LRWDe2wZ>tr z(mx5=sOrg|PoYmTgZav)Oa@^k93bx4NK{)r#!872x5Wu_fUKcW5LQYch4~TeShIua zpmc^K8tHOUFw??9HWkgm!I-BB{W0z3Uc259U^8VI^OD2~%h=vyt2qzs-4w0NM58j+ zeJp+A!pW{o&vP6lTrWc^`Qtg1`!0l_1TsukoTc6`TfKXlRw-4rJRrF(;6j_kTm9}U zPs4#KQ5f3NT+dEdHxxJ59Z2(h_d3VVj3_mu*}QW1Gvk&On)}WN-w1WH8Ven3YJ#dk zFUc0b9_uczpyS0~dY>~3NXy>9da3cQjY=|Q%yJRfzAG0|%BMSkEY*}INbR6!#i0qj z;Q?W>x=`gaLb%^iTZbBccIsCI`g(;ZKlxPrlzkU5O5 z^)0I?8uk%aMfW@vm)lD0CZ?#SNW0#9w!BsNcjq0r*$;Vtxs=1W2cMR>mD~Q$OMTfJ) zthJ&)r}miI?RRWnvag%qHp~!Ib(cG}>b|ZBa!$wvbi@==^9daB5HV}IzdxsZ+0VuJ z&h>TY`T3;E^?{Ams=CWJm0~CRc2ulu>+78B3Db)yZQtRx<3HjofzHN-v@Gn$Zy#1P zn_7fbFm#XcoODX3nU~2R$8ThuEiA5Z54Uy?*{C9XCat%!{ThNlu9_a$pnxyyq^p%Slt z+JFnIqsrqS6VK;p!Jy~og>DR6h+^CV{KEOiNG_oQ28X>?_FO{8Y}CRH%2=%=TjtHj zm~xdvXmL?4VE?YnyQcau*=Y1J243&BNo_lh8XhV4Px^L_;h>F&U3zAXxn{C_D)v$5 z2O3Ed;U@lZTcqXB*i7f5t?{sXs;Od}-c7u{M+d*@F#K~LaL~a8gnChBf? z6(9Ox)d^aQ+2`_Hx}3cgVsSsE?lZQ%%r%41kH1;vxo=hRHD9(B#VtZXVKC)6$EZPR-4FFZzE2_PB8#~=db1Pz!?JH00cQlTp1*&(seDgP ztW%T@TL~4ynOpUBsT=seV4A*Y5X5XkGVo+0HA;Zn8jgc}2jZp2+m{{aWXLu*D0qw_|_xP5EmY?qpQHF|7HeJ|l#i zoTQU>juz0&_JD5BJwAoi!AU>Tums{1a-(la)&03q*%LA{;!8;?;Eb1!1oVe*Mm?`y zLKZvgZ(cUl8>Zg)g*c_3V|>8N$N%s5IpgaR3*b*hs<(6l`mF+SC>5s_2Kw zh<$BYnSM7)$jmN>io8WMJL}g`V(*Oo)cJ`Z7KamIgEJGRo`CXs=Dcktzi<In(-2xg8b2p zMLwECTzVJ3webf+;2YTDDT!VC)Jtr z-hU$0trJTu=~Jmd$j_WvELFws(gz2_S#u_(-+$CU*iuQ$P!=p;B{&J;a5r`oE(UdN zMitHx3iaBJC7EDx^`Ri1=Hm-ZA@St2+E6Dql<12Mhf_9w@p@u7p{@@A3kyxfh0WcD zIil!$p=|(YbhW22)T{HrreM0=V=^6DqnVIS2e^BN`aBx5oQa;-XTO~LNUf$kmiEQ) zFi~AMz+A22^g=R}^+i+LE1F8-&c%y|VM>aQmh8bw)5pAQS;cVwFpO4e4O6vR=)BPV z%gj?Vxkpyl%1_U3-BVz$j%y6@J$-ZV%DLocsA|cLsS9V?bRYPgJLu1T>iLOlFrj$@ z>v_3q>+ZOCYK%{I9>6*yF9tnWetGKD*b~W5z?+-tdg5js%PL-Orr5%)GtNZo3Gg!X z1X#{;KW^Rl8RHyHZ(Bp%LvwG|h9(wp59txg_Z3v|!P8_Y1}(U~(1Zz8kYU1mAupF|<9-hZe1A=r-Q zX*0-Q8ce>f^|JCl`9+JS;Ty5&!_B-O9$xs^)SvK0`QsE#&0&pCA#kAs+`!}_V(i+1 zEI+V^W&0TG$wY0GHw$nTCo!pt(e1#!>SdA0db}17M*dI%r`U@&K%3=aL!Yf3C5)PoiWlnj;8qV z_*a^s^LytXlr>@vt~J!iub(#wW~YU1eg(qGKSd&vTJB6SCfGCH%CvDLaaRK)`F`qp z&@(1i^%$Ppno*wpxFjlALBK)y*K>?n)JZ3=@O`yb>%J?%bW-Kq{m?m60smNLMtTKd zTZl2j5f!?-FvMrP<)p!^oT#f`ryDeqBU`~+5V-8ToJX5w3UU9!hTIXt+1uZ1;J$n+ zfk_oxn*&USXr^3h5YGA5*oPK7{j(@hU>*sa7V9#;7tPFITug#QUyijV8XSnp0Lu5@ zIT`KqZ=W?xI#4IAbUQb^T5PHP`fU@8^WE*T@&Ms2MP6i|2rp$e7(E|{XQm<*(Lh}4 zlHwr{NBg&O3IX|K@j7cT+IPc|1W7f?ohOMVs#|WVKd#zTPhNZHMp`U+qjcHtXhXnB zpJYaYj#$qLo`~iE<8KJBWzou{`*^+3y8E=8UuGJlGA}(vw=hPitCW_`F){P$gp^l( z?G^P{qUr^rrUx`WPy;^MI2pl=S-40QR!gWER~XlQNTe+Jj9u*Y zJr?3b#Qlzl=?>rO(6r>=lN6t>?x5LiEgCSl4fJ#&z04?UhLiUm(s%(D3WgeC;8m*} zZ6nm+aJMD{z4{tGLfZ069%U35r2gmqQZ-DSAnmul7N<6@=ufanMyK~%3x>w(MVpE6 zS-?g}aXb$>zu%o`J^hv!95{=&<}+LFG5%T;xqRwsrs(BQgBxoWBkuP&%9BF3%Y4ft zA@0xPUv`fNk3Sk$UbWI)gTa-Us!aAGWqiAMNPG$0yRzf0niWmOLNMaET9tm6I8dfz z?x@kifNBt2+V5FlBrF6MmNoD4{-^Oy%9`ZbA*3wcF=@190S5BfRE;~CL z$AcaVY_D9pfWKT#sB#o9s1@gmXDl)*V?&%~i{%h3`%W73J!2b*e0lhms3A^R=hLae z&>O)JcWIcO_atOzOv5n5w;f`*c0zpfiTA|c1y2R(ecAXD0gH^jc9k_^k*_c|XL zxf@9sn?&wi{VsP~fqd;luG-#kHUpFHoW!fZiWI_z{;bO5aaBjozej_^`c~xjh(Sgd2j*_ zGP~N(G!fINNUN9mNKb?UV`XRMk0@{Q70)WG;yjX z6b`)hDYBcAeDpITpbi*I<7JnZf&6_n@Y zFR<>qu?808$zrL<1zJ&osi(`_y& zgWHMLMRBR{s}j|*gCM&4Ya-d+(M4nd@f+GuX?XP_KAO$ zKHmNc-;pZ@d6dkXGwc`L-xI(NP#2zix|IgipMh2}wuLzT3SwN+o%-QcQ9JqNn!q9K z9gioOlgHt#tEPA;+E}tDH0>~!CQTiC;bCE9nl(v4bA7esjkNNZW0+(*9z4gI8V1997gKW0r??TQgTw+}L_; zz7$U7o>)KLFJklRFIs@4S`%j-TB~Hv+`yl?OUJm$4u!H`TpFHHCFh2Q3srZe`J7oR z@kdS|EsMok)q zG;+02e8WEZ8kKJ zg-Nn|nG-+P_H|r;{XL86$~6 z=eFv*U`Tuqvt|u|n&C@fA$^>ukDkZ2@G?>nOn&{GBShrv+PXloaDm)_x3(@3>kQ`X z`B|uY{!W3uI(85Ek4WikH?X+)2YQ<;8nms{7*8{Di_{2_Bptw0uH%mPs0Uag>q=|Wuk+im4APi-@H znfYj6oGhoRyzG=wYhp&5&vp(TR+;naxg2hg%O@--r6LQKX&TG_X$Gbo0h{V24%u&qrJaaYAdr6?;p& zJjKj(RqtX926ZXrB1$X9X$3cEOPCTPB}MF=W@?S~NzQ+o3vc93@yTvqNd?1Gc!&{T z91*?9$5s_axpHu`Q|yGmwn&L8y7`xRRa!<2#5CWz1{nPofHBehpa%=G{I#-X!ZDIz zv)Yx13s*mUD(o&TOpANM4`0*c0V!V~uEN(5dF2xkB95hK?0{sG(mhfX0DslC05!{E zLsT6n-D>^xgPV8@q_tgXN#Qm}13>y`R_W_kE=TYeWpH#-4m4{9=#Gc;V&L|DXH7^J z1fk-OsYEEv2Pm8WaUK03zd)u_ON(=P{^?GFhb5LGZ>0%pkAqXv$cm&Y9&&0CO+FA7 zl1`FVOHL*Q8r*|f8`JNm-bSp_kykv;O=K5Mf*L8(T|4c6X9rNexxlOh5Anye!br=X9{VcI;x>DL6q%(xp35{}5}s}CNv^_FO@BQ9P@(C~p~N{P{4RI@UeSLBVKf76 z?Q|YHXM)kG_{VP%5$-{Iu`If&KqjJbr%pnYyb}{vl(BsiKt>BY)2U3niDk~%wOo3D zNl0mXp)aMv?17OH5~m=jS~k~*`NqNUM~3rPU>lMqE9l>3)H-0Ii=t^ZoE4j6T14RP zlelaMF^k9)z%sM87$)%CW6wRj*B2Va2hz6h5L?mJHU!By7K0O+Qx&Blk2 zxm~SZv^TKVo&+?gxf~})sbEovo)#od%E8dgBFT!i zR3I0vS9EP3q=DE4A3WVI{Tq<>msJF*{KJ&5c-bhLmRMq(xaB4>>^&XNQ4MO&(KJUheAgJ0dE{swi8R z+ScF-v_`hH(p4(_&S^GSK|dQc@$D~#-&^+&FF?^UbQ^#+{Kt8?^~b_@;d*+SLSG)VWnjx009|2i6rC;Pd}IxM0;c+^RdJ0zUH|>0 zapRN4@9ItN`>CIvhgRaNGgL-L#zse}|L;7nM27Yct|D?E2C_<(gc=Z2hkNA{qP&{~`N^BT#@UpL>q6^lL}M}H)H z__@WsW1ZHHoz@&+2-{fag4W_sRYo_-lTkMsl8v4BH4D3I2hAAq0mquZ!FQnL9^pTA zp1+eA-+2V$aBHl>|5NFJ=?D*w!+S&FNy*v69m@z4BfXN@Ppu3NUY+ zlE|_l+g}fcSLdNT&!@Nt6{2%z6tulm>Oy_1wH{*J^V3#%>$C66IiE2DIdah)sdvbq zCdaJo37Lw0hiLS3-JO?IPK1D$O z@9%jCc5ya?!iL+gk8746 zNh*K=mwvOsT2Rs$*q)*0a^65tE;bx9*4uD%#M&t-Rc|fVxLm1KWbZ=$%16J9!WQ-D z!^3`6nM#AaqKC*yaMSlr(kr!_DH~H`@W|m5QSlQtKR1Ju4q?$D-rk|e)oW{GjL)zC zf~MpV@1UuCq9X$MmfzMrs|H7N=Kom?WfmR~Ie}@>UP8=9@`=!>>>{bbYbGi|Ms1gE zdpsl}TL&EE%`k?$8pLp{m&S(Qq+h5rteDXTT*OX0cN62YI|JSbI%79??0=k-Ze=Jm z8<{U1YW3OVDVgDodluA0MwC@KpL7wUhSkeCUD$XaHYC64lEIj&!gt%~f_Ep)*cn7R z?dOfen8wxh2U=EsL2Do6otz6hHZTX4$9Mh_)>AB1HmnFtg&jN^iSh#w_b*o(BvyE>r+5YEL%J-qK3X@{6=dC3|(QnyNd#_%# zMjR!6K4TN%r_XJ*6Hju|R#fD(jP*pc$4G1@UxtvQJP<)(U?Yel5yGu)7(q~+GD$oX z%HQ!2dZ_VBe_(5OcOk!H8EO20b#gQ|cNuf9yD%$P^7P|Yn{reu2eFoK|7UBBCenqd z(if5O;!X~AZotd9Y5qmE-rFZ(1>{H0af&gL7H|KzsxqsHcHJgDY+-F8-UuK<3>1p~Q6fY*C1RP1akvd^{?0Ik3ub=$3x`CQ+g2@mplEW` zLf;Q+;HG4}X1oUBQ>e##BN{CkJD)aF%V|f1m;E}=0~Ky|Qk5S`KY}&>$^VEx1o(oS zji1)B$$kB=aBBh_@3r6Q0?u=2@f^W70Z#!>f`S&VY!*rur4LX(mD2(HHxzTP*_bcz zvcVSTwHnli!j=BxEh!*RGArRY{xv*Zw7wiezNQj^34i{s?7{WZ5E8~ zz^Yay*_b)NLrlBHO#TXn^hm3{va~zszT*J4(#1xr`PzXV^~P%TeG5T^#)i;sJoAnB z+D%o)XO?G#xeaNaF0SWk7Jh^TQ)7L$8UFFK+!df+I}$)|Ezi6*>mh^_naV@+5T9D; zKU}zlHr8`>HKE`Y_XFYj1!B)@JLq#RGoPt75c(1#Hj*loC}4n6enGA2a>?u{asC1` z`DaZZVhkkpY6$wXL(yeAOngB>n30fimkSl#n=h_!wU$TiX(MibL#zc1{?cwJ9Olm6 zXm0P#Si+IL~{1eS%#S%}U6*7{7joExoQ?@?wSO5_<_}={Bn2m4ET5 z_~kNk?}8Rvlv%|&%*F|5?)_f>#1o}n`NujL{^Y#=DPyE05WM%no7TnZ=7HLgub4y) zrzRRNy&6O`Ga1r6DG@eoY_|(ZiT>pvCNF6D^oY6bhK@I^)&cxCtpx{@KIV+n+jdpL zh8BdHCA*-lPBvuSDM%6o*?DHc#(CiUDb?ue2s*w`E;GBn7ZoptXZBX%RH~bAkrcCo zEG+!`vJYYXmUW#Iibc5}-suifFup+Ujretbe6^QJpTWY?Dr+CA{y%L@E$TZR z;vxq#tlD@Ukqay*lEU^N2c!fP@ccR;DZ44cA^(JN0#EdU?SSa)uy(wtLBw>F4S(I# zeYnv!CrH8eD+Yzwq{<>?UKrP!J#d1_edaa9!9KMT3Z-b_#c?A8RVejn=_~ABY03dKw8gXd=2Ifb9pOeAiBJ}8TA16AzKLK&q#t@Mlf+Nq4k*K6e z^`*SzAO0|7I&Ap&9b*Ou<3*d?_;F^{;|-{tk-$dVz1W9R1$RV>q? zQ!@uaFPAJR{s?>TokZ3V7_E%B+Q|-$+{5kPRV5KBH9+0Mp39kd=Y4FVlcbmo2!B!f zbX!|7zkbrjS+Txi>X2J{Uieo?@yuMc3g^_g zUX~T~#jE=JIR4F`NP)pfL3KEAt$>>L54}T0Y1KGbVFh6Lnd^DQP69Vi2T-v)6?m29RbC*bOZjF;?J;th%o(w+~Fr5`)R8 zm{#^H4A}m18hK}4_8wBvF@b6fw!mzpMWyfix1?wH@%WzXvf&`{S(g7bJ?v2dhat#I zvTpr!GXUbw?}EH!q3^fPW>}y5Dj6tIyX0Z{soG;7K1{vhb2>XAXsDH9^}xvkdM?k4SypItT*bg{8vQoQx*aTR+-;V%blQp!AK8tDMbwI+T|=(EP!o8)gL`+$^vFa2^d&nnX1E;?Z+0QFcLZ` z++ZLOMoB42LBZ1o={%!%Fr3uicz|3Ev>0hG-*aXBl5*#)SmmRWhZI&moV1{iB}~4* zBrCs9C;$E4rSzZYo7BE^ghMb|ow8D&?e+M@5{shmNLU(KU(>D%= zFFfp}>-~8~Px}`3FjjM6?;Xv!+ImmT#~y<{3>(r^9P8Zu`h%U}=Qr9ptxGy*u_jH2 z>rf4-h8m>?BAWx7t4vF9t@~XVLU!oxiiX;WhA)VQsVy&R_eqsI`id1VhLgj7I7B@v zyhL1n6}{jsg-m=v@1prx0joNEX(PMe0G^QM;s1mmol7lmE?~=Ugc=njdfp0mdIKY9 zv#STGagg}n1n_CL4HBn>z6l=Cej*S;+Wx-&AG1`fTW$vE)(GsLb~2*wt}|@$5;k6_ z+D=J~IxhRXSZrLhKGnT4X?IWIyo9`GgM40)>jhWjN9W3wU@qMmgbkCAN8a-%GLJH$(pQBH?8%|3GL7(g7+Ot zdEudxzo^%yMULeKS&(%KVz_83gxkaa$JSSeMfGlNGjxbZOLq)8w6sdY0MZRZhm@3{#7HVg z*QS*i8l)wJp`-?o78I10P-$tut-o{5cfR*~FaCi)xURjQy`B~KeXq3yz~_ZHP5eaB zs{qu$8XKUi#`blGmA}99wROyb4W2#%8C~NxI2rbJvO2s=Qjd$T8PLmhG9Nz<=nltc z<%h`ASac4F@eSPcYLdGgp=7HIu5Fq z4AS@A0GkR|rV-)b4Ba$@z8dC1BFo5{+WnHRm|_Zn5H^FLEA_X^ie`QQ$T;CyT>J(Ol=o=CrmL{O)sd2knLz>5aSfmtj!Az&uSkRR>-hnmA>tMCy?bfO!`hI zA;MFidc#Q^p~?@0Mhm=If6T2G(5){jVana}DUI`8uBmP(Cyv=^Fm=6SR6Y^$2K@|T zSa|0HIC|84u&2ulBEqI*>;`mGnHhsB%bz%4$M}X%Oe%m^rq!2qD$imK=ZOD=4S#L9 z$c-TG1LwbB{~)%x4TDr3Y-_wfP>VXRe}A6#HtwU(JMT2It{rs4`1)b`Lu@CHPV@0E zG>H7o!%`Dm_}dG+8gHtcW1c@&@XzfB9f4RC{^2_6M>riGkT9T2Xsfz0D1>Wni~q!K zENYMIYl%*hww+FuXJ{5`?&Ir7L%99HA;DlulYzs;irO{+!81S&`jGxHU@VlN1^vgD z3N~)GYGU!1NQl_+I>cAR3+oUL&6LLbZAS?+F`6`SJ#h|WLRT3C{HVrq>O0_uY_yt+ zuf3<~6s>%OQ%FF))Zl^JQ4 z#zp#?#xyCP4JO|Nu zK}+VK;tA zYFcT+*ZP(K7%+M?*HjUF!eiu98^ngU6}_EE)8e!x z)*RzCYt=^%6x&4f3joODJ?V1_TbPlQaCiA@8crk3DS77-&utmhme@^%CcB^oyQ3T3xJ)JtTypgnEUjfay&eK^3D!|YLcB@86oa=OJ*NxNWZd589cVGvVd_sBOJ3`Uza9<^;0M?khk`LfgaHd#(QZ}SlHR={;IBG z$mQwbIEIVyiyXo;)Jw*848)a4`??z<%Yw7u{D6nU$l`EpEax?|zRcFP5EH^I8<=!8DVI77i)_ zzb74)Di(tK&CPmhr5+C`)jw_c#&0yM)MtejH=rhL`|2_T!MyFfY+!GC6O;BjP6;B0 zavF_Pm`M5gHKKuPMqQe}&ggK}w&QKVc&6BZ9&$_0QTnUi9v$_J)p!0ZaNy~k0~13D zr0dUwhEq!I_v)|oKu32_ROgk~bT5Za3H>tl*2~{@%T&AZlJ2*i&})b{%OMDKE|ubM z2Rsh$0V%J81{G}>4!5FLI9#?IxTYYv3O^=8bWE zB%zddn9!W{}}dOC&$c=w49&uFtxqk5INiiN~yej zEHR`LFrZ8%vU$8h=i~A$x-vC3b11#<_$)2FzSr)`uf|+>?M z)ddaWM<)G&DiTb&rpLccYxby!z&Hw0T9OqI*^Ax3-@mbmksqpLDN_<>36JWV>FL3m zx^zLW$_*Rw6R+7`WwlxPw3@z`Zrn8we1%d3J)b0F2iY$|;HI^xolo#5Boq%LSV<_x zt};sw)G&`GfO@^%?v?AdL4|shTz>~5cY`jh+3LUg?6X{r77y*d2F*y zg5()A+$|&AR$aNjeVz>4X{M9Rq&w}22wuVMmV>_=I@l=@F2QIFn{hHJReJks^bWtX4M zOq=M{79cXzu*_%_!#5zHF#Yd1{DS~&%%OUoc!P)W3n=s&5rii%PJal#ay`3nAVMes@*N9F7$@-@b9F?q7TFF-& zrGsGX5Ju2!=7wW>>8LR0%L>=wiiw-fgp&H{2WHQBS3YW)Mx~P9-dS(3<3+428zvjS zR`_^F@sk?f-i>Z!X!zm!m9PJmE!x$P$eO|VBBoRlVSIB$TEzp`k&klb?O+CVUtcon zP&|114i-=$3d4mZTO{6prtXG(r1I!(V=StwV`J1l%0@pavSM=Ga=Y=iZ?Nr7?9U2b zvYhsM$kY>6YAT^|+wq5inb0r}E3{t#kJ{c1wVSq(pa!a#gMC*sr0Z{&-5r(+v8$rD zx2G54+<4Y}wfnhE$3+o7u#bBRFvRf6NI_4y_Om;k>tO8?72Zm>!p>m+jBZ-kst^8= z*r`V+F|P{2-M#8O;O#gm*unWIwUgVC1w>VHl$m_}jr@B!$;?>cRo$PTss*+Rf*Tb* zpCZp9!P4R|pBj^U<;jhgT4-Fx8lXN?VRt01L9Tz-m%>=@NQSdhBMO_FuBDI2_)>W= zC~Mjr-JdBaWe{|(C-90wQ}mwjjG>#S&_~o-W9ak8hDO6cHA0i-;oUT@vRRDX2{;5q z8E4}~3ayfytLS^ki*HS@9%#mMDh@hV96R?v zQl6(FqN_>}oundX%T#9gmfn~)WO|}u1o=I3XSOj(1Wu}A zDLy?L(oCu&-3KCOZ6ow>Gutr1=c(@T@d5tHyR8)$Ig4`ouan3u*##+ZTZpeJsOiq@ znSN=wOEt}ShBS-Chu>Pzk9vT%lx*9e!?cyLbTOAZNN@J--J25AV4_zT?LT6=?PcBC zkH-L8bWY2Bm_~d=r2b~}^l0(#DdstjKRNvzdP5{;e^U z?S!R)QX}VopWsV;xJB1xzp)q(`38)?u)6DIW=0?X=1V=vr(|XK8!CpbROG&Xb8N^1 zPm<*65E4p_0WUKtmJ<-SdpLw_O)(WAMtMHVdgCyne7F1aSvR?Wd?`53woS&DIwzB` zB2tJ8H?uN-h%9Tcvq%y+7&%#Lwul(w2EFSq9H1XuxHVRY7|Yb=dbJ`tl~}aB5O22&@br{*z8{WbVyr2cxv^eCe2K z8**!Rtd#(u5oACMm?`Sg!xmZMLqbHiW^W?G%RRUp4ffd$QJ%1WUrtr*jq#eM@qzi= zP%M}>3}MZ*E+lMWBaE=NKI!~Uj~Ufo-KSh;-ysBqgFF1r)2cY{lfXFEv`P>^c;9@U zV1lK38lgg?nld#iz=fvutTx+{Tw!2Kn|S}qd{lN*s1Bi0D5qk^L& zFKtU;wU1sI%U~+4Db>jxTcBJC&zS4$E;V=&Pv`-B%s6bxr@XC-sQgLfOEdjFKN=-I z|CPo-r?xC!H2k$dfHfw)kQ#2bX)-zSX38bgivVVzmLE>!pK(_2a*w@0FROwoQEbzB zv)}wCi)#P#Fs>4{VU*xP^;}z~m}w~~a3aR1zD!Z?g2!*X+4R=ctC$1~2#gx-X4L+s zAM3lVu536lA)o$)gm2E`YFcl=%^p{Ght)Fb%nJTp+4yRP?~dme>?+Gqh}^nMAS`pQ zx!tAZCj_SUeF6s$vrL7&6h+RvwFvLuOX##k2SXvg{Maz1PY4|Yn2GVO5LfenZXo$a zF4qHdo%${0PcqHSHX;-}l?zX-oF8VQ(TUs42{CBB?1-WOTQ(fgm+VOCO)S(Wtw$B% zLB*3ab=RIkp~Ty>cH~^5ubzK?gz2rA-ha_oW6ngX?%#ya#^ELngFBRCiPcaFjt~L^ zhZL#$tzzj$P%pbL!t+%UU!UbNCQH3EhV{p--&s^k3*X8ab-%p_Rl{+DlX!qB5}lZb zwedrmj%bmAlcC~U881sdh`zJ*`~0MG1G?6&1XdNBbS1@>#9r&$qIWu4l7$d5?e;+o zTfY%{Qx!$mgKLHs|AdD64{a6Q7>i#N{Ib6%zABHsQ!F>*|98Q#F`kO$Yu=EeBPn~- z*e6VR18BLv+vSLkROR}@-}CwCA?!<}!(iv$)?`yMHM8Ouu5cm67s|@u!QrK7>^|S} zofZoSivG`6C2}H|y3LL2vZ%2|JSM~el&F(KeIG5H!MU7h?f9((G&paV3_ZTwM zE>CddqKTmY>*tIDYTaM!`#`3C`bMyBZJ-H)T0>QS!W z`5?Y6in~9b+|QExxIkSh`S@`j$=8qu#a;i!!x^*oc5y0e^@d0sr;nzB`_1d|t~3)O zJ1w(ssRDuwYL#nT`Mq0zb#B$Px5|z=J$*tbRz(7gj=h;9Vu9_3m@GY2M47T=VX5W%F(?m(K{9L&V!6(w_v|P0b#ke2DlPLKmy>`iQaw8-x;%ezh~!J z7NOW#ov1zagCD%Jevib6=<3T5+^@BX&M#SQ_-M_JlC0*q0IW;|$Icw$TK4b9f&jh= z)w-T-f2`gqSz*HcBdeP{PApdaoyQ*rlIXbfQo74Hxn>9s;mb5Z<%PV)uQF~=h8o^i z*UWpM^vj4nA!V*inRUfB*VNdc(kH5PXVTUI?cIYhH-2tzQM7BnIDGj*@tcE= z^aTb_Y7!6r`0?~gWnUF+-x-5UU?Y~buKma3Hf2(~O}Orf(0(%wCpj8@Ne2VroIhv| z2OfqMaqYu}-bF5x5qYtP(7Y*1G|Dc@;7_t5L=IaM`aP?~zuAAGKoht?*DUX!!O3!Y zc^_*5oMiRYbQDtK^xU3LF-PmN&#J<#=rzyIfiVru-Q%+ABw7EZhxhaaG&x@<7#+LK zG0b+ZE9B4oh$g)q{Do=xqBDE5C;hST{+^0hpx6mVJJO;zc{Fs{Y1pbihI^a{LkFzB zMhz0D7L2;!87!|?YJ_MS8~0c`cu*TCIkI=)8)&AKS((dgzTwi)Z$TH^YY=X6@^6Wf z%ER=~Y4de^jv-dpBz~?+_PG^JkU*L~ z?~nZkn=&^DO1Zh{eu$zZ2SO6@j7~babh=!&VwD3@n^V!cA-41+dc(5j!L(uWs;%D5 zY?~WcsNntup;z?Gcl_998`K@2a5_h6wE-Mr?m&}U2o}rG`Hz!Vsrr+`I zb46|?%rKhOC~GToiWv|5`KlXQ7e45A8WFEBsCVme=R+JP+UQ2e6w@p=W)RZLorL2o z=y1fcJA2pOe$Nb!gQ>eeSoOpu64n0+CugCB6WnmA>Z>n*e__VkE!8owwkFd z|AGsdIOey4UHO55VNeJy-rvT2FC_)PId@B6M6Hm%|3x%jQZcw9ZH7mJGl%6Vc@ zz3&*2bR#%>rsJnBL&jQDPCg<<>8E#|$x9;G_pwIvq)ixbrU&UUt@W)%W>`}KeY31g z!!pF~+C$rUD;+97s(a4u&bfoZx^1G<&2tiVx$Ox$$=B`n`_?zPuPw!puCym@$nr4p zlhG&RpDiBCY~6_yQe6A@vHzflDK7CL63(rLRB&*BlokA|gthW!9AIVD+F6PU*8i&7 z>C4RQg0c~Z#HCQa0y(Vx+B7BXiIu4~!IJ9+!}5H@&Cho~Srrlg^3ky3uv`cPHy?NW ze0<>%?8OhHRLZXC2+1&IecdK~h_j(m%0>IdQ>Or-6&m`YxA8t!14-}kx)AMnMCtQ6}ih}#M<^YOYDd|%M+puZJ{ zruwDd;|cS7K>*B(@x4#$)>(Rx*O2@+GsbthT`5kHfFVCV)zY)Z*yGjHq|vcY&}bH6)4Ek zq7uq6k7rz&o}z*~3zSF?PpC#`^L}cMH#~Y8m&Cu*5_u#P(|>~v+Sp3fk-H=;@o8_# z04A8q5@t#Qi_tO``jJL-1fvbi`;8F(W@fo^t&FLtIe>?zEj%sn*L?dhOvfvMpc1FghQNJ9Qope>Q8AxLMJCT3F2ULH#set??wx7 z4%SqsrtaqR^+YNF`9tHBe=isb42OQhs6#r@H>yG^F%g-7zd+ z%oHd6TPt3iM}-mkSB*ketXLl7V#(L_!Abd)qsx)?Izg?gLn=7po^LM+ zLeh2QZbIPIa|r~i#%opNNT}{l_URS!&t(5PWzUr0R%r_s%qGaMkci~nFdGF(NM0yA zuwaT;S@S*HQcdV-Sv;OeFy|uNl$sNCE)D%R7UlFjQGwD0<(XZxIID$ zxVjw6U~b1>0@2K>AyG`C8UWSrX8cz5k&8vcQIOU0gWr%S1uU~Xo&`~lecM9@{xPXGxgtW`!4Ba zCvI%+8dV?8A3G)c$8{S=8-X8)H!|LjjBW|lkK16XY|M`{`P3xIGuY!2>e6^CXS7lo z9%duB)Az}IoG3_Obs9t~TqT3C3cwDZn(;ChjqP71EfW|1f;EOc7K8hkSqPE;c_7VY z&6kDObK57c-r^?6qk>#m%mqGNO66o#xa-pIifX=j%t-=CR1!Kr;V9MN<;aVeyhz3g zv`4cB-OQbvU=?r0pJJ}eYAPyq)MGrKl%}6F5c8IXK1ooi?7Y;^TWM)J0=Hk;zEc1^ z=)G5PDa4=Ji%Yo0$eHFk!#-POi$9dYhFR=A^K1SNF>g-q*uiX#?Z1LdX(q+%!>nlM zUvMk(*MzseT1Yqb&LY%}!c=GIBSn&qzC~s~lcavqQm? zN>qWLOZv1JL=MHDa;d`vz4o@YjM=D4ao`soaF%+}`tDS8-|<-sxR2uxQMp_R!QB1K z2y2Sp-E%?`DH9P4h?b-}OqLUaEZfa=0nOtmS`!@1>-WQ2Mgv=xq051r@CTqkri7%e zxp$(GD8qb#Mv@AanZ0KD=?5H;(Ck(&OnXW^|3*;e$0+zZ9}FB8>ERv+hkT|HXL7u@ zHOi|ceBGAnkuAJ>$kxbF3WBtmeZck_T+>WajTqi_ioG_0zU=} z_9dn|xmZu)8VBC#fMsgQ!krJ&@5mcKaNt1=i$eMnxA7zFrcKZwKSJxLKBbuwqX>X{ z>?vFLxRUY3rRhRRk@J|D;R_1XqjL@V9^668sK@#pX)amHZE3=dL*smbmk2TVw+QH| zen1Z=rP-G?f`177KNS9d3Yv#l8^VuC0YPM@)A;x3WnKVBDQN-;9Y}pLpCR|?O(mms zEivGu(bfK<0B5o60GsT(lS8V}g zK!E~C?t#-P>FPh=`GyZAK1$T)3G;5V<(5xnaotWec7Q@D`9+3S2f*4?iE`dwGs!4k zbEyqlKfE6ENAdv1ssAV-4!4o5;&iY~Phx4FJChT(E`4zCNlEYjnU#d9Jix28L+UeHW8Nksz z2PKWCW<&wq@JJ&Y3Pg&LyT0_$kJ}an#dw(jT|2RpqUJj||74?pgLc9${Z{7BHzX+W zv-zhWo9B~5@sH%)wpVHSqd$YWgm6MeaXfxq!mAQ{PxC}1@la1 zsX(FoIZ%^2Gu{{U2B*zZXDTjth8O6>4SMZnkO?QQ`t!*mYPvr!EEwi#7E)noD~Jz+ zfcUCsaO7~hGFoHSQEO8$`*Ih^=iKspfHtI29OCSZ?)WHv31KmVCt39?06)>n#w+VA zWw?iV(C#%52mPxj`OnK)V8hjVF8fEatlC5n*fiEBF*tJ@1L0CcLhn4g^760&t*<;& z`sraf&aBYdJ}bI9$|ytRlxW>@?|H09OGc1i8jM0F)>2(&-a z*Y~nfTtnwxGm^d49@;PlbxW|$8yd;}lMa;NcN$KL=k3ky?K8zo%?QkDG*3hP(ux9N z1vI)W$V`_b!6>q(#wp)!emTQ6t6dbeS3F9@5?duwP~g`ZG7judLhZh zS1EnB(2c@Br6-*gy<3C6h|@J6^UjIj>q}F=xP%|ru6|Rqic5xKD5v^)mz&*Ub-sS)CFZf8h z`HHpK&=D{Hc1n2_i#(m3rKBQR*Lvd?v7mC3cFPVg>0flxfzxkRCYqe`U(J8t)d_vG zw7!_5c#6)|GGJN8(MVp@m#)vTj@>=FVK%a^HfwcmrD<51q8`VhnH)83TDdow=Ah32 z*GL;`oFb&u5;1Xvpf2m3UWVRnx3nm=1|Eci&yOJG{=;a2#1E*M`WN|Q%>e7QNDsbT zrwJSXV!rY=abWL)_HFr>t__~hD=IU7B}qP&Sym_Yf|PWRYRjIM^dt(5GJDUev#qCE z3oly+=SRPg5JNG_?>srE0H9v!#U(pw=uLIBt zZP?kI$$y2g7o(a^2fvoT26iCNe0Kg@^&$g)JIIuyW-ZS~?n#_BN-#^mx1`!;C1<7> zFPqmU{8`li;BwFqL(h#G^z1smKnzpOXSu<|M98J1rSaj{$Laq!qWFs1iCX&vuM(r6 z-G&~T+?v36Da46gPD!!@o01wM&I=&8+wHk}Dx$ffW!OYA8nv=mNNiyD0wh=wokap> z9tV_#2!$-8Zn-pM&>(C8In>qpU!N?RZDX88rDAs=vBf-isC%DPJ5)Kg$|Ry3>u@Fq z))`E0chK-I<#|A|mFWi^MWUU`Y6Si90Rd@NKwL`WOewBl*e$U+_5QTyMAfjm8@{C3 zX-T8w`Npu8U)VzRx~TR+;zrCXB-DCMv~CYRzd-QvOX!th9?Z1*Xt}qvy}!CE?9?0c zC*|qx?z(#*2GU|*L*9b<+*bs88rgdGZYxq+iPY(XYJf$NqjCWS6^xP`T0f1SDr!IF zz*c#!EtBG8?h4W}*_YgUzS*}R@>7IjWe6RuWUj_W4)|b|yx1dg z+qXr|<8}s9kNBg^=J`y@Z)pn|^Qkv?$v=JnpQreJpYA_mthAwV8j0>3z$I@_@V;Bh zK;l?3S1QYZI?a%N`ylsyc&NDH!u^s-n7_YMwt0HeYrfVI)!9F~<{9U5}-5_gJNyM5o}e-S#;Lf}w@ zrzIXzXZjmU}(!oTI#LNy874<4cfETo3eUYZ_@dVAdnVWapC@3Yg&I$HJiI^3? z9j-pPd=M?rI(R=P#iL7msF5Bs51}l7bDrotzNAL>>tbkVMG$-A3l5S_Q9*i{{%=$S z^`gINhyNo?bkqks?rdXsQO4l1#E9}e(PYB$M3uH1$7fknfv&a==Cqceh^}I0)M)XI zL;4NJIxMl)X1CQ6W7w}(+C#GX!b)zi-+}Q|^B7KaB;(8zxzLn{gbSFNwck+cUx%A( zcc12tbz1{^;`l7|_2+)<1u|E%_g8G?7RV zWQst`rkg783`b^gTOV!*-?l2;5uVbR>2^3~C1Qe+y<(?a8<}Y`vH%?rF}#x8iM<&A za7Y}D(OWEGZL;LnM>j%zf6q4r%Uxg3e~HR~>NQ}yHjG~rJD3EYRNd$eMUsVaU}1XO ze-+&A>N{;!bKaG{FIPOM>pc@AQ#Hzx0!e+A z@_#+Rp9FI?;J+AP%;tljRtkK8#2gJ*K7wW)z6?o`F*4>r9RU6K0H^K!REo=+d73xD zq^BI1TqDqg{eqJ^m4y>=0aMf%_+Snpx(bSpEE~{@y$Y?QL+6(EjxU*PQM~M8K+BnM z{N4ZvvX)uzNz%i<ri$7@c^HxD(ky@vN zR9U}diZ9Gg+)yA4Z&CkXQE^NxPI`Z(R_!=s|GO7GEHNtdY`Mexn)!~N`G~?r)2Z{L z|2e^7gIMujVmVU4TCFPW(sGTh_tY5j5 zRHMd-an&oGoi{%yO#S~&1pnB7!l#Pv&!5Ga70HF_yBX(cX{;3zap49?IyM_~WR{La zo;uvz{ew!zrG58(;|hSF{)PdSZB46{<}5si!d-s_3g|jR;6})DKDvjkWukCWAl_zT zP3_iF>DvziDx%wpeZK`~Dpn$LvEgjIp=B?M4o{Z})SoYYycN4PIxNGx7%aY4nnECIzg89qZ7@kOe~v z!4qq{7PXat67l!sa`>{0NJwV&$&3{L#;2p~WJY$8r6DvSl$>yc)@%Ek+PJVU%LQ|> z!e7G2$4_ST9U_|R^r~Tah|b^Nd%akeQq_5h$Ziq_=9)NW6i^|q`ARfz>*QOeYSbVW z4wtXIAhh#csq*eybHPN>vc@kON*>Ufkpg=^7#9kkN16%rUlIt6F@d)I-}%g6+inJ3 zjDD@B5XWC67X!oktdy4=78B#$k z0CpAq_I&OX%2(8;U`W~uQTiWw&z~gdGdALyp?FX~Cnbq$-i!)EZO;xauOT6-C2(T@ zyrxELLBBNZ2sAfpfYeBGl$_Z?iaZv?mzqyx&Ocf}vgb2LX2JJYal46%*B6|4Q@Y9V zx|+!$C)Ry2U1@cn2+6G>TEY@;?1;`5@1vH$`4Pk8b3|eKc#O^ZTj`zq8YPre@U>65 zxR?~{KJwwplcfKV8~sU?TEMmyc&KC%H9RV__WnhhdnGs5Cs7b$%Hn+JV@GLA>_l&o z#u=YjRpe~GRmbUE7`0n5oT{xKLjkp0;8qMs*tLE|wqcORE<3cwo*v#ZuRH?crf*!LIi=1}f6#BP4FY zCR75bMIF8jg$=Gd>uwHuR?@>&G7$&9BOaQ(msIm_Gua=mqq3>ypS+{Tg-bsX;g*dv ztXf|AiX)yA4&{^jBFq=_sY8(yl+1(e82_QE=dgfL&lZ0Z`?x(Z+ueeaf>*Fa3IUEMHl6wON?c-7ffTJh3h5L6jf>m%XXz7tUqP zT}QG(AJTiS^#I0Y6C3s?FjB#4YY^KNBXFHx0x#BUuz<;zo4D3}=y9W*OT#zl`pa?z zJX8mFU+^4xHb>UuZM?h7vju{nRB{n+Y8T*60X|^AtGnruboBr43NW=0So{3$Y0dji zjMLAQ*!uaCqg4EP_d}WuH|K`OGMx$5lAl@=gP323(`~UoY)iv+gV_YWl!gddbyK8a zBApLXjdc|C`^7!98fSDxU1ic;T3EjS!U=Xg<~55ThJ*cjKbZV_#EH3&yMx`1p;;Q; zek+>98(+5W-g{gupafmJn7R|tbcs9r=3;F>)f&ghCTM6a@wDb>2AP1;;q?{N(z?M0BN(jUa{iDzPqI_^i+U z`Y@^=db;bisdnEvZBffDTsQ_*U{QQsAe>JH+Be-?B!-m8Cw;QJjg0Us+Xk7R{LO=`@oqW@R zpn_5w5)@xzso`TR3}7wd)}CW4>=&O6V0Bo&*fv4{~4bXU?a5S^MpKXipGo& zCHG(vMuyflPF;VdC!!KA09+=;39xQRLID}2lOcaBu>fi`?(waaT266IlL2P_Mc0ZYt3q|A(hZiF_nsJ8}l&x=yQR_TB};X5It|9EVB&mV># zKA9{vmvY}eO?w>sFI<%zXT$0}6V3*_8+lfZg-T~-0HtPoobXy=N;=>BDZAkuw%ozA zW|imqL_p86TF^CTY8to^Pv6=k*0pcg ze4#`LY06I%U`RES1&vo)HS4%5g|wdibN-Q`^QY(SJ+p44vwvWNKN&tV0YdWez`VQH zW0SU-#sG^0El^`#i7xrX5RiRBkhk=PfBZvh9@CwOVqJM$>xGun(s{a*NRxOH!220} z)eOtYZkpa%=$yjD6Wj6Wv^aNZ{tqKF{4JR;5VNAc5byd*vLWk)T3YQ_wqbKN01j}H z8o8enB8I;ha@3HSSV=an79&vt!C3h^>m45LwrEveuN6caQao6*4DM=r-g4ohi)fUe3JVOQQrqH+KwFN8^ zD~-<>%1O_XUrY__y>lWnk!dbn1nz_p#|pi^Yd~De_BZr}9~?r)83Pt? zf60^ESO%;&RZTL4tqD_xtO;K}t4-o3As8s+ZHNJ)oqt81M9WPMkf{n*APtoS{P!mV z2Fkp>OGp5mNB3!VxN~si?8{!oEdBbtpsD}8Dobd;BMzld>+Gg3{OIoEtUId4i@o|{ zJFnPD@3@plwRl7A?b=D6INU@m=<4Toerl$v6{eHo$iLEm99ete_rdH9K3p0;0D(Vd z_S({FU*axvULv+ye02tPlP&N2rb}{-M@Rsvny{wv#1bo+CmXp3hayCE6oU7gtUo->MQUKo&%n-Rfe!qEEJamv-PjXJ*0(Qx;<%| zW}uYJsCpw;YECN~m_rMrWqF@?!LIcOy!fNePP_$! z$O@o2Z<6OK!6uDL<8az(y!nj9lp2qoU8g)PBm>NFF&pYks**=b1Q3H!@dumn+1qj? zw;wq!i9UKsLfQ~AFs)AZ{H#j}2=J&b7 zqiQr@!XcIT{t}1$=;0yhk|3C8{~?~#uz+@^?nktFIt0}3c3;iS&mia17$M=+3Y4Y3KeFKS6Re2o=st=HD(76!NGJs(WAdJs&fAF{;<#0vY@>?T+R zX{@YQcZg9Qx;%j>Zp)CEz@4rt`JN@(NsRAx+Kr?iwQeyk?e0IXxJ4-w| z#dh(=d~=G|)>OVZ5j0=9-rJ~ghz$OrFUM#KJ4dp&=G#+WKbI?};4wmgNunVlc2@3s zGUw=ndCi{mgYo+BH8qZo-xRXxG1b-DGv%C`-NCjMu!5rasuv!ZWNqd5;h%^o4IH*q z*dapovNtW*J%1F(7rOrHn)Wg&Cp^SzORE}EtW?kZNSu0(_>rXmG>4xZxq%CWai5e= z)1H?YUOG%`HJ#e8jSzsaiQ<9H=0AYn-&=D7&_m8ohrony-Ibwldom~@Xw~?ZWV5Mh zSE@PYgL4YJ%v7f#=~-Uqg&m17lMA<7@5L>&d)xMEAZ&J1V#fBA*>vNs*5k~haxMYu z8)Y<)styf=tA4zq=xv_9a6#{uyDu;^-hSG$V>LlXws%OAUt!!V&iJw&7igcq`&i$L zIc!T%5h&L6CypNc}Qn!FUT%}PapwqEptD_)m#M;j$_Q&$F zM1M9){kek2VVd-M(di+-Ww*HJoeVR&{Kx) zq)e@A91`q}8uQP$q;G9@`AMjEX=^lSJ*f_M>&=)#6;r2lZ;w)%wtXzw_cZlQ&1i4a z0p=!FT@315C91*IE$lmd06+iKQ(Fq{b3dqJRzshbQK-gQy`+hZYyGJ%#fb2PrtvFB z{fmnZ$>(P}n}gU6DRBZ&Kyj%hd}!YTl*Nn`oYfu*GtcM~b-IJCxc>mld%qQf9g*_^ zSPiZUcL(b`yx$`wn-}BLUu~P84~n%XR6msZI8?V~`GKTf=lM|LYxZZ>y;hrYcFQ84 zEfHC6#txMkt-3Rft3D1LkDkop(03o|yM1+WW5L{H{XCc-dJ6(nF98BJ{{HLV>K`(P z$yU0E7Lz%tK~U{}Z;0J&{IXMP=v~DYO9t=(bf$%qzj{57A)fo;^SpNFW&s+Zo|9F(?N)<|A8nhQM^*fe|`;Etl@z{KSJe7ZbsR~PJ7 zLltLq`w#2!hw(W5&B9EKU^nu=!Ce>-Ai{tM;L5hXZJb98C_ViFMYc|JN*V9=zg95m zaSebvfJec(k?)*0H6pW7$&9eR&C7@(1U#Oe>wTjZ#Ch#8pc>#(>mp7Hdsng(;=NFW zYceO^=69$E*%;Agg^#T9aH`B6p6**Zl#2IQ5^96nt5K5zM52#Vj6JIa`ox#&ZaQmL zq{VCQ*?fBdHcggg{wy9!#3ZH1!^%lfUF{+~HGFl&OR_nTj(Cq9mi{1?t2nc>$ zYQc7ES7$8mmUB=3najDW|44YVf%KZJ?4{>zDRnRYlaM-@lnzQZA?6J-s$`0&i@WdHs0`p?niW_nOdrSl(AFS^ zA)|QJuK*fSK>xD$gfVk=^FjK@xp>h<$At#T^1-R^%%X31e_BUkJB{-K-{+|@uk*VY z0`SEBT@#;zBZMSn%Ulwuq?$i<4(MXRu1ZF#JKS?l1ioa4Lpk9f!y&3NJD%*%iTVDG z9)q`7$=SCW0}q8iDv8RL)!0kcHV}rl5TJGqu)hnJ_+?r4GIM=BEP{tBhYC=jgnE>T zz&d`Wcufr(qxc77SYkq8+bm&LH&MuWQ|p>9xb#Z3T})`XjH@uG+k{xYH7{S@A*i)0 zb|SVDP{!Vv@X=eu9pz9x4^~3U#jZQc8&$3LU2&RJ*&J&ALAw4%5HFSPG)_^=dJpxc zd%7m=Im3scF=4xG0UUVhwO=PxOn+nlZFwH!>4khdynYQ$bpF4VUYOD2itC2U_R0oUWrf1>Jw9C zPwMi6)oRi?8pdUcDktZ@G*clm=s$;iyhIL>QQ7{0_Ix%Ls@IAAD};MY2wqGnf$k0{ zB37toL<)lWurqRYAb81SlZK?r$)Aivn(2A-Ho+$ab^9v=zh0&WhtmBlbH~LLVo$CL z69Ns^e>sT1X)Gy@Q-P>p9Siq?sfB-28ghQ}_D8yxPfu=TN0~>|*{gpVj>bVn&u;{J zPaJ%0cC435SFFm`xad!*O|*Wt+ilyl<(*OPoL-WxR2mcG(3H$9jSCx%yfT9{l#&|* zJpny6AExE|xDuytY^E90xeRu;sqMyTY<_8T9k6;A0fnK^AF;Oezn>rrqB zz=52MiX=u+av6pK2QQ-hZ75u{Q!;4kWw)4Cp*z6kZ7#|STTNjz4g`fuEHj|rEiPIA zm95gsp`fRLJ6lDG4r)uPYAj69oqB92?6OQbS^Gjc_77h z@Plk&1j7n;)v+9Lbx(&dx=IB6-@hXfV2JJ5-Vs7{Z`u*Fl*T{Ljg2d#)qYKDEBK8;w8SJ&xW`@RUlWa;Ob@BPgbc23Sv5H4m z>csBkEH5K$o$JF5hq<2-uKC5K!W(WyZP@rE?Ycxj)w$SE}a?9K%Z-g3d5;lq66x zm~N#}NNh+*5g&68JH|)f!<0jOZH@S$Z-3DfZ6SV71>W*653ATvZk7~cRC!fnu$%-<6kp(D zROKJLE69|aJWK}On(~Slemm-;dr$vYi}F|PRELA=i{1DBc%!_VQ_LU;TSVR9Z0144 z4Gp31%ic-Hb+u5s&3dUZidA$J?WqD4>|Qef?KyXwL4d36Yt?&j6Iu8bX>3Z;!&{@76${l~z?1tm z;as+wq^WMO?+RR zsomg~ksyp+CaBb&hOZ>;ed6A+!i22^nb)B|0gNj%o%(2g?&rWklw5wvSbV`UVO-ru zIt3oa1A$T>ecv(0*;`n#)LVxJbK$GDOgu&Pnr$aZScflTVZXGUJea$lN*SluyC_v# zmH`F+XQ+-s5hfVza_0e846)+C@M#U!j?Cl--Xd%lUgIZxTZZoBc6N}H6?eThoPIbu zitF}96}e~vHoR^xMV(j}y;t^l*paN_Gb<`x=LHwG6#$Jv!j zpp|1AZlSBnoFD%mvc5Z>>i7Ns*ehg5MrIxJAPFH#)M68*?YZElyPKM z2Nfd6$T~`9Rw^8u>}363>it>Y-^1gf`lG|^e%<$VJ@4y!KA#sicQj;63#W8i5dK@F z125kxmf~DjT=^^Od>i#PO3LCX?o3r54wc#8{MmTd-{N)QFXmY7PGrklSRkV#dC zL%J0cF(8xQPtaL6^OINV>gyZh85gLQe(3n+m{u=oTiPM(op*j4r7PB2UCtJ?1NA%qv9*DIW6R5)DuvMTMv zBeD%uXbBN^#pG6GJ*-bc<01jeX7VohJ0Aq9o)NTN@L`3=!x%I5<-??^s5SL+gab^= zLq&cXVRCRJTbIl-uWc$k3;>-O2haP^Kz9O;$ZxTm5rt%1g-JEm6=1B^++su>NDwoOWA1JV_g$Yv1t;oyPkhJ&YhDGahbC3p z!S`+7O{H*T66xk?VFve2E#fUYH+vNG7pKo%OSiNptu^=8_q_Xcy!jJ?1aan}O;wMb zK~<|Z8KC*LhuhwvzW-93pZeP5ZH&bL#@{qXS3`kJwPNc>SYPjjuLQ#Ao;Iz|2mTgT z*FO2H8d?7U#s{Qm?=JNoY?|O5bFzoe*wpmWru5}&1MLTu`s1#C(&1e7wpWw=LsX z1xt@LmU_h@QU^_q**6_KydAfKs6Mq0Iu5)6n9YJIxUzS{sNd>_sGl9J2!xYoPoI$t zJTtu%SELN|O0IL>+?SFP{)`A$d#hq1;%FRQA7VQKANQIw#=1O#)CjQ2D|9d=`fK4T zxOcmF)D;`ikE6B7>Bk*tI}rUmV&njdMC_|1Vo`+t`xFo&(lcA?VnH&OqJ5DJZBfZA zEzN>OhIc&-Zzwj`W842J4#$)G8{PL)xgFsjc?r5!%*~iUhJjM{7Mywn{5?hTRDki| z=xQp0(R6vIajLg+pqbbAa--4aa)@vR4Q^{B`^hm=aaZ$cGr2G2iZB?|eb|USJsI2< z!bp+Y@OmT^aCToJ`=D|n{si^efW5}C>R9L&K9byGe-*N=%hZw;lig2?pasgQ5z(Hh zaxvXd9O(J-u)rwN-=sNVAv%XYT-T(>De5)_%N@_IoBj6Rw$HYB>&r{NJU{PrAm>v| z!X5p^M5=&ocqdv^)nvMg3=nN(omU)<2Af!04Nqkl7v4N`t}8byeWaD@WMDnW2Q9l$ z_3?&`D_XzlML(Go{+!RgAp^RM@}4*Zph$lt=Geb!+%l;-9zS0m556W;)yT(hGqiJ` zX(x(c6g9=HpApjUe7$6IW|k`F&YqK@kFu2M!H|9I5()BY7$y(D}FayTf8;nMGy%h zr(3|D7wKF1GEJkjK+aft^cf>jX#r6P5BX{@2zNmgp4xY9DXzz3JUZ?+KRy5hBh#u* z@tUdgmv@U|zz`G%;d@vb4Z(T^yiA@8DoYS>vOyO!uYTUq{9t#{7+T5Vj3CxpX3FND{T%=3MO{W1yKv+y-_bTDm z2o>D9sMhxJUZrhaH)DtuNesDeu}+dc{UzY}^*ASU>IbRH(EJFh+Y)QZf~#Tnv<9aI zu|I|TX6Y8=xx^vrcA2+}63Xl?+;Tb7yi!OHuK0q}Scqf9PL~Qv*Bf+)Yp3pzusjKV zKH#aT&|PjxNLex0Nu`M9E3@J4QiSISHO4i;Kw`n#`)4e~#+}gy-B{5!V_Feuk&IK^ zi?rEk8iK{A|Js}v`zfq2my!J3S}P{pVo=C3zerR!IDi_dSgI5(KC?52d$mK7opb2l zUEI`DX)F~sWCBYD)!$;>hD+g24DUeW%VlY$@{ebu)4s=*6pAZVnnx7!=GHjZU3Yri zBMX~m`&nC^o;h{CQ5reY|EHb zb)AO0*XOwYovPkY1Fc7Op<`Zj{DIj=BQq^o(=SZsx;9?2o+{p-%nB$yqx2Fs!VgKn z5~JPThR0T`Xb(O?UDQj-fm6v5F8ks$cCrVSsZj9ReLT1{7Y;n=B~YW%8tib?iAHIc zib+iOsqB3;n2}gMsFXoy&6XJ(#_7-_U~Ies=j%^5N?-#8kOM5AzjLu&p)wV7mqFta zK+@v2kp8hHR11HH0#Y*zLF0J~(R|&}5Mm)`W%*fSc02S1D)XiMGRHcquZem(sZR=o zHLlpib1-bp9+x|Iw!tV6z6A1u;=^mAz?Q5ikjCFT-(i*d=)nh)mPz@l+o#o#f9;B( z$qgmd@Xj-UR8+Y0wUBDl28jxpAvrC2Pr$lo#fC z5Ezpt<)MEYwoD7t_~CwZ=3?ezy(6jB$pm3eVY4#-U5B7E{*av~?RTz#eU4qkd%DEsG%=6V0x*}wX8TG)+! z8Dl&g{v1D~r<2f@{9Ts>xL{XK%vNep(l*L=#O_>FRNK9fNU7?o{c>Mg68GG;Fc$1< z*ovj|S%gE$;ZxW@fbHI|km`q52j8h0+j=8zIto|{D|L|!q@UsI@qLQ^$U$i-cP)!zo*K=f-C2lRU6R7>X>>{9IX~IJ zrjY9Mn-+xh5{{qz-PtysX(lMR^9I=tP+^3>_EHZ1GgX6Q%+Jmp99yw9{YMatXgPFL zwAJaBVa&+~oGeQ`c+$Rtyd;6ehq%CKzu%8Ly!TTE^=uimpAMYULfhzUSC&g=T?7(z zu9_Am3_t2;N9^vXwM2UL_7?E>u`?S8TdeXuAvttB%Vzf~jcIR{U&a^f!g~)-Mo25C zQa0wMbK=syAZ+zp9!(;mwt{=s^R7Ntt0loK^Mrx*=7&#Ro|a~AbQgaopvE^+DemzJ>4JWU7zNykP8DplgA}OxUs+YIuqe%yte8NI3^7nHbK@TGFRIq%(K)1F@`*R}sg$@k zRH$Ang2Lzsj}qbHx>NOZr32eEj}An=Ieuak47F;tYoFNsN?6G?=7Ovxev+h<{4)SG7BD6(Z*K9?cA zm*`FNkC`u?hPnxx-trQX`KHZdr|hP7=Oo6218fe7FTsTyJZ5*1pKU}^(&f>7W&|nPewmh7u^>RW%V9y@O;K?kdk|ZWhl^F1m)Nb{Mz83P zi}t`L`gBZRv9h>@JT-~6j;OyS2e=x&w=!FN_0wkpWH)aJru|iy>$V(TeIm7r=AC=H z^SY7*5qrnUu2f3&l^z*3P|;T~H4@uoDn?zj)nIUIrTvGJq4cGFI0?2MY<}MlgU^RMqh(0-3c;0#Q;LSC&-f ztgvS^I#i?tkoh!CbUU@5Hu?w8KM$g@B1GpWodpb~$dCF=eop>USWK3q;-Y)XoYke* zX%$K&LswZ|%~7h(C+*QLR;pr8>d5B8aEp|DEW*~?n&4)nALP5IWklBmw>-|%tY5!t zNQR!gzZFFIvhz^hxA3vgvp2J2j~)B+-r#*fXY!Q-KZX#af7)~0+_pOS$KM3bX9_H9 z1BS{v>>wSBVm)S;l{n_ArDwRrIKD_vOk1Sk&Mr})CGWM|JtbiD!i=B0%`aDf$w z=KYnUpM{0y$4c29s^fn*o)jBj^-VUTtDor_G+5sG&^~BVOr)#NJvguVEab?xeYrT8 z^eTJmnJqbCtMUiQUc#@w4Ar6UveG8D=n=dfSWlwnM=!~~erlL`x>^5YSv#Z8O3+J_ zl8*?6wOOOwDZHYBP(|Yz;^*4EBXyNDOy!y;`Mcn25PR%rO?EFQ?eB1xcs`TJ`dbz_ z6hGaA3wg+DH4>uh_pKr@X;N*KxeNB5Iv9eyn9di=FNTemu24k?bgI`GizxBrRNwhH z>)8=>w`_*v-75iKH1vi)^bh|m;*X~yNJ8s;0tN1HNy!Vc63kv)Yu?`O&D4r6peUsr z>;~#s2||t^j7o%e-^175Hc2Z}Sx{?dyBc*_3C8oq0#wT>f(KkysOhS81xeM3>Ui-# zu4B?*vt{yOd#LujHn3-Kfd20Q2O(O0PdQBY*j?WNEu2Bt$*m=C?T@+&ciuM*_!)5U~ z5U*Nmd*%QBc&Nse_St!ZRLuS)8_bb{SjaT9c)2L&!VChO&5)mUT zMmL93tc!W5cUH7@F3iIS^s7;C&rrcxwoKN*Lu&tMIsN@GPziK%lh%~Wlli0Dn?b8~ zhi^xT=MO9XA%gu)F;z&9xGzSDr}{RtL2%T&5P{-I;+m={6zEYgJ<6*cx3k}vOVR*t zKhl2a4^h@(=)cZ>vOnsBf!~dQ%AhE0nzNXRcMc;k`^Kl$ez4vTMS5OKnQkPW5>6V; zFRioxE2Ci(Jl?QvI?Eo;+=DJK-BgZknXmGA(=F~l@rTxM#6_TPo(yST1+^&`1w2t4 zthS(w>qlWZ)7;lWcP~}g+&NZxYGYKuY!N{d;}4O5Nwf=*c!Ab$v{p($JpD!SMub$G& z)fM3*8VizN?G)`){3bH4g*GQD;9^}LnS#qcD^Ou+d#Zuvd3F_J3Z!twD-uOcdLxbgqXkoi6yb!w)F7f|hFi7 z6v&IwN$@#9iVSK;WC=N{3&Lv;U(vyam&sfrV^vhc#Y-NQDcM!?VS=D1YT10aloA!N z_pn9t6SPAGzlidDrCVF^whHzm?DO*m{-xly`l}(4zO`!DRa)hHunCF<1IW(+w*9kC zu3pOrVhvr23H3Oc5)PsRUMRCNy>3?! znIj6AWl}ZxEJii5*iPGdaE1~)70fqV#(yqApbierE+d8h{g`BHgbm((+N}1K1q2R6 z;rO8ANq1u~?Wm6GrD}A`=!p`Oob}U29DxQhV)@7PsB9T|UwU`cGjdQ=v_mGd|Ctoj z+4)`vvF;x^#cd{Ax1cHSG4t8qy8kp1HOYXgsjS2BHJPceFQ>g3wgc|oteIH8lXQ9N z_~odNzLm?`T3pTVwqr?)FDtFU{o7|Iq|M}&+P+pd5Uw;Wo{4_loA|c86w}LRE%Dls z=QTR&-kmCI#Q|f4PrLexV=q(*-DApUN=XF{gBka!@^7!T_9?EPCAjhp^I7UR);K8B z{0gdY%|el3n~ak3in~0@xX1dyB>Ij2;D=};taJn^qFoKQlvMp~kOmJKs0Piu;VIIF zprY`$wT?}j{L5ed64ofS7S%!WE;B7bZOLRI4LK zR166*4;us@=B-Z10rn{)5EWkeD)QUPI;2NGAO5Vyf+1$1egE~$mtNhuz6$oH19Ma0?(>Q zg)GXw_S$_L2M&0ukXR)uFkr+TOV(S*LhjEPjc zI+|=V!D<9M6t?%Tm-rf;v2JmjD0OZb;@fj;j!eGs%g8X(0`3Gwc`rZwbgQNs-Eal$ zz~6?THEjoQ9#)iE8GWe%N{_s#bOQt~#>-XUUpq5=fBNzB1ti0^UDUU*y!OVQracc> zS|*D{34IVG*MIeZ=Nf25*zC$Bh07gch3&CC=PmB1DVq<2NBqkXrY%{#)tC)fP7$U9 z@3Xz1e8I^Dr{Vp7G=O_&fbf!lCsQTZZ&kkUC!ASEwYa%_axY;M8~b=4^k+r3^?EqD zrRJ)?2&s`1dMAU);ql3vdQ~Ejq%_6f^5jk^(8O9GI9=AUO?5d}wZU>lE8V)2g}&oz zaB3nght0N9S4V`tSxCRByo!fh3A5tuU!%*d>Ie60%P_wNB=ZPK0WkOTQlG7qXu0d| z?x|1xcsgwoARYeu>9mpjv-MV6Xy=YA%+_N`5@_x=D8}sDA4KT^si4lD0<^pYmDoO= z%+FgVQ$285{%!;19ppzcG<~pVq@YBDS$Z@@YFazJz-N`eQBMvqSY)il&z?%Qz%cM1 zU40-GoUp}BCS<3-Ancs1*P&s1W^DQ|Wfb9El@YG?d@9q^M|GULa%>FD6f?$q{hO*f1g_iMi2=EC2B%2&f5cjcq?oZ<+%@R?A<-LdmYfC5rHV6ZwrbzM zkPzZi){+~52h>uEmKn=eF$zP_vMv`cI%nTd?_h3ciJN#gS4U&pyDD50@+#ry`4&V! zzYcn#e@{Ww{@%5Pa_Qf1-(vk^z8dnuq$ne>>ig^@=)sU5-W*|>o>tSdn#El-%2#hrB?Bbp?#Pm- za7#LSUM`|f7}l?#;Ydyh)|Y}cC>#e?aN0yD^e13>ygxh)B(D!RmY#f$RelvN5tXwW zu6Ou#>FIq@sa2^lw!D`&eTH7(jZNzWt*R`FQIpu__!b=n4{(U#be=w76X#}5k9~nu zG2>Mot;UlBaLc)@flW;|hczKT0qS&5$594`5y(~0td%wxW~;lC<^nTpExrP%w@ivq z40@){{eOb_gDd+R365R6=%+EdjT?8%U3Wg9t}a8+ohp40G$K)zJQ%m0T2&&jv~X^* zdY;(wV}ZwDoX!v?^-eJR1^m_M5UwyLsr2&263{+7g*~q}13o=_ zF?Klnhb=x+9nXq^=4Ev0z{e=OriPH!>mXaChLuoyY7G6quSblGum%qEa&8=+AS1jU zVa%NLw=>@ofCWsmQM9qb!M%bdMNgPOzeiLTjpq_Cm19`E4d_cxcTr%q>q<3~rquUW z{gzVaVE1VLtKE|U()P_@&sfm*j&qbC>#44|)_f2$*xK`?I2jmM-@V={yaTluWLI@v z$G||zLVwAxBJk34YJCrW{g)JlgNop5t0~I`!{?VOulZPxLJ0Y$tCk2>0-lw^>l+C= zv>{xf|IH{3glL4ByP)xF0P5G6Fz8!X1%F!>q$7@lPgm`L-L2EDs zA^wqcf@J+1qlm>Tq89bqNh^;%d}61CjE`Ry?Z7kSZ^)n=YnZijT5^m!jYU+cVsZd} zF<02$ZbrECc*gTH7W%12>=4c-KYc**pzS&b1u&3Y>Nr6pkOS`pk-k1q3+>W>Z?PQ*d)Ugzgc@pi7U;`TMwI8l1vyo~FC z&veyI>dHfhK;PldyAigr;*1c8BvgmkT|{}@Zs>Vkmb!$ zeniB~GR+}4D}{`TN^=Ng94ce#iwXAGmJu5r!{(4=!;G@aS}B4}3C(o? zc61@_)@bJe46aEs=LmYeT^92NojlIMs6_4RG<+L8(FRbts9Zt0uoW$JL5idXkQ(#m z%Vj7I6*cjI*A5A0^ScOp@eoqb*!Q^#5T+tL>X#}9+N^4;cW-}Wyt|j4zzi!1_rE?~ z62yNLY-*C7MrIgIqk!u#Z8t`O=Boxgr{3>;(nzd}A63RbnebE+FHZ>{d%)aMo@gvb z0y`+b3;L2k%gTcvjO{Mb`GgX92QqC47lOP4U)>noD9TJEhx?zOk+4*wM*6bWFtAd` zy&HZ#5rZqUthYi}HS}D0=KSq01ZLM-l?s{)JJ$~1`XrFKuv}F;xa4M?r6q35e7U(!LLGFIMu=Fr1DSqqLk%yEL2~0TXb@amK_-Wy5^x>EDi|`STepIPM z^uq3UPR?@UouXqy%c#fZ#Ya7|noW}M3q97Ud>t80x^;!k??U8X4!nMl2XaYV=!rz^ z#;=?v%E4CMjrA*c!O6&o!bJNrTkQd3#QAtKL&L!w>a!>lYk=vN4=gq?l3_)Fnh zF^hCmeYq=4g93Y#FiI*JCgsCK+|AcdHIc+U7C49jr{3s?fl|#sB4qH}77+uDlVaj- z6uoh|P>)8(dj{=#nFMx6%gn(5@8+YSn=+N9BwcvnfO)2uOfp|aJ|^ADoI@mftqE^HD#4XbTDNs7AafP0(3 z0nfX|BlX*KV;2x;M|e8|YvkO7OTCV3?l#sQMLWh=%LyC&-or zN=KtHk;MJc(dC)tIKgd7)a29?1_-@ zmMeW_1d3_n8E7%dS&27}aRw_BpEZCFr_Og3!*(CTBHWBFb{W~YS(arhDShFU1y!!Q zmza29&p3$X9^u*)061&$c;Oa6Dm^VqkuDw*dFoo2YziX2KjQpv-$02zdF#8E!S2l- zEpC3CtGsXjF-k0iZ0Ug-NwHG$L1?R$1wY3aSVZ*d`E>EgSHo4E*<~SUPPr`l$NxM&@UN@dDQ8AZGc#h zrmxb2CZH(c3mDsbW~jDMh6Ba7gh?p8*sM?`;WRthR5CZR7L4M6G)Yj>I=VNjnsK25 zge@QbeceCB`KMqX{3B`NuSD?HC4d2`63UCU^$?@jQVfQfO!o@y3Wa55%V=h~NcnZG zcXlW1*?X>pc_*{kn)0jp>IEDvr~m^RrbuGGMS*Qr?qx3XirH| zSb+xE>`41ZDrrMoB-!exTUO4wem*fNA`0}pMq7^yYb)UCnOpng$j+LYI zIu&!f3NicRmjLiTFRCh^UJ?igq=XY$PS{EI3GTn2DuI@`mr6?%lfNDrNLHE2(gclBuQ;>8ef zCeGiD@QW6KdSjflg3bu%B=R4q%eRsOj5DP!RK~sim20Vs1}&>(!bGR;2RVT z=~`bR)d>+kZc+8S9#bHjHVAb1FKFev@W)R+=spH2?tHSirtimoe6k@Wz-?O*q>%IH zl%JYsU2V3CtIXtALePuIV9&y!2J9f%eEo`2_JIby7sF%wLCZHnu6*S$i$m3o31llK zqyp%)4EUS?T}pkk{u3SXWWGv#Q2DIxKdOZgT@0AqPWDcY=M(6_Ne#bpe_U5~+?Ax9AeEx4D5QFEwA%P}h_C{6#ZZo2sAL6UF%Q z4B`sjO6e+WX)f1$KQ*YH64Xo`ovbwJ-F0w!y(bpoUQaRioXjMHpG+?S?`b`8uU+Xa z*&>hC<)4AyelsZdtf;Gni8%e}enzGD4I@Eq647(HwO>|S%~ab3kv5a;rR}AD!>=cT z>$1keqelXnH`K3c-|#BfsoV)#ZQ8NxG?~Mlgx0$SuQWB4XlgH-ozxw>8d7^%7@_Ws zNfH=gSaOX27B^u&U4I&!%xO0HUMQHdCGr9_I4skwf@C0+F)~q z2$Yn$U|!F?STm>wu0%pJ13zs3%9>pyAZ_D(mDj_S9*Suc?lvyemWVn!IA;%isw!hI4=y(seQ-eU zJu=w*2MF==7RU6vl$a+u^PiHpGiT;MIh~RIxH8r$nAdBHG7)hPIXsNwO5B`uyIiyP zBTreXcRqFe{`w_SKsM$ethzi*p;(Lk@3_$Lua;fOqN%~_@4R4o99aD{$9I?6(Yx=U zIAK|`LC=z7JCDrvZbBzLoaL8bLN^mGGW&c31)@gSq-$+Oi-UOY8kOuz7>P54AWsHN zUxIc`B2=4sD1ZnK8G-bdYYS2QgY$06<<>ZogBfbN)4{6!!er=u@Slu7DZOiozixAY&|+^= zh@chgX2ghHN3y@AAMAI`lA8B{>5javy;LvkTf%Vi865a)J5D=~ELT+!O)n84EImO;!w&+Q*_ow7%0$@{=$>(T_F#Ao{4hJ6uZIQQZ z%QHd-?bhITE7trm3Prvr2WM2$uHUmLsK@F?Wrcbt5uHvaVSmL$?2AD}ytmE}Y^|@( zI$1wLCQ zJ4nwMaRcashxO@lUa)!VE=F*!tp6}%%Y15oCcbI?*n}nD2bRo@EN{COsvo^C`P!cu z8IZ?UW^dth@|AbEqq#p&C3K@{Wivfsw&Sb(G~NBpQ_DxRrX8f9yh!?)r1!-LeMzBAqd57z3GpjX?fR*DNAVQ zqq)?5J1BnvS+11y<$e(F#5EdNQ(tnDcU)!l!=uKp+Y1X!1sw?j-44h5FQf>^nyRs~ zv}*Uv9$l2P6PW}@B%j^3B=K5Qy$P-`GTk0uBc=6r!7Y&H%pxlQI=J$oGVqvD^q*V|2z_Nt57yAb-dBdr%GLW(Sa> zn`art#&As6{WHIGb-t>2q6xH97O)L`XceK%?Kc&KDwLM8FK87<6EfKILd4hg(xUoBJId*;`s zI!0Rpuo6%b(75sg>GB$!A^w5`-kPkJrd%3P@5l|G@Bs(kzdOnwz2XFI*Il+X!c^*| z&Z&XTv-Fz?oj9=k@ebennP1hb>P(sKOdeaX7-Es0Tx$P&oZxLAO(}QztSK4J@nhJa z*>oUh(XF?deJW4i^rDi0C%^nm8Fs2}>|989lo}*8x0z=hE4uftcvOgL@Y}Oi{1+k( zhSTGCl7tw4|JFRxU=tnsc*B63LByMlm3}|xMi(Qw+C6qy@2s6D2g?-^@2Pl)lNs5* zY%qQW_TaraRoen#WQ>U6PH;DiEXey7T4uS%o^)c>N4Ak4wdjadOc?*ki<5#auKSpr?%FXUw{lIf?YH~{kAjWc zSJ=;OlhiPgz#BDXkV}CR+f^bM=m81rg#z*;J$g6f(1tQzNCA*J*{pxX(bSg?k)51W z1T?1?If3b>hsRHzbrm(OZxaaX1$itdBLrC3(>E2LxD7Uev`=7K1$P<+U?o_ga&MXO z>sjxk9ljG2NyZl`Jm~_u!nihwET@$4*JzfI+6eIT=BeQBCOVxZv8kCkfX#AAHy}|@ z2^TKZ%?T|NbzGlhqC#jK&U$|^kk%woP3G`xO)2m<<3GUvWFD&)XndsJeZ%3FsSLdd zUU6#KDff*3@3qBd{%-G!gX>O;AJCUcLg=_o_I7&EN<p2@wiZAbwa#gorSq_uq$5e2A=8wLV^Y-&1Bt zNqh?iC?kQ23RH9--YV7-AkXQgK*N9NIN0a2NYRzw93kJIZEF3JW+v&HhSJ^FVnGk* zMfz(7!B@dn#h@3=**Mm?tAW+xd+&D!bl)4!##~NU^(gNe2VPS%a?!jT)7}(YVFc58 zne-elO!rNc80tuQVDYuWSu(WQ@F!3Al?S(ESNXr+rNj8Il5A-S_z@`;`F6G58kx1y zB99mDo%8T`YqT{x&2mNcHKx8(IR~B_xciIta($L*!D=d}B`FwvBMgqs3ROr>qwgaK zf(5ORDSD&d$5iL*j4lT$A@s8_P6jJ9hKI-OOLO8rbXZXzNPX%Cpfo#U10=ZdX7O%h z+B@g>glsp+?Q?>G(c>_$%V*PRQGFTAb2c6@Yxc#8=gq$DZQ^fJ_~>)^f_{F!O;wXq z23T6$(jo(_S1<0awDpC2np6$$5-(r6#=5NM+?~~{O9Cc3@nG7N+X!};NBz$Gq&+&jF+If%2K0Meu|KqEDHV^q0}8mW z9z6aOCq0-j5&86u9?*?ZfSf+(Z<4`_AV}M_nhXB%EXf_Gy+WK}w)!;jwQ_U{~k!yH}e?Fk{jW zw4dm2_b4#{4?4Kvn`cgT8abwedanf(Yi*Qr1S+O#@NBBoaBbV%im*TM*lc~SWGk?}R>&##^$*L?*sZ$2tZ*arY1(e$HE8{(8g&kQG zgc-^`WB}ttRlv<;Ju;w&%>5z+D^^Co7ej*X^v`piTG#FbMgna^TZ-RVvy=>7HVcp^ zhxr&*#ux<>34I0P$ez1QWx`=*D|CGD4iw~zRe?4z#Vl$#J>5>GR^wQ68u$T6CtQ?* z7>ifWYEqR&ykN09=STTqN?-*A!3`JHeG*vvW*!@HCV#L>Ff@HW@RQRcw~*owSu4u9 zbg%d13(lW;cyULQ=hv5sp9?X5nKRrJrC_qW^Z9APCnDt^Jbf0a_~&uLB{hGZK90#s z@9-~f_%gCO5~^y<+gPZHWOqwiiR8!Ce0Xm>?aS(c^2v-UB zDMdODWBkiDL%!gR6Y8H+X7*ozJ34{@#K9Fs2jU`V>e0eAWgrocygwu@jNATcRXUh@ z`RQw(bZ3$p-4s?*bX#Iy!iuy7BRW7kb>!PPnTT>P+FT&7UQHNx#``#C$W>Sj5x;Pyk*B$d`X8}QYLAb&^FsL5Yn+>YWQ_{3*_mKJv4hTjoX z$_Y~tV;2d|8&>t{N=ECxg1LuPHgrDBX$7>Qsw+rODJ<#`&<1*%i`faC9Y~#6AJ|2j z{jxKEV|E&J$_6F+4|Bp(#6YC(&b6*zAO@X*p3ShSa<9C z^oD~JMEq$ddb2eK$IrLBZ}0s%Bd(m>AL_;-q==j)1FnZGb)3?>_%ZsE!zxe?HdBke$%D>*RzO)8LD zu{5#U@vDRC^&chS7ZyB8SR!e{q%KON@{B|*3W8euj19!MeIX_)FXPBcm*yd>vgeyy z;f{nMQ&!j(yHs~g?Zd@>+-y#^k(llcn0r@pVQ+`VRFrlwATm?Bpa5*W5qDtlgRya= zxx5zF?1bA;&XDwt-@1(2z)76ia=Af@|MJj^83gopVrpArl5O zajra8%cF6?a^KO>uFMJV=6G@aiU-nELEF@zRcKG<9uI+DxgwIA(D5T_sdaKA5O#0M zlT(%N3=y7OcqfbxjSa=W)v$H)x(VB9si!CIqrA)ygXS;t#$TN%4g*t7;T%9 z$7&L*B*e>*El`UaMxf9hcr(5BeLaAh^faqgLqyK8kXk4C!OSv8`ktmeV?g&uLIq)T zbaYWqZ(dQVr9qIDcr>91Lu+$Mya@Y1DR;R_`E?_KP=UJ^adP&ewS+N#;W)|6Xy|Fq zZ@)miR~g~#f{G?X2UK@FdsXL64UmRghrAtKHS5Z&B+scqJD5zBm!`lqAvK&6g5+@Y zCujZYDsEcy-M%lZ5F%*OdX@`BtYy9ue_l(|pE-c(mReq0O;shZA>XufJ*fxuoY8 z^uvu1Kt^*Lx#Ziss6YSkm+r)qYCS&(KmFa4VwycbsgmcT^@;sem8etqoerSDLjdt*oNrOO&NX>)0`g2H}aKd;h>ehuE3fr4E6E&r1R( z-P4!iZAc}XFAx~2#fZiepfr@3D9t|T=MRn}H>+uV5mMZt13h8ybwL~ z842L#@S74VKq!CI`}kvuNsvXtRRF7#^=tFZ=_i9%J_s28PcMyUw|hwtiJ(_~aw4>z zuK@AmAiD=|e82wP1z{Y!bj=NQ6kTg(&6R7fkuo7TsI=%CZw0rM^>nHI9UNL;+l>J` z)J-hU756gYZv-k-B*)p|N+qah9)iCe{3`HYPXPjmg#m8(k6xeh6l*;QL5^dSWw})x z*fbA4i}xGqhJ6YfTdD*%@(ihE*mWJN^g4xjJ# zYUrm5cU_jljo{zOsc5j1{y`^yDpP}lp9x#a)D6S{71=#qUDT*v;>yx}D%q#H`l%8x zBRF{?bI7wl;2#HH*cFyXGP^AC!ufmOaR$^oiuX$FN*|pYdmHU6hVmMSEDMC0WULf2 zr1?-{aWJ|x0i}Dyj6+`(ctL9cYl3INB?PymD}y)96zhk_2O7dSn<%$y8om;r@mAkS z$J7bU;xEH;&TZY;Er~qT>R}A_VnFA=*RjbF4|UIBAUtFd$?P)vVDM2ulMx^-OIyC!W+WGo{8>7i4x-TEbMRH0@r;6{LU~FMqPRz` z=wH)dXCV4e5M=(@5Ng?!Kzp{V>>Ce~>rav`m0J_+I+ zU6erz#ka0hJkgach-4Z}5Y--Q*2QWqy`Pks$|m0FPU;fx=yAxAYGWOvk4rUq0>qEE z8Z%(a(vA|hk>j)$bI3xft#L(w?4LazE=dD(dM-yF?(v90lZAMWYOoF&fPsQ_{S*H0 zj_Qoq<<>J0StTKjEP)VStuDsoi_%YXdX$=nf`RIJ+^k8D$jvU(j*g+-IiBhF#&fTF z-is4ehD3-AoB)~u-Hv}pLA9j_4&np1$SF*C+Dk%E_$lmNwB6X$R3HE7@Hz7sh-H8D zM$<~YS!Z0t8++FiZomZjRioT|&!OU6>k%(CaNgQ9)*mR5S=Z(VQc4(e{7?*7jxDcF z0g*f4l3gyoDTjOn7T}zS?g`=+0H$`C2G9k6*y-^lx28o(DpT32%j>*1IVtwEYq1Lc zq}K|b$hH6&M;BgPoZ9~Pu_K{vR_W4obPdXfvOnpmNFvlO35?ojGCa;fD* z=LO9mDY27ry@GViUFo^d*NC#up8T~i$7aW&mf{T|i;3W}PJ1++43Dhi{^!H4&PFNS z*SD{XKXSH5Rrsy-`|V1}8@D9WrVWS2SqZx`A&4NRyU9qDqa+yD(}53nng{x&{9uOs zbW_*qw)QD>QR`J+u`4d&V3(wx#|IWGYR1lN31jKZ(DJTC6L_?LjX}Gn#__XwA+)LX zH{?>IRi)d&1-J>fZiYo)eF~p_Z~3#{PL23%iaY^V^^n^FfmI}k9XnsDf4#pWnHDP} zG8hSWf8v0IiD2M&=CQquhq@ciJd(YT0nOhH^7cx4lXjL+B~=;{qxJ3+o zYl3E%j-}f(eYo~*q4Pd4v)X%52iOg&K_~c)G%)VW z?AdJ|$xtDv4rj;e#W`8h-{;)52neb``PVGL5M$Z?IT76qBEb(L#qR*Df?{kqUj`8a z(S3-64g|~yr$+*EzLE^$wcSY+2CLt)V6|3V8K|g`oVkd{PwL}fWn}48nLi7jNdT;~ z2qcx4sl9{$S!@-cm-BlR$r@Yl?qp94j+Y&eC~Kyctv~X-adMjtH{OzP#g2<=5Du-6 zVzu~Jj%%S@hMdOh{t^gLnUt_|4w)+rOzfrWpy(Q|n;!N?J&(vq^g*(3bHf9fYBTJ| zSe*`DhSjq@ka`2psT6@X>A}iwlA@fAwe=^m{UX6~npAu}SmIQrtZ8Auy0;!e{?7s` zEIDj3Ls%NhF!i7T{y&ACA_go4;gY?ixL1Ok9jh(cz0~pbg<+gh)P1D38aLtTx{-Zw z!Yk(nnKx$wZe1fhmQ3@J!wrSiYlU7$JwoURC<6dkAOD+nNWUO?{;XpIYs(>rwSbs| zjy)8YE46-62}t`XXsdSOXhem6og%lWB6ALr7J_TInPoR1l_4>A6yKqnpRqXF?fKRq z|9hHjh*ri{4KPSaoGBd8M}gOxw4-kk$=|@-^EZw_u9#E(Sx3n>{JT>15Yu#S0_?{{ zB2tvh%Ob)t=q%RBc4^l9h4Bb$_mU)B`%qi@cEF;Fzifrjx^QQf6^T0-F9i8<15p-^ zR9>$vh3F8RK)eeaJcAz_1uO{vPIDw~FBu>NL%zIhumjddK`4n&fY!gsKfB4o1@I%; zO~16V^2u@|(tOlZDWs)pu5qXLI3y;Pys0KmMA41rLA)%U-#<0W1Za{7GUP)wO6Y}BkTg;`a@RFF8{Hme3iu?>c4 z-&ZK78T7=?=_%2r_c|g%>?@aB#5cN_Wl7U7I^OE*741AUu1hTAPuI-2om1&6rI%3o z1OZO?4(;`WNrRok|NDa!7>G;1Q>29%N5PzFbfNJju63>C|B>|-4o$V~|Aa7+n9_}; z(xatS6a-yA9g^C*Kqz7Ic35?4=&eDh(2~W$$wJ$eXzWvXug{KFyr4D7dz}ZW$5Yc3 z2YHu&KamyJ5d_BYLfwEz`2W9KSBODdy>V1hr`wx#uMjjzG7O6wEisRBz}2NC2pEb^ z+H_E3?x2l=(qh=cY}tAPAe<6%B)ieq`ATzM^*oG5^)B+LB`BoMgKG_tLTn?FU zkw;IK#r1h=K2ShML#sZhAc%>Fdr(p#hA#J%hr(=a;-7&^b$6`e;uoxy%&rk5Mw8~k z3}XX0j-1?rd{osFxPsi|elC@4HVUcgAfk}r?>YXrKFB;Wd!}a!3tu0gHH7i7q$3UC z?Ls~Ih7R4{ww%v~^G$;P+zE(qDH) zsuN<{(&`6Tap|9M{I2d0N<{O2Z*!p7dc^5+D*7vir~`=i{U9MzCpF8U&!Ax~{%}ku zX=V24(4C~Q+O!0XiFj4D&@T8B^}KknfMsQXCX5xAuZbG125^8ybG#DHwtj(jBK;8< z574lHzTz(N^%69Q4uZTakv4Oy^yBAgnjU5XVzaW zvpG!Rd2Bf97Ar>HHQr9YeQZv^cT0eET!hc&&7xQhsZe@m4>#Z<)MoSymhKS82qD)0 z8zlY|+1jh{&9ixqi{Jehrya@{N7`HU!9#%_OTV-?>*8#RGtLGYAZ&XQy=D&>(lz*o zQ{gqHe7iL$rDqthuD=aM&jldbtN`yW+7m;KxYq6mZL)wQ@*X7Kboaey&;<-?3L_qr z$A+Ff9)J1W|Ht{WdkV;QGOpaZI0A3gn{!NOs$WbM%(l6F#CV;Nls)x-*l@Z; zR&jbytn+in=u74gmEO08)b@scE#lTzn~crOq~bIzs4$B?vd3M2~tx{1pd7k%ddSCVE! z&$8l}jJV1Fr%s6yp#3k7GH^_oK)N=AX*+X;kQeL=T3R- z>b4_KfT!e-(R%&>-;+E}yHi?UeLg$zJ~PZ--5oN~WIQH@eRHK>+{D-hFBQ1)JWHPE}SM$-W+(xb(V?wR9e3>34(9K6+ysKnPI%ZVwSUu);3mVu%n&q?!eXWv}&Q_2N(QyX5w6&G1vHDbG;hoT9rHx?!|z0F)ckwOF= z7hCm%&VN{uD|nL#A~GrGH5V}r80ra67Sgx-9>I3d-OlS|DwwLA-6H2&geeh1yPASE2 zpDGcS0F?ma1CC!3Po_G{h_$&EDri`Nbg9X}gXj5x8mrTiMR;C_w4Z+~ZDam(W5aQ|g+&;&W*?nJVem;~9aRRY+B zK-~=4`%ODy%#WVIjq--ie~Y13JOo{Qc=%(v&#Z-nsl*)t;?{Z4N1OYfPdS_Jqd-_6WmbcouV8{;|N$|O}p zUr7YN<;iT*mL$N1-ZQ;9lR%0~@}k0*j$c7v%xBm)MJ99#kV|l?>L0>W7$*k1kLfYa zaJP!W%_d2{#Y7TKhMQ|AXeu$}&576wQFq_rH~fnw07O{wSt5wt-%Lsf< z?w!}oA0~U|l4r0Y(_8fb9DfBp{KYHBd%^3Ge~9ll6ewDL81HrTwIw(3bI=8+QpMfB z*Cqrh+t-9}8Ht?0+G1lU2I|D;-i!wwbbU`K7dIn`sUM<8cqn^*TS|eN)%nqTnJc|x zO+Vb~SRP>Ceqa`!@fnQ1WgT)&LeS$O;qZZ3y;5VX8ZUN>G93wAv<1dA^((KXDzFw5pya9X0uc;@c#&c^a z(+m%wyQKS7d3iKjU$ticGSyUg!U~Bw`jH2nT>qZbnNMt}Sz6$1Gif&uyFVB>TM`8p z`KKU%hevNV@i2((;@-`1KDXZ-TztUI{?ZIKw~Z@C1zPuL`wnzQ+7BYm{jFqtMs&am zH1LWu; zs>J*1q3OLJKFs+#OQQo|2CAzg!tK>h zY*S_X_Tv@*;>|HQ7QcSl{aC~3V z$0-L||8zi}os_&~mj}fKTkbtLd(Tk`vIr^?fy>ngeBs5e(E`U0*%3gsY?x02Xe2xE z(q*xa$0W@(F#XXREoDjZi#-95tXAV0i}@jz*XrXubGddSv#(!W1~K$_{C!ZC_v$xP@Q;xZr-HHU z`CeYu+ob3{mVQ4yLki2NK6;HIYV0nAExy8Q#3Rc_=Dtu%rS8TZZ6hl|YiWFkC z#-Hjsi*>^sNyCR70r9ieg-*M&w;OZpRA`AfB76^;{F(_{R*3LIIA;8h67qLW>Ap>3l&r;#2MqVKb+i z@7X29mo%}jLI6Z)0%ZqYJlqEE_xtc5gbg^Nby*5qUjWHw*z%!sb~B-m%nqmCc=P* z6?ei10OVt@Y}s&PQrOs~I8S5QwJd8+UY{x}A06(+MEkGP*v=JM0LOqO&_mgHdN`G< z$>RzT4ErCbNze#Odf-z7>aue`7!3nbWb^X=nmU@=n9>fFMGATIFcy z$zoT$8A^@p@`;fIcGcvT4yI>-|0kQL%9z>`O|)hYw2O2TFpCD2eU^f&3Y zPRPomTCQqDgK)C$^r!c;DVr~G{>z5$I~?G;MICjbw@-qs|MFizE%Y#5M8EL2Y(IcY zpU&4}S<)2zF)5fm4&IV=Dr1pf!j&x+kB=`I8mr7!?yA8UU zBzdvk`QEtqvH2OVy5ck<Hd8;NnG3e>SnMS*kTeTgaT?2okozECkxLNq4 zNB@#FdVi1Cw4^W{X{#FhE&f?}yeCSTSB8362eU7#74D#ihwB4_;(;iV9a{Y8pptT^_E!l9(;neU0K<`pN zumu6_b`x3i#%`UY06GB?lom70!V(b(D*f;dLEa;nWr?6f&^zE_?xehFY{H$)=F}{Y z;c997kBy!+^L&^3bNV^FX@wH?gE+i50nP@gGWq6pF%c3fhQ>4 z!TRHS%I5;Dik>pg2O1Pl+&g-hYUaSjwH}iO%BQF6SYakE*Ebc%! z{N{z%^W=g&!QjXN~Iu7dtDq^SU!fbhj?1&#_1TqKb`$UGcFcRQQkHH9r_jKDjz7z^l2YFjH02nXOF}eXnsn$Lb9- zCdv%(M1I!JYl`Uqt3}ZosIv`jnXrX~BFLGf<<@suZsn;C``KtPE~p}Ji2W_OZrzz& zJjp^(f6r^$i9b8$TeuFLNb(2MiuL}R)q)C`1+9Mp;)$mmFl5JoG?~|g)hHItArkW?;$2>fsEk|ND%YD%0XoOG>xFAg ztY%yV*LPVjgL>7>+&W_{)bxH$Y6&WxvYf@_Wo>?DYSf)w7%pN=UhT`RzUgJVQXKJ+ zcOkbXp8Yd1<+s@YqKMj8A<1^^+3iG_pOxDo>U-nV6$%?NpFyRYksWxRkUWc5Uc!aP zis;-v9z|9#y8FCvoI;F`2(SCSYLaHuR@sW}wo&^n27sEnL@-<+9lcGboEnxkk$(Mt z5`_Hc*ZYqKM{Egp2TMC$gQ%Q)dJURhQ#4MVcBx#oh*HoA;`0(JsK)b;&U(;-j0)0$K5;JmtB1gKXm=YeO zuFpFo|B$61FjG8DDR3u26Q!sFm(4vqb>pD*r6Y9Dz5)SbQb6-;ILr@Kx9h3QN zj69mU@O2wHTq!B_-ob87B_zZpC0sR~^&9cc@XgbBqU?F7xRsgq`(a+?d1hQ(sCTN@ z@`IGSXDsLEl@?9qWY@I!U@QXJE<8c)1EVB3n_>{;;$c!}H#+vigRy65`@=X^XbLj# zA`%4%5}BlmLyglqXa`V%c_nE;v|+4_QOi-HcIOC z(V+81;0rLmsY(a&<#xUrAs&`2yOS`GzxfxUQMaww?0jEyfB%+1nq$=j+K(iPDZj#R zCnvwcMBtXF&>oPye6AKTatGwz$1^Xlz>dQq`dkfnfVg(%>%7~BtqlDMaro}a&WliEUF&~-Ociqp6I zO6e3g?|-hLL$+%7mt1^N4>CUu;hS87eV^95f)ky+2^Xl>Ms*jbKhGutB;S(LdcqA+F`iB*u_VK{pvJPi~>@zTQ#ClhK6b9O1r-f`1_YO@| z`k^q6Jeptq>QU9%gOwGI1K-KF1lC9+QDHkE^J?2=uVAkpVvC@{Q-^gb9Sx7eJG~ho zuwLulnF-dha`k|UxuWaoPqpj~p3duHhHe?BWl%t1n6wjJiJyb z0<;Ia%@@^7*>Y>|C${%)Z4g>IGz?CqM-cIeZI2kr!_?ji0nG9xBXkEHcLzNV?ToJb zS&&6!dhXqJaj-LS#TClH0dsR!WKOtn~%^N(B3Yo0BAQa{r32`pC26T zw8wkYUy^Um`xYS>}{PuZd zgc74egoGEqS`%GL^V6~WlwD~(y*K!W811rkBLSJyMggbK=WGb32_dTw1alN!NW1Y zLNV)eO?h7QyYXV{Ogo67o+D06YYhrw!YKoQ^645MIHmjv=tj!!=*S~Onaw%L-N_)8 zwEioNg-0P18}K1&oDzD4fHhR)coxjhpkN8D(Ed}mjaM^Kj%X{(js>RYkX3TK9sxar(v014XdvYxY3jItf zz_AD?HfJk(gFK1}K6T&9yf_8!B@E!0$&(XyzG_bNv#5eSs&3wm4X0NkYS2PG5ymVQ z^|Fes1fmzZ9?{wv{TS7&3PYIqIy4dSsVtZrAJqTQlqh>JH;HA(2{(}z=O;sKNAIY-2G6<~pZ?Pu{!k^v z6bCamp5A&vTFrMsjdKk$<0e^}9osCvT4!Xv?8=2;XFD*Yv7JD;La}I9(N5lj7CMi5dhdT(3!4^p))lj)=$^Sj?{^8 zPA#o%v9c3%jjRd@@|nxPHAlVDlJ`n$3W_U+wOFK2cyCjO$Q~e(3tJ;kbFKmEo)F5{ zo<4#hvZc8VH2sv-nrc7I^y#6JkL4wt;-!qUgJ zCgZ6CW=OcCUTc2@BbwU?()*eEqItCBbdeSJOuaA(&uvx4E6-8N^Om2~x~Q8M>jVu4 z3J6~Mt*9rMj|EM=W7z=@`qcz9&D}2b3U8ff*xcfCoW5Lm?!GOqLGgrb6sHa!1RKrF z+FsnLLpXJxE#{~;1forefIoLBDI{{Z2?#J~Ud@vILk^M=4%2lxsoh6}izJbVghlgj ze~+#2rT_g&5eF*O;Z7C5r^L#g>Rni+x6@&o2uufkC+4+6wud?jIQc{oncItMBJTj( zsReMYyDVDzq1RWWY;&{1IHJ2KB`R-w*Pf2UzsBa=jP7RMq%}Y4aZ*SU08#$4I0S&? z#2W+wwlVopRlgW^ii}fv2&tY0As=gEl_HBPwR8mIfhn%7+k+oi3H8}Z@jwG0Li%X1 zFUPU9j08LK#`Y@g>?z>ediPH5kyq|AE401GR|12$4fnDKb@>=p-Msd6WzFE`{Y&l8 z-+Kgba14}h{6pj22f3fr+cz1L-$zu@xf_#PkKMyCqGx@mVZy09H-VrML}gl7)uJm# zFr{~mADw@@0-+ZFH4<>5$R==|aME?zH3K0)rNHZz2(~8z?PB*wH*N)|65sgm)piO& zGNmG`Hc}wm0#Cr!_omQ-S)}IH8$!^j;3K&7nxcarq_S3>*GS1e@lyiU_e3v;dLniU zj^Bu+rrV91=jWBx_&tb(0IgM9QbV9+NM4FS6Y{?oa*gE=3uo%q*{`wzcTziYO9r3? zZOGMO#^hLTd#vRf>GIU%9U9Bs3LUSWUCGMRP^c(0u?dTKhuCdzq^%di$NT0_t;sXg)p`^GP1}@$JCzKUJnLe^d1l7Q{Y9ZRiC-%Ki~F{P|9< z|DS5dDxtF|m3d;62PDzesLPM_x{mK&eu|#Bxw3R`%m-bnBc6(FE3TMu>sjlXrYO{u zz&yBSr751`GcUp9|L^1D=tUlTEb z3uz0WICNAuUvQDfE`aH+Q^sK+}r+GAFlvN?GlxG+y2&{mlz$01;}{F6j<=rzdsA z9d$GW<#)ZT9srK|Um^f^TDTGjeqTnb|1iUh_HR7D4rmdA!1#mb-(IN$bUN!kjM3I| z@^z+RiKwt`_g!54ous2!aN^NtZ^IoBJcZeCvyrED?|W2q;?xc@{gSNZ(@Bg$p))DvXzy3J5j{Vb~MGJ>|pT8g+}T z4w@W3e)khUlSLafjcCfJAOv4@O-Hz3%FK@~A?YvkZp7un-N^Bl8Ly722=F)s{C_LA z@Q9FP_7AG`)xN^$i@+O=JjCuZ_RV`sa697RJLph>%C4r7A4-xruL(;3`b!}mQfj!% zEjX7$90!jp#%OG#cv4fuWh~Rtz9x?atpM27x;(o{ToeX5DoBaDhgQgbz=>hk`Kf_e zx@7A9QzaB9i_TZ2r|0va5k!dU{Dc0^Q{h{r?9Hp$aaOj?Jbk(j{4fprcKj)V+nbIZ zVMT>ePDi}^wR5%rF%;DsqZBl!f#EcnV9gJ)5A$PYI=9iWY(!AtM%wPQ%wxot@nbA|D?iuJz=l;>n0nzhdgUr+@1zeDl&IVkW-oZrl7g< z{gjtqs;oHrPIjRH(7;1wJ?jp!qQHoY$;1a63P*%hS;7#Cpk4Yu>XEcdb?qNj9R71m z?;d}R0x{r+qc`h|58Gb+CdMoB^Og`Qhik%-T?bo%wO)2h&W#7O#dL}ogq3;x%jEs@ zXGdsYRM;sZl%U_-4a%d9Zi>Hi6UMFSVwj&IV92CnaeY@Mfve^=FSh#rSYMOm+oKyA zi2vTO6mXH8dw{onwSl(2_BvdLoG+6ny7>v9%|2f4LbctFE7QD^_3Oj7}# z*wr%af6i*CJRmjBosbL-@O|1?o68DK+6KzMXGCEsMA*dc#LfH-F~WdRGR_2QTbUO4 z-7vWnuQ`XpI{1!bI;)u{crmA83x1rg+8) zuoLG)AY!mNq`8 zq`c`eLCZg!YbxW!{;#T&h5(2}$jQ;Qp=b7WLo1tbxQHY!=+d6iOqcg*T^Ss$v3%O{W3 zV^Xe`)DQMI4k-MNUET+rf4WqB|62}QJFL~vi*CP(UlpbP{-PKmss`|??=wdcAonOX zXvXU*dY;oqABEnWJR;}2br$(Hwp^;jm{Ibaf;~1)O0ggntXN{{2bugpgqXcd6As?$T#q3^UMi_k>|U zac4b^O+1AQTWOmT2x@ETA>@jbxwT`bwuO3OLBxzu+b?lvN9al;aCDhQt*b`gc6W7E zsR0DeD>{g}_Z)g$@7VwQgG4n(;ZzVYp?VkI+mMt)n({6LpuB0!ieu;Viay2b#*<|33jJ zuzjUpleNEgqA;Oa>H+LR_ah3WARCN;C(+4>7$(xg%^mbwthC7(_o_F(_m~P(jjhs) zNbIb>Zdgn{?3d)BQfaPlf)e7!lWI3lf2W;zxFf1;U^dxQCXBA^{39*=53w%(QKQG7 zUjq*5@4&C&^S>n(49cRxdq$!Rp~+|qv+A8^(qhWx02~Fa+Y`op zxozR3+HDgRoQFiv8FoW(@_J+_U>Le4%DbqRjLu|0it!53?EqxTe|1r;#Aqe+`va6u zj$efwDQ~L3UbmB62s46YQQ@(9C@Fqy)==Q7lcuYLjg*Zw@AHF~uTK|RcE)=j**UyT zJgOB$ebI;%GU$3v9LENw+f^xrRFTAS2RVQ-oPU1jKR(UJ1n8U4s{yi1a6b|(iXFNlQp6 z*cjLXJ2dnivr)T?V;C=~xx=R;CgvcRw!rptNI5c30Xp*%_=R@OdH5vtgb^c*=)X>@ zI0C-wG(@J4^|?}McU1TQ30jftS4k`EYR4)EHnh4~3 zj?JdpVpgc7Iddz|T5MJjy~nqhV^z5Nc&rxAp%s_r>18dw=)N$6*wPvAOw>SKGu+M5 zwA7qzj8}sG)bxTBRJ93g^KxP`bbcmu_N9vP{%;$#OUQR+#yAQXrvFGWPXR+Wgo7C* z@%FdjSR6!8uh0T#wc29*ECcdA3LHJTi%X zD(mP5-SEOsJzR^OBF{lEZh-BrAOYsA%boro4|!=~!?NLC1C_!PjQ0`(z?iwOC$Zg@ zBw`Gqj|ECxR$fT-gjD5WOoyy|0H)u1w*MT(a!pXvPb2&pnkboN^&~8wyXI+p8b>mW ziwbiWuhyD$`C#?3jfGO+_!OzreX9Q$w4|;;r7#FjMOUc$G91Xw{0UzLTJ$EKic^^l zi)hBsqs9`vkjX_^Be59FJb2M$TYT#ngSSS!P@km~%v+^3+=f&=kS?y(q>3Ho;|n_l zqL=!XhLEyb`|wLyay@^e!(&6KZ3cx)E1>lCOaI5caUeye-Z=N##OR}l^|Khyz8H0s ziMvbQb}V<$w`Se!Bm4AUrL_<@U3H(D@4!yT{?=>0h?O4em@eZ~)>~<5(1LIR-SvCc_$=y>SSjY|Qg^HoSH3>J68T7G&Rfpe9I z!E%=_rpf^cTg|t*9vEz&Ge&aNqkjA3TH*P=BErB&=h?k^rV_JQPn0cL-6?Clyh7i`K zwoo50;f7WXc!WP9s zCfKc+08bh4)%|cJz-KBS(J4K@H*n`{HBK3bRx@d+w9CEx)Vs;L*Rq59QHCB*P$F7~mJs2oNW%hz7#s2++~|;8T`fRM{e-^_Nj+zn21wk4xuPA1Iq>d&_nFkR39m zxViFJ&1=gengUMc=NRWU%t@YaNd;Nfn-nf~aV;7w+9SOYE4%7ZtA#-lLn}w9+Ci)y zax2%{NWXa1;M>+D1p+;PUx(~7<$~qyO6U8^6m=#}f0@2|(C}rVcrTGlKbZsEpMatXCgu$sW^RdsOWdq~38{Z@aHB zlAOqiYT5bZssgy_lN#X^F3H&Loe&R$+89n|jtKqRU%d_N( z7ZK|wVR2hY7U9ZIFNW@6JVkd&MGFK}5^uwLn-}PD=|7j8sPF+m%+uw|7zZ?3Dv|&N zHU;+8CDK2TZ_2f=mYF}1n|_f?e%rPn68!cG+Bo0n*2VI-wClcFD@tZ_3H`f#H!#!$ ze1wbj$kxCuf{t4OigD6Tw$Hfy1qy|iVe>SKR|!kGZN3frd!2FTafID!;plrueVV*gY4>Y4QK_XAYjc!hlk?6gCLAVlu=u00Gg&DLF2!7D z+E0wL_Q6U8!;ULS>rHI!->#|7D(|E`NaFRHHYp#1zP<8>n0&^DnPEoRo?^R%tbZdk z@We^O{hn*f1_Ob09AW7O0W7^;QkkP}40Nu^s^q%gDtB_M%YD*1`w>VW+Isf)nPYqv zQq$dhDsh$OmFjU)!}0Fb+)P6EeMK3vVM0EGPG)lVw%(>`fE;Wll0}pMG!}^zjo}5y zHCs+J0uUArznjFi-EF6q!l4{HuApoXtR{)&o$Q7=y(0@I>9Pra4M=OHyq`3&Tnq8N z*H{R!qmw*Tn5i0PrP+6wI_+`F3biw0AAadgiLWHk0!x;G7PN~6#~`D)<5R(TLcYb; z5l|jaqX(hfNhs}eCAhT%Yb5D_<(BUK#*q$NUw^sA!kEara*@2hm#M!Y;|ZajEnib_ z)m~eDzc{O2SD%?omHD0+m-pTCDj{rqZfuX-u=1|H*cE?PL&`1Bb)SxnhmSMveK9vH zG7p*1c$WCh)jQqM4}sH@t4xfU0!3y;1cQDEP7Zvq%P{tyjAXUl2400Y2S~V(Qm(C9 z=^i=SYeV2hW2Ay>X*Y6Nsl@vQEoArv_BW9K^#d|_^ZY1ITj_)@Ht6Py+onMW;j4}7 zfRxrGml0@UI>GX2a1^r2Q5Y%%M0}q1SefJQ-{VW9Ab+2}Uky<|ehiFaRIcJ``5N3B z8r39#@*{ssmJ{m9ug}0B0|nui5Eg(Rgc6nRLWK!n1ep^-M6aTIfk#OLhSIptv(A!K zXc8&&v@$=N`{X+-DIpf)9IKy|5L*V!$#UZmK%io4S>2X4&a39A;rcL&L&sLeqc69) zbalM)FgKtelA@+M^Ubg;=w`g^+DdJbJ`sFqTO#VYS#&Xt&GX@?;X7~v9tWL|UnWY| z31A!IcbvfYJh+S#?hH@vv(Jrdy<&%lM&_7~%*=%Bc3Sc51G*vuDtjBeElG&4b$jYa z+t=(Q`|%Jr4=F&!Hzl_@NEL=(Fe=wKNi;Hw{qp=Fv8dQWMozE zeu>{TzFVh4FWQBcop6H(i^Rbqn2eX|xMcCWRZQ*zqjh87rY9rilYPa+_T#CMXo_Uk zUs-(S^kibb8sUAY1ZqQhQg@e>u_mRn-lYY#X^%vxxp z?5J(hTM(X-NLkS}EEKvMw>@9rK1}g=Nzpkj{SY~9$0ueL{fO46h;yn4~ ztd@$nzXGUK=%Q%geS+;bz1>WoHl?{`r9tl(S0Jelp8zB8WqV-}qE6bIdGGplNoer- zy1|N*pGWw59Uv7n(ei-EKJZFKBIQNa*f%Vcl&x^xI0_!QIHIp;9G{c3-n1oKu{l-03oCNV5vf}Tt zidDRp=F$r}G9k8g+KM-@{3LBBQBDRj3f1Mjp|bqsaPh0cqrGbugrzIEWoDKn;k7rL z!(9*bxrxy3voxkVH%j_uf1o^X$e%0U{k4WC##9Oo&JTr?RyA48Z2Q1Xk3wq)>UbJy z6=DdifIIKO7mE)XC13L2ryTT2T1kY1WHl)b z0yI+(;{x&nQw<;N3f^APmt#|D0)xwYJLx|W%GnV#&7pv^^8hizd)Fv13~GMf3btHfP>RYQf-f@^H*k0 z_+~Z3Uj-`>W*L@R2U3TKRrX#f2el#)`W*WpM)THcDcm55=dg$Lyzh$n{!pG-Ml@qW zuP!ZwU;IV>CflyhoK$o)5l-cz!V{9EotE*ettj}b_5vF-A@tL%cA6lHI^vq2LbL! zhQTvxlA-_Ny`Sh<>Picz=V%IC&zA*pL%N%q^wzVH0wq@12$6DRR~*DMbbSd$ zI{|J7A~CcxO~cpUnnH=u&wFQPGC=vc`^7AHKgahO@-vDfLX)#wKLUM*?+3jy>Q!gO z*>)g~cLS?7Y>K>UA>V#o^>k)_LpB6fHp2XAG+J?vb?&Gvj@W7Gdl2i2_K`k`UtUMvt& z0#iDY$Ujn$IMoH)l=_5BczN0PM1Cll`M@qL58fF#sU#yu{{Z$d3lRoWg?xD-dQ3TmS{n~a3e$ATtxrlqjx(oU4J!yU6)B)s5ZFkK*#dQqa##SPa0oES> zJ}i0sb+UvadM{7~03)6^13m!V7kfm6LmRDBO22qQH?Xc?$dtB+1m27^GCp{y zGNWR-{p0H3DYD)dea>7g}7Hxx2`L@ zjDsR4-qHmyzn(#EK#USSFhpUSP!7@IRfNzzkK+@sfGhGbO^}G|ke*3a`IW;L_3EsU zykV@B?k*c{_F1EY_7yaxfw9p?gOWnC*MU;69%Lr}_FB}(FWgOFb~k*{yFK5SHmLK& zXZ?GA*Ak$*E;2~X*NmQO4yBU~KuFzhl+=cSp|(%r^Ytc0QRQf;XrUHG5s#?m-d936 zyXv`XR-qYo{@J5KU)j1O=gjaEp;i3~RqdIlqTb>_$cn^qs~Umz9vd?epRjiU$sap0 zkZ^IOjt#K3g;cSFa3>b#O2+r~nC-O^J@$K6;jNZy)Gi~uLrm`R@4JrQr#hxJ_% zvlh6z>x#*_*VTI%rp@2g!vmeUjCbFaUdnwYjMq&sWP@Sn@$gC72_X5j0x}lO&ougO zg`m{xXHfkoqx4&NzN!SaH;Fsn$4__FDV^Kw!RR~!NQ-Q!B^$2(==R+EWTYCi1U_Yb z?gh}!H(%A?7xQ;{uR`j4KEUM0-4N1H!5uk{Uj zC>FT4OgWJhs+fbAa)%fI>44+iIv_q2p0Smps9(v23V)JiZQ^wNrd0jCx{taETF(Ni z3Uw3Hb9IPbGS{KOw4SE9Q(;=Vx(S(*SGovFF>iOYH)iW!sZIV8Y5YIn#&^aS6Xu77H_SsLIg>4ERG(}6 z#y>ByiU}lJ+mrrQ`Pj67^SyX^S(=DD?bb1EqtGE`t|T>Ksclzoga5$r*Tj- z8y^^nfwV2p4>osI3FAK3j{n@()O-w`;9X{YOXx?hnmxJ5e6{geqwbiPXX0jc`R4AI zz`!qbW>ME6ic-&Ys&AbItZ$yLkCpk@E-4!O-wZp^|yIMq`cT54;xx z_2@^a7QhUP! ztKqfdr;+M@4omQ3n1&Lg6eio~-9$q9jbRNTxIpXzUeoG%#gJMJ%nxF1Ae*8kf*O66 zO#quai@!}lpvTYt?QEb0t3&!`szMQ*DivDX$up`^psDR92R;3LpIVj1tnj^zuhDrACh1g7~x3a*&JP`I$2#-vuGEeHACd*YVN z{=-^(u5aHxR0#jl2KsoR0asIQVCp=`J?cHv3AK=1y8su2pIH+MdZXb3_T@U)zwR0t z!#_Uo`mxM;0_gg_VIX`>G&oq))pNr0i&S8K?t(jMrT3WK0B6Y`T& zmIyVf%hjfD70M(Kmi;=U+NSZ$AJu$+2-Ap&CV3`(z8Flpi@UcNtslSCD$?q;sM?a6 zp!u_P<7aBG&BxPFQshTJw=-KAp8H%DF;vj4x=|i{=P4Mk3vp~I)3R~>`YXX;r=#dY z2Yj=-B{{X3#62j>4qf=1Jrwdac7RNUon7^@GYojtVYtrwwy!-%DDxlDhY4zWin#k0 zl(bNj1@bzkIOr1npu=Xb^a0s7pmHetR?{`LZu?zGX>Xg!n1xTU4yi=x+E8C~Os_Q9 zyAVO)G zwCpcBH|`sG9BAm;Qv;wGbaxN>R~~;sS1=fDvAzQ9X?wRvJ{X}g76)ws^NTLnu*|i< z_Dtn>H}ZR*KPaTs6j+lroZF0Df2Gu!WK`;#ZYLftm#}b_6d5XDLw)Z*toDD=_g?Jv zAK{;^ee_lDS}}fdqImtJR8c`$0&jl9OD6M`YNL4&SS+XK7I%R_aA)7p7QEYfheah< zMh1jlk$2&(hm)eT7~&$H*)9BgK?dI$3#L7#w=q^%n_3L=k`kwrJ4P&X?_@70ZFI7H z4<2Qt9*_hfnN1Cf06#M{4tr;FnZ9u%H77oMX!2_;ynoMzihPk4aB@32kf_3({n2`E z8?60*{K9N&{XqSRyc~&ZD}NwF@;We*k93To`p*iTn(kC^(4-B9TYNn^I`@qUD9z3- z2$JNcLzkN9-owEKyTB1K4`tLAZN^l=&8?Z5v~Da(jC>hFl0|yNhbS^bUGVc&CBZt@ z$(1?hmpm(lV}y4=;4zmPMzkI3Em?Rm^g{y^m@9+V4y)4YQ|`&V3t@a9#;?G2pmLZ~ ztv9VU`Nlg$s;2y|qnK&-dccvQ-`3vWyb7E?S<56iAMe#-DdDM`9UCB(mRhJl1j66z zma=9#RK8p$#f~}ZNNJ2I6^hR9-{&aQLO%8n$M>XIPT?M$dVk$oZN*>ObHb)*ZGrM> zUhP+WKC6GY)->dQlHa351GLCuA|jkSOn@q^qpiC~aSIyhd!tj=*2+Ishz7$4{1|Z+ zSg=ZG8Nt7{8|it;iOb0AUzHsdH}4{DTml7_gTlB-87IpKWJfwE?`OoebYL!vYnu6{ zo=fF%p-qkC$ze!n-F8rO!N2KumtFzDfd!Hxs z5gHrIV7g@<1!!a{ht_#ul&klxT?kB)20s*&p7QCO!O9d%0-{Q`~--o`M@S zm;Q7GI!fcMm)J%_`QR!H#tOF1bOSC;(PfCAq>Ek_oc)i}0-tZ}Lf=2!xd2+bb0V;h zyJ|A7TgsB9bt|05Ks%{LT9ElS14;ewZLgr%2cRfb(HR~=OVB+FEf4dBcHys@q^a5j zQvBtZ$(uI)PI63SpSt1nSE_{Kv7-@7pS(ZWE^YTvaPIHZoV*b>e?o6ZeEQ1K$f1t7 z+rT)NOqII)*pmW%c*Z)cngOP~9zTec5nu6pUPXFkA4r3SRb`u*cfRGZuoBKF&DHgc z3I(ke6v2C(fov=9)7lA^~$`*Y{6Je^GHr)!Q<6TF^Or z=M%BxCm&(ycBEvk@LpwV7`E20u$PTi2VV0g8$>=tM>0)F{_ zNESAzkLqO7L{>li}A1U%c) z$h#G1>Spgu12dPi;Z|I-VHdL3t!1zNN+C9@- z{gZj_sUGAw;T;B0?edSFhN4r5we2>#1K`ES|8G@el%oLi#vyg&juJam!hI8KJ1s(`2H*`HZm zd#rF`V6vpt^&0FPPB;yapAKi#$M^JH*lkBcmNshJQxusK=EQ`#qpDtYZi|6(&H#;o zo$(-kitLGJzBilqXoDl-ivLe42Yzs!;DWZK!XnWaF;==nMmT5(K6Fp)7U-=K86n05 zyoGO03ld-Rufn*0LHNPGpIB&~fmvE=hC8$qo7C$r5-Z-R2N7}XiN8HZ{=>YjDe^et`;5f@35po5-Nc3H1XLNE!-4IMpvgt`se$d3 zaUVV+F0@11iY4*W1KW-dY%X@o8hpZZ=--J1`RS3 zU%|yb2Xpq~!RQFO2K+<#A9~^5tG_(0yrE)q^8zeQCL-8tdJ6QVjW!iKp1!!{Zo0d5 zu95M-iugsiO`3Og)EKU*nRX`1sQ}S{s~zTzBter`FvFe>v@J$2z8ZL$2Dx zoQ{iBB8=h-Z3Hu5rMj+93nk*q0~B>wM8FXA+Y$Ot>#x(t!36lopLzH0laMMAv~ES6 zjBqs#I&4YLSfKk++G%nrk5UH9$zN<1ed%7H(H@lyMT#vc^`N^Jw5J|_QS4j<7zbC~ zQ^%+t-r*rw^@6TkM^FC`Q7i~@Mzf7>_`mZx2^!tEH3z(*NAC&r;U}hI6@f+TgACp6 zosG3;wiiEdcF$!waIED5p5#Ef=Sf1`PIxb5+F-CnvaRr2rHa{d+sKfrk0l)f;D|G1 z<5`--_M`wXhW=+get<1q!%sM=;cj+s&sx~qR&xq3BKld`mH zZF3kyO}Me~PFQcj%{_%}CeW2bWX3^m1%(i=xOgg2S`&s1B_ejkaI$n~K#usIQZ;b+ zM&1>`L@;9{WapEdt?FRzk%BPNt?A+!v_lp2GSSiZ_H`m|37>T9;zI={%dtnb4g|Uq5rT=>y~=frU#^Zkh4BTF8`eij?T;?#vgb$+ z(_vK;pUwYhD@EXa0BvpjuZS4jU%r3Y;+)zMIF$%auX^m8lb_`359S|b;Z7n+l6y|7 zy(Rk%i7+JpM}97-_bDv5W|Ima;NzzzUzZJ^K@c~dB_tpz=a0HaJt61pj1naCI@rPC zH1ofu9H6r2Z{s!aootu^B%;V)A7F>_Pvs|p4ty+~;K(}99z`Jgan&E}#HI!(ZZH8q zV~kAIj*-;yRdx7kJ;ihtzRL%sW@P*7Ouwa-21jQ{ZWlP$GV&6v+;9H3Qv&=IJx~T+ z*(;2D2Toq9hF0c*0?GFM4-r-0S(*mh@_M%{Hs9HX!&}NoB~w5`%|T&zLU-Z@7epaE zucP|n=vC8p&#=Pf`iYzOI%U*aTVD~HCUz!LI(i8iiU9wS7yZBANe>(_cx09W!bjmn zH_W=%=5P`5a{_>bM76HZV=m~S+bkp8+g{Px-kHugxx+o>306<-M>~5DZFxyXJis55 zBHY(RAko6-Y%m}1?dxd*72xPTQ1&*(2p*-s`ntim@Z_#CqNw^=P6v<>*V;IN0nY*# z)S3os2ls)VvoXqA{IojazzJV#Olk8|MLENw!zaDo0~uG}Tr^V3h)#_L4l)zOk5u$~ zw!PGy8co0os9+Y{b=7o<5O`|!WglBjvO~b^ z++p9;cx{_^p7peY669yZTn3$n@`dNJr^OABy6SR8G%O}8ky@s=Z*KECiC^a&$jwLx z_|9LuekZZ(MNSE7rN!r&lmtVp{u4U+M|M~HFXYayJldKXKKQt)sEtcZTG=KYNOcZ{ z&&91XI33&F#`R2uIZ?Vp6!+^M~ew9ZAVC+`_x<=`v_D@#?b8Jhync zV(yn3W<2nQ5KbP#mu@PN;E2=5psa{yG!>Lp{S-C9g&q2kiwezWg&`KHn<)bY+Gd1q zsL7gBY=WloK;Ol2iL{T;7ER|24;qA$pX+}fy=B4OH7I7$*(u(3C)Io&QRLCVNNkwk zL@guO;!lA8DV4}E*GJ4$BI@@Zu#UQ@f@bQt9dPRA&I4sYhsa!d0|r>z5)4h@6)$J< z-#p116LZp3=ubdVb;j$qwDlFZ5ZdG|YRq5B>!NCk42LQ@+L9K{ZRyaX%D^Jx7(fp? z3sTEprK36{P)1Mb(L_?Lg=m5xA#I{3#^j0~fZA)_0`?9z#;q@M0q?9pyU5^QjceAT~#KeIGajPkb(?2e=osuo5}y?uT$HV+uobG}!;OA}2JE z`kjXPqxdpj9Qc>|2oyWE0lyd-;Z)G?o-~q{AXrmV2)9?7UgPf~WNx#MzT26b`w}gs zeeR>{y0qTtrJbv2=;S%9PDhbH*qm&uMF(3(eE-kO0MA_KEeDveZt+=qoqYl0JxRy+ zJkzD2ZmL6pQI0@)a)RJZ)UEE0pU)O=Oq*({p)Ysk%XA;lIv2S7y~ODRno`}<)>{~; zSR*%b{ziA4g&1iLCM+-5&GhdjkD`L3pHvMn2ni?i!WE1XPGSOj43iofz3%(wXXqkf zD@M~+>^5%XeEao=wVvOkirm#|9mSDyPR3- zHV6rEI*s;2`&;oMo-z>xEdeV#I~3O8)&`VGzuK438I-otQkyx*OE9{gS6v4W#Pv{0 zJ~uJ4TCCWNVfmX=MK07xh!D;3ct&>OPih4K+_Urof4|BcVp*eh5vdHIImKm&a}$a{ zk(QYf#xC6TbNM5ZJ5;TlkC??H74>NwcFS5VaeW3CVv%A#bI@_gd*=t=XmGj(_syYS zLf3&Dx|Xtox$1H#lXcNbZ-8pWya&`+oxSlmcvk5D-pbHM6Y5dW+Z)t@!k{5xoX%iy zz{Yj>P;Tga!86_(J#@Y;k%kmjv$!zyVD3hP^4pJTBvWHl`lScB(5m_cL7e=B8i8kG zV1t14j)34Pa4k;%eF|@}j<#{b{hv6UM0)6kyF#J)0|6({dRn>_l7A2t0L{wXz$RT& z(eabSIa3GIw_9)V7IJ{1E-m(ucvrQOTX^AHE0oyQ);Kd~ZnP%kCB`QL13l*d@ufK8 zRq|)GEs^=-ZB3bmiD6ZFk;s{wI$N>Pi0T)v3Q2os6^-TJgUz6dU0;nPyKgaxmq%H6 zL*AWJdu3e{QlT>E?h)Nyq4 z8Ptlzxp>eX@n}1}3{qC{UH!^%C_yFv>}dC{w^N~_JAAl!;0mFNEE0gRqgB^d{r7ez z1iS5N7(TDQ6TY&u(S13B#6x)JOY8GG3iAYCKrYp1#wy6zcgB6wivno46Ouy>-}clK#AQpr4|H0A z*5o*Uko3B0rkMV6Mf5ew-mfC>g{*wXF5p^Vk2{wO>+VjWShO~*ix8hq;wNtNyM`w4 zs~O|=4<&8d@JBo95bL-4m7Cmj$o%&Yf*mqcgsO+o`Oi(6B5wLoAHqmGzl^txUf)WX zwPzPQXQkYf`H|nl^v$i*wl+hJVVoWK6=A?KBbf{h8BI*!zn)4f?Ksusr2t4@bYUeo zt_n-xzzy(Yni=|jdz$*gwWDbK^6)|9WtebQRsa^+V}sy}2vPKA#umqaVa6^zU+Q8+ zRo6x*rQI&QHzJz$BO&K4jD#+6I3XwDPJCmwX6y}h^r0U}12FWks17Z<#AlM=J+-^l ze`ew6QgJ=qN8PCLgXKO{%4T^a)U|w8n+p^D*cS$a(m0H{=lqc}g@=%Y9 zh(G?<&%{M`EQdzB=1&PqC;+$+P9NRK9|*3NeG^M0x?=HXx1Npgo+g*nt(;8-z#PT` z>uyJRt2t~s+@jn=q>R;j$&4~_By8-e^AP5cwtP3&X#RZ|Es~%2SRuh7Iunx!0~*`0 z^c*0;o1K_)s>5?TyCp;p;oDV{MX_4k>=T$py38M94s#hH@#7X(hDzMue#s zbTUf0i6=C=Fhk73ObVA;^?T`$f=T!x^w*YXs4Cc}zkd#;O^vd|Uk+AeS$4PpcZ&Ds zw8)jxP9aShm3Oc|$U}Y~DET5pDAe>ZaoN`?vk>Pn{&LU~pAa~_YTR~g^s#@9GK_lE zTMg`T>|a4>9ksZYfgg-sAe4$bn+9AU%NrU#^OmATh#_7=Ml3Y2-LRvB{WHF-Z7x4W z)J>z}J|F5>kF9yDEm@NwXxd%jNn>1$h{YwjZo8XoK|;dPgptG6QHB2g-P@^-suv}M z)n00DuKj=19-Zs64y$0fE?taJH_ zl{kAwabl8;Ft@I~&?7U}(iLt@SFQTcb}A|#X+Nn1U}y(Y_gnZZjMtifUySO1XI)`e z0C_?GJTTGVdkD=J5X`tvOzCM?BCMy~mXVQoCKjGDx|Fxq!h!E{Ezr8{VAjUsg?HT) z&b!-`IU4F}MxHT*K9sUu#%8gtAXFJREtEt5%pwGikF4@gfkD8IsuV3OWoy}(=}$MX zmRAn7Z|^f}>{f5+U_vQO*!sxXwi%7K03{ASg0zkjP4>@PQp7H1Ji-+UBg_3Q3U+qx zSO6lzHSV!c|K)KG8_oYaBvQ8^5lX3%@sY*b;6kxo3;xCG zcs8qx3+twNv-9RFh-|)~t;vU#I}ho=)F^NwC@2(4&E10j__cv$pTh8xLc4jxj0NA+9n zq3Y_T#ql}F(^UQm4vpL!`39Hu~Hc>kY%7r|4VyydCY~Fz42sztV8m!a~v4+jaWT#{ukmU;yKbxSt*G zNkQDvKFHjgrh`(wWT6MLOnxOQ0}9qLg`;AlDbb4+SHONLtNshzddO2YIic2jV3XmU`WpUg@D-TM_BcLjz%FowZhM7o5Mc9j>#g;>C8UnoS zrbaRsLH9KdDv?8*h|}svA7!8GF*SW4w6PY&Lm2#(SlOXOhaP7mdi|6lNcrk>3)|Ae zKIc{Aqeasae`@Jet~yyf3@KnOEfjR9TWxoAwyzlCx7`)DuwJuYhDf2(*+(}XYasod zu(O_5ngaM6ud37s?rq;`oY<#gCSq+n9LxJu@02urCice}nwu@B#pQ5L9Ea{S=p1Eb ze_m$xRKH6v&&E65yo4$ls0UbaXCY|mU|hfyz~ci;HDXEn*k^cLW697d*#Tu8Yqu-M zha`SX%bmNF4%i{YnRx8eG5la!x4qUKls0^Ot#ltu11qmTTr7_0Q1yMVvX}JhaCeEa z5_jfYp#R~xqh64z>L$Z5oyW>#Z&OjFxtZ(ZzQmcEKgbi9CBvptx5bW+HPg4Kkwy3C=Es zkzH@a68l3?4N2r~+)rD}7M|NQ=;nG?07eLk2uYL0y#qydqz`NTzl!fe4I4nQXF@~3 z_xkT(Qu3LbB~z^??rs_%|1gu6q}r}!$*Y~nm8|QWi#O-*X<3o+U0ud|3+d5|LSW$$ zdKwY(yV4B$ct8}yVX(8hGfY?wWGjARbJL*=ns?od3psEWRS`y#ksTM^5j5*CScmHr z^g-qB61UPEb{QO2p<6RzC{3)Ek3Gr-6A-s(ZgV+lzgAmgbK9=b)f3vV;c!*LP>NNR znLq1_Y5=DyTjT!h#ehsk!;nn1bOnzl$o?mbly;jS-W{^ze1f0o88AwKG5^BR1^J1e zH(pLyEh{}l(U2J%)1kCww6%XW%oAF<7RwLXzFsWL++LBoLtAY87HO5dnf-LB`_}ta z(G4oobPTHchR7M+HnL&NFb$-0;>D-i35f#sbl5H!RZ z@Rx_C$q~#>1)sIys$J}JX0SN(yxbhvq#ZazG-rUO25M{uxPa-m)Yh*2NK z`KCkF;NOS$vM8XO1vT=_^9&&g;>N)#-gclU04~lU2mr0d-Tg0@b z{2UOIv}$8L!_cwW%!#7!OsT$6F+Zd$?^imtn~KN7X~FfV*N$4O;UPTbhSNL~4U$(w zlMlMJ^)guatP}V#_77Ii@ViWw4b9PT0N!eT&Y0xA0}kJ>4a9Yh2ht4+!9=3AQ^x}durdm@z zG)*e`_#ltpxaTYA<+zTv%Curmozg+kG>bnf2=Y+96~yL*m9fAABb>Z+*z(NveAAt} zRa&z0H1FQHAWjt0P_kN8<#e;Z_Nwo%wepPO0M;nqsVj|_a{_nBdgNV^-Gt3qqu@0c zr$jlm*<#3hF=veWi>_hWXGa&t&B6a%rAMC_eAc6~x*|mJAv2b{X`&(k9Myr$x7wvO zRzg<3vavuzMJJOoOoefq)>dnY;sNUyBQ_<=z5X!kOX^@Fx(21Z#S{jSERjp}*hK`` zN4Qqw0rj9O1Uqnv*rbl^T3-p9U)!da)_m<~iq7X9v^J&9-&AS@Lqwq(-Mpx^yOxbJWi{5s<* zWJKRuY(^1UTurO}y>k10ILk_Ck9}oK61%5(QdziE)~0E(^Javv3mmT4v@b|V+KyhF z1jjMd=wS}lvY3g13^+eQFUjpiqcJ{3il(leP0sDc0G`AT#WaZQ<6%6Daq)6CmOOGU z7gHy+`xYU)M2p?`e`epNDkaP(SS=c9fAREER%p%#f1*1MKll9o1fthEVzFmwsfz(C zK$yTUx`#c@(E!%8&UI?Fc-fFyOxKJ#s~Gw3>f4S)hNl^oO*Jp3)_-k(1hC4|H37t| z$D>V2X9$qO5E4l?Wq*P~0UiG2tqr9+)$?{{Nr{zeSs&vTbvwJ;)vmk>U^^9Xr>al` zxgdhvHa+ljZP@jGb1370BRxzg&ecpgEY+a@azNY3AaWErIrg`x8UOgvtcB!uhsFds zIqJeg3NP=&Da_c~ONX9%A7lW4^R9TAj$Z zSb%Du11;V^2v~SOu1Y>HyYO89wMENJw_qmHV87;Hl_v!c>`pp&dv#pa2nrF026`st45`&q|Z!A@zvHtNP9cb}eM4S6EE#Xs#o zCs;@<72S&Ew`%vCb2OIP>oDK#fOlfuG6UDQ=OSWjRGH0Fjx1QwV3YcE%C|!Y&hD9R}|dfVL($2Z*_;Ep>qj&s?l|wifN4R1hlSHB26HV($icsp&m^8>vJU1cfIS6+|J?oHmtG}W7q2kH>y6xiA zz^bzKs{yN8s#5{8st*nmzJ4psKL|JEyJvX$6PBXmam-sh&G0Hqe)^wy5_ojLO6lNH z<7I|XgWKYrJaCTZS>2~mW0ywPt_%O99S8(-CEMEIc(Yx&%*qoce z*bouhZxA5|bzp)OA*#dJ%mr#{>c43eM?!KNjRksg*k@NI-KF`NBsHJSB9t7Cw zVdPWg`CmJ~n2(!Q-M_fEp~_G(hox9#!~``uwh^yV3=fCTgLluD)*#NK!sKW#UZ)x$ zBzgIW!-QQP(1F0pz%0T5O=k=uvC?Ail#sA4$^P@GR|K8i0KG3;+gn8_1`F!mcTW4V z4#+uuH91Ph2~=8t3HDq2d4TwZ4hcGz?%`hk1y1TtS< zaAC3g2g0Cc;O43j=jeB1{)CN7oF39Mn|WJOqLYQrL+b}+wNAcrHX^s=Q77b&pwOl= z5$t5+qPY}-$v@Odl5lOs#s&qpW#VfC1Q1iq!K7Pgu@}T8G^U9}+}n=G$S?j9NbIj_ z7#c{lnfi4~Ma8$dp>1D|?e7PZ*zP|q5)`C<6lET&YgL?~LNBx7w50`HgmBEVAwdO7 z$T_Rwy2Q$6=L!=c;3q{;2}l>v3&i-Af1`qxcZiFYuDr#lvIwhD%ecu8ea)+Dni#qL zP#f?J5z_|P66)T}I+8XYP6s~BY;c^eqWB-CPS?&e%jZnhY`>eLoXT4BkonEX9M#f~ zJX9X7#`u0)F{BUdcvh&}0g7c$F%v)QKMZx+?&n9iw+fD`FC~5K)|(#9Ex~cW4FZ7= zx7cL(1;C33-vI_|4Ccg z;RChqj)TNYIk!2yRKnC>GO05Bqt~n+G%5xi)N((>6m&au2QU~rpj7O1+v9%5^WKOmY=wi4 zk$fmm=yi?FC){w5mQ~RQ6o~Hg@h9a_hAg0f4p}EUJq8LIyi8^p4`KPTj*-}u)84le$o&0B6{T~c)|QQ zS_*e~{P%Sg905XN%|Qv!o#F1_(1&#)K!)%E8#rwISR zBqC*JGVkE)&)c_U4@+dWi;?PA6j#Yw^1 zc;bGl8MUpnHZA(su4}55b^r_Y8be9|R!csrqb)7LlvsC}~Q_LbWJpLA5A`j95F`=iz?cSvbxf^5KTly;hcfU@o*63D4^ z_cc!2a&e=DlJ_-e61n03rlvUJ>nem;M-4ooQg?Q<*d9LNJcW4@Qosn8GcB`o?Y|Kjz17kUy=AmX=Cd#tlu%Kll|I~XxDTDtjgWz|fMSck z$*1MAAiI&VzEK_lBnC+PSre3X_MxQ!(vd z^u`8IYYq90j?F~{g-}iq$T5v7q=pL~q4{7j7|e78v(rm~gW=+zxne>{NGW({J4ha@ znBbB{?dv?!q@m3lc?RuTv#w%^xU2cP74=pLX3*>E6+s5-o30flH7?tpL|vq@#*ioy z0y94Lf9LN{0F<2m0BYQB2;#WR*vtEbe6*FI1Uu@-&MgI?UT5eUinT~OYTaEy18E=U z`no3~_fv}Vmakg!Hy)S_);)enqJFU|k8NP3)Pz_27@c{Ugv zjarNtORZ9CoG>`ZLqqf9WopKn!AsY9?=$(LZ-MK(sc(t-_S{7>-2^;b6*cXiWa5up z5+fppUn4y>Kz~DAJ^tcvBI9#ovx2DiK!Gwq9yvPoOQFC9H{pHsr7s%} znVr%16@Lj>nC=;uY317Z2Xjh818-DJNZ4!0pY813G}fpPg>07RqjZnUn1eLTQ8M{E zuuBVLBksBvZ_W9A!QWB2JxBZRun-v@vM; zy#66g5I5(c(-n)8f}Cm4TNxz$hs%$jyqL+}tnB z;3dSwdTtay7fC>#A_Xdg6C|5xO&3{8NHTGl&C}Jb(VG6^6-_YE&$ zt}Mhi?KhG455Y-v$B;X;nUYrqx|9loWQ;792jTY&P6vqbakD^c1jLmY*yk?>ST87K z-EAx4cZ#3X6B1^Z-U5XYD>g6Im(DugaU{rkB!6KY#V~d*=O?obtPhkzHb66b zL7anaGt#EPcw522jLaGL2&g=HN-tq5Kq`;6eMU!PjR z8C5b@e~KBU@E-BAM(>+0sJ7X3xZ2F94CFrK1ZK#xr4F@?1WSyG){qf42XDeUItS_q z{v%ZZkBd%+Pm@tZ1HSB80Z7X!U04uDzGEhuoq$j_9M&(-P@&ZOA_4|?rRG$t;+$-D zL3;5*DWjK@IW#8B$(nF?5`VSvH=X!@X~qwlD}ec1X6yxG&((|EKG5Wl0t=WD@Js({ zH6v*RVapuk)@c|cL%FTP8Hv%Yd0gf3!*hBP!TcnIbfxqgEvOgylhprTELH!rI$29& ztHQ5e7am9F#OrISZ5|uIz}ShG|Cq}^R|~&SiYB-lBwNb?qe_tT%9vt(f^3#;WEg3` zJ1{v;ZsB5Ys|if_t>)0w{}<_4k7fw-W<38Qtd=d^+L*a_2L450f#fLxx*VK=wNoD~x@7P7@K z9i{_sxWzVud%ra;aw|vtoNZgE>vY|6#@1%yW5v~jx!uNdl$HekX+i(A|3(WsfHx>? zG$NSff&}d-Es@EYK~;H6-R_fkxIHlW6^S*5s~F4n(tGar8WV74B35l&7M*QB)7bP< ziEPGq2$oTcb6;rwn<`ustU2mPbbtXhB&f`y2N;hw4Q}SiL749Oy~y;*@PO8Ey6Aq6BiQ<}3^3cHf)(9WE3US; ztV;mPaz#m@m>gMQq(g3tBEap8MJkqcc!Fx8zsrK7v_zc;&K+N8G>Xs6=Z253%{hIR!lL8ZdAl&El zfVUSaGMMakYr55Gp;=J{Aedg${i`(<_;p}prrX4Zn(jetI?mN|iGwU%by5eG-9mt{ zRgbTwOy-U6b00|~{`V`IAk(DG7Qb|nZa{r5{vos{Dy(bArw4CBi<+TDZP1wWWheua zH`ieG>AaS)@AA}U$ZTbE*`oR^sY8wHR>FJK5QV~!Di z!pUxZujbzHgT3T)da3bsMOK(aa3FT-0K9WAX+OXbgpeO%Mt=<(ZV#XQXIVpOKZX)D z-PfQaUmEIgH=JJk>rc}7!1Te5 zDb>K;?ZAf3dC;*Z@Ocmf)5Cv6TmP@h`kmVsdP3*zY{!J*3wKd+YRpng209P9d~_?+ zKO4U8z8_rB3km16RyuThjvq#dag~fT@OkoL>m(3@&wA z&iMOhqj#en0fLW6r^!1Wn2THHW_N36^bYp7OGaK7o13vy1C{)0Z)&&rg6RLRWID>& zor8UQt^fry`OApjc~GD^@O~D-UZO4UmJ}N7ns;@mA?*#<3o~?U-^cGZ*~UWLtFnbj z>vL44YJ0WCPeBLan6wjc?+{;$@{YUoKq}YZx_6(Uq{V?JiDPebHhg;X4|xUtx{M{h z;=&2E%|1#dxC*x>9<&m8aQa_y*OZ!^eu9w2168q(407s)zGFst$?)EOiscVW?I-4j zL&L3Ao}>vlzSD|Ju9UMe4nuA&QloyvPnPl#;ReudN}nU;Y`jXTtK!Ic*9mZfm^QEy z*-;NcaE+Ck_*7SU_0?_-cmxP$Xv-F8)s7Ff;19M#VRJ^193QbWFE-9_a_d^t^FF>Qf3NYt zR?;t!Vb^PKH(d1i${m)if?5CJ?~$&m(9kYAJ>`GPtu}D&`_xuJ@DKdBf2ASCqXDvIk zT_RH%*wdT@nvoO!Nz6fgjLUA(hF8inndqzysYueEVLe_0Ug!m0epze?d z;^#9^i-8WWQXYBJmvozs-1Ka*q@m_=qffwI01bJ5mPXnweWoK-Pp{Z4drJAi$WFO( z-&Nm7xV1J)g2${8*@Z4tLc+tQDlSXPX|b<;nO=4ZJ=UkH^kAhSfVi*sxnPIMY+_}; zVK;B@D{?Mhd%4-h>&vI%wNv|o_Z~^kzbcN)auM4#s}l{PC(j2hV#Zvk2fEZ+Rd#;v z{cGoe3oW)oGT|Y}kv)?Xfr08{W(H^$vcaYBCFYu6bV+fX>Bz*0QRmD`lN*Y?j&LuH zq2tIfS>8LF6g@G0aCcmuc01p42Lv2@@r-h6-Kpj*Q{DL5$<=Ej*X>w@5SW#ZtELw| z(225gNTsVp@LPJUM%aSGBlw^S5C_o0C18LG$cSrll(w(JQ2RvJ_*fZHc4#|;XnXDif#+Rp4mk>`f@sUmp&0F?K9wHU*Yas@oO_Ya3on7P zn1SHr)dOj5-JR7MCA*>@O<9I!lDigv_@h@gOXjNbr!kZt1=_F~kL)|UyPGc5m7?tH z^+7)SQ301+9|r7|_*PMF*AuzP!o_z3?60l;>KCnKazF6g+guW60oeI$R0Hk_tiON0 z+?C6ZDLb&!ZArJ8--v2&pl4Qv1wGG)@YOu6wj3Q=*< zz)g6N*~nuKCv??p0It*Vd2~H>A3~gX;U2cjQ6Y*@Ufe>+E?tz{LE|WbCs)20#>~xkx9xC*J z7!UfuE@+BKBHEktdHvMIxE`2NkqgRI6%-8lyZFCp;41PP-iVAi62ln}vZv5CcF)#H z1v@7sWp!_CChzPJ6=GI28_No&2RNR>Cg<1HTJ0Y@j3f63_$kFQK`}?y$&se!NE~t% zgt|uYjM~6}Z=L4QbFf@VVuian_?Z4t(eY5PI;;q6KuG+MIvS}p_vJx!GfD(ykTHXO z?Hb*=%T2&M>$v-nnFuP9cSa^RrrepJ`kj3z6o8Gf5i}Oce?^~W^&)Vsa8dL1C9&9C{#G1+ z)M4GJN^b^A(4#$P~WT zva``_&;E?Jd>*n}mJREcxFHHKL${ilP=+ALItivEtqQ<&79EiMDraDxm3);Uy>U^( zQO9!ZEE-L|eMhYniCdqU(ep64gWZTA)AhtnJv8Bm~E%9|da9-~~7Z@4uaU*Y{@c21%9y!VCOwfR)ymzp>ZDkALAkX*KDXsbV# zzm;>P^xVIafv-mh0vskRBj|4|h$FkcYVifHV7qiKWU}U{X?7kV@uR(-i{&mPJ(L?h zESM28bEPOL8pNF3*CF~hhWt~rrMivGp!#u(7}uQ#dcrEkYxXQMr3w6r&+AS@-?5J7 zkp*#j5>rA2$LD@EZ2fzM%U1-w);upAi;4%;uJ9qQ7 zlKN>!%d^HFwg&J06}^}90H7I(z}o8_%Ieq)hn022zEi#^F7nPYh)WOidPWpM%nl(G zYWqO-j@ncrSS@6rkm4!I$|;ntdr+#Vokx35LS&e)VAcmg#c;-8_F6ToAu>vA+5c)_ z7By%J3FR-=@C36T-?i#ad;p|zBEUyXDiKiosq+c4!UTK?ip-nI*gR#YNcn5_|9GTJd1=M zHq6Q9q%BBI=jSpHtFC$;my|*!K%ZgM$PzQ*A83zAUnzWCwUU6NM9VPU?e(jvyeP%qX)pvb_+(z5dbeASIgr&VwbrLs1n9@$)zxxDLrdk*S7;d?O;S zX7;zw{_`ME9F+my+$^XGn+LR+5fu_bVkEpj{oGqlZL;7|T33U~X|agW8>xxV$E5GL z_l3jSya-|-ZszHm!tOTE9$%hv`{QDew3RR>uO%C78EgTeneMlmn|6(ij)1N{sC|if z!57Y0P~*lJjCv?)xZJS4qw#66o{qr5F@fOA8G(#NPdIko!tgSDJ;=9L4V_dUvNjY%+*YYG$OA#4Fj~mxt_dN(>DaVY>)e--I$F zoT!|dfRM{%Hzg@S1$JL?{QUX)b+AQEe5V8^4Ax8w0T$zzSd~9b3e|t4e9u4E;L1t8 zbjIf04Ex2UyK`L>kNWyq13`Z6#)&~z3xBsAv-PIH_1$q%$b>L4citUM&ZE>`0XB#C zNUf*N*vk0>J;A;|zpnnOGmpJyoxhmd4lxY9vBYw)zPW_nxO)acJUpwHGonVRYmd8| z-}oZRnX;E4s3R>^5Kt4dAQGpJ=OKoGG{&ABYE3sw8sXI})?@dNF&jQ>kfj9FtSOh}aAaFPy|@PI47y#3xu7Y9hhTFl%NM4dSlzu2RMw zCMDmGufga$Qu57M&6thLc8T(R07XYG7x$ABab66tW|+-)b~c=T3T6^s%wSF*-BSXo zJ0~90OLw5KxihtTR|sKtAhz79_SODhC;ulisv2*?i(`fei)N_f!b0Ft$(wuMQn(u~ zPwSfofvUssY)Mfg!SOGU_q4;FCS>V_`xZZ+yK?^O?{~0lViX06w1>2iS>TK?OALXT zs0KMsSeb$d)|C1B?RT9SHf-19g6630ADkgCh3CzCn(s72WLHbP4mGH;b@+fE)a1)_ zgn{;0`W)4=XO8!m)8zQUL*qWBtP{Zb5K3krov(kk8MYrW^)c>lXO1!Ez<%Q{6VzrH z%zqRb*x`Y@J^Tm%O$082rct^|%I4xyV29jACBS%3st<&aNidv24tDAP5%%WsQ11U9 zcPpobq9}WjB+Cp_WNXd|2@~g#We9^Yl8NlemWEK4Mwn}goE9S*b&M^$u?};|iKt0Y zVI*7DjIH~l&iPf&{k`wM&*Qk7>-sG3<@I{L$#0$JJJ_f8Kir0@jG%fUo95dGGHO~4juid9t8gkDHFKiWoYXrpJlo74($nGGro?k`j2-AyIy?Rvb-SXaYC zL&ujZyV0lge%E*QYLOGoU;gx|+Z*HAmV@w1)yxJ{0mRH?Lch!zEy!noRM;1KdY;>` zr9<6*Y2$KkZ||Y%`l<6>!Sjm~-7%TXLH`TpRbwhl6ohube zj@fyn)wPl0cWhp3=r1TQ=bIPkp5%a&X!{v)jtg`(tRj_@kb&OuImbFrUc-}|2i;6C zf{yOdndI5J<7TZ40*k9ahid&os}3~8H#N!BF$u3oY-??_w)M^t^y+<#*G&3C`PjFV z{r2Zqq_9%ET;}sr8O2>?Y{8UVE*((8uv6`smCCs&sHZYMFQ0KrebaMnR4lQqf-dQp z_;D$}=t^dL=-}=G^g)oeonoae@Tq@3XAt<}B9J5buZ+WUG}A)|_4<%CdV&>)klZ>rvAT9-P6ForfnkY{w?7*Q?5y%vl4nGD z{MLcW0xQkC?4bO^(AKz$jGZp{;^(acC*1H2AT5!A`#rk)#R?8lfOdmwv-kuWxw5I* zD~g)@T+Y)A*}DZ9A(OE*Gxu)Wmy$2t3|{`S<)@!hR^fkC47r9Hd%49qbEQ}*#r)q( z<@aKnph-tZsawwg+|bLWWYOP^e&3#|DE6>`O)y7^w6y$cOy`MdRivcQw>pn)ap_^G zF!ZJx{TE+P3ev8!dX@&!=*2J3t0zB9-YZf=H=;#;4#E!!60}#e2%as>l-Q9|9LU}| zr(O&SDi(GyD-S9w;}}H}Y%uIch@=dI5X(2Xu19Kh*#%vhH*au&&5axKVhVM`=2?KJ z^O$&9#ef-dScNejM2b7=IPvoje2;q-JCgcJkkBRPc`@8eyzI5$`QWCD6nl$(!#yw8 zW1J>Xtz(G3md$g;=hMV4Ot{8n{U+40yn%M8%!gQA!bvnJgJRLAwZ-nrpTzZBn!u&Gypw_i<_ z!+4*eNx>DbvMr=^q+g7fhQ(vYnmx6WvxNH`TOKyC14w}CcRZeTmVYYZfGjY;{!nO$ z_|+h=sf!hOs=4jvEFxPInDBDLlSv*qzNXf|D0wN(GU6H#anzllczj|E4}; z6+JB^pviOeS)_<{;?xzHYAabkc~nre7D}26OFNr1wQ#8oq2t~VBL#fJvqIcZWWn&LY0f-Z0^_jf;Rx&#-_*r+vx=ibP*%n6C$9@c&|!m4 zV>%1%K-=qz+tjg+J2}8slk*g205;H171b?QzsepI%9-hp#Ts=d{l%WC7cUa2H)Y)I z=i}*3aL#9><+4>?Dn{2q)KEy6A^4lU;;{G!#(S5K&qprkK04<-8;xu|-psOxRAoJd zP}0sfZ)Q4rmt@=FNiiNP;+>zaP|r@W^IM4HGVvG-MK9OnX+NOfv+XRja1s zep^ zVI!g`N4@LCBFo(l%Hr3Z+=Rv^5CDyV_TbjHKD_$QA z?JySt3)~Ai{@|TyMry|Wd|@8$y&S0V8pPn5?h)_e=`Ylk;ys9PyBq$qu*($*c=&16 zA^%b@ZG0su=L$0Jt26MsH~&HG$YB|BfFjmuQf@7F8?`%dgP&dILF45tnS-VGG)!s_ z#pzW`THCpG*v91_vKN=U=SIcAyP=a^G{^d#6|a>X?7FbfQ8`Z)%#Q1Ck_5T)f0lSU zs^A%dZ|rjnt+l>6c)y3( zcE1{Qr7Zdbf!obYCTxcE?pidhOW7+s+=j4hHZM7LQ$OM&XtCAV8J{61@N9e(jqc%3 zC)N>-Pb+!o*8BTouSR3VKexZl@}A2VK})zd){Ml(iMg9Z{V!dGFUjLm`y1T;#`Ly6 za2qt)HKs6ye>3%V2^@bQ%6YWz>7(_n#tq#>t47rwCuwP!f2?UE-+fl`t|`O0ObSyH zT{`ZKRB^do8cG+dJp@iCEr?>-+QvNi&F%hO^s@okx`}J15T9~il*7~QT09X$JS;!F zgHalpL&~pv^q3s53!2#4k+y(eCYX+rKPa*DmkpNn1x_-u#On@eLa5K@2x4H{&JsJ# zud?Qau{+3F|CL#EigHTjFd4}iG!*>S<<%uRl52e)nSDoJL%;Px$k1kdLv{KC@4SA8 zi}8|fmhwJd+Dlpz3vRTY!Ht#5V)ZHa!WCnIZb{(u&Gj&o>cAWks34W56HK1`+_66M z8?cRcQ!)-&-mT5=_OqF~frSk5u#3_NZe9rXIvOpu;1V;Tj@sz{rX+21W1|jiJB{j+ zlkfXAvnZ4vfHYdGc7s%kX4~O4pWY==wZqS>($Y8!a?R@5W<;{U>_988;*O!lPFBla zLRpo69OsOS_H9YpdQ3^avMQe1CU&dw57Y(Q1cA}#WAp~pGw`kEN6^6?v>`7JL8E0Yq2t9 zU>&${QR`7R!-0KmBFqW~O;3AkqSuIsfBki+tf zqkHXl`aHOQ_t9r$WrAW_miyDVMAku)MYO9`Yuk}m)JR>cLGwujTdx%GZ)~;dKrTvs zjT5gr-CG%;gy3Nv9R6Qc*w;KBoZY1}NYvpyv?mm>fgU63wgZp;Xo!A*FGnD8puTQ! z-#VJFqR~y%j^7ilLWr)vYV2H!T90ve83RpVPnWVg!X9LivgvQ6fFFn>$eMT|PGU9l z^uu>Q}f5~GjeD8~cA9B!YMb2p!mPFL7u9}XgQd89=bp888tai8CP zDOk^DqGJ~V^@Jj-HY!3GQo-2EbzcKDl|3+t&7ZOeYUn?#t_Lfk?~D8$BF)yiu!p{Z z#%-L}MhVMk*IOoGlfPj!?bs1&TU{gAd2at#N zj?xZqaV^5Y66j^Ptz(|j(6EwNIbJKiJU}FZv+?dP z98MGmbronr)+tcUW6aIpq@O>L+8G8r$0hUVPD;pxsf*!Fxn+JPE1NxbAHZ*#8xjkaUm8$fapKQ=^p2%ohQgfWLD*a?LThD> zy-JhOiPCzzO5M3lrDVzB+}h7LnKCE)n%7!&Y8e-f2kmSxz)lQ@6KqsQdm z4;1F~H_6iIIN-#~sjM048Z~uu2o!NQn~bG1l!{WRJ80u`6Pc8hk%VU#%q{ED(Ym|cT2QpM+W}<~V=R3r+ zSe{88E-v!b{U~i7!1sR6Najf~FXH_HCN^bU>q>TE~RgI`*YWN}iS)+TQTMJSWGpls! z(o@3}$r%}1Rgy1t;NK9%EY-eMo8b3}CflbRkFlBBi;=n`#q$ovr;5s!BXbN_6Y4Oz45?{5cx zf32UwdQ8M+>Pcs_!Olw~X}-uXgW`v;*-*kcJREno)!I%`Y-n?)xC2)Jo&SFD_(7iH z8&Mk2dZ;+22_ilNR~+>~98hKZ{_ixSh(zsk3&`2mgEK3LB3hkGqTcx6ZFcYpCpXH) za}%dU!2;?s!jG2dl`;#XI5N?47@K{=6CkstzG3>yqbaVfZMjrMXa1izS8v`o9l{?f zj48V&^S(_WhNA{%-#KpWk0xd4-HM4W$DgJmMNb-gZ&Uo z_e#hMTDpH0>*joA3ufU?jT<05J(%XBJtT{4^Ls1DC?NoBfZ+em9ZJ&{C^MU}<83jyE=0zwebzP=~rIt7wFxXo|pR@b<`=bt+Z{LDAp^%6X)pZkUUTf z@E29kFXZ>?>?Ur#_>8$#3jK|K^F7B!@&hk&R?1>5DcGMJnUl_rqRgJO?_4ZYiq{(I zl4Wo+S_BB9u~GxSacq@!#G~)iNo)zVeZ#_xi&^^$S!IHp9%4|}7LaoT>LXoM)&PCQH+AvS3LNejLepd$Wl`Pf=`S_L`X4j3;jr^A5|Do4Chy_%p z>}Sk$4IL;Nv9RgpZFH1YGoQ(RA*7X`dB+;=f$HW{MU441e&(`_;y!o8R_CY3nv`O<(sXDuMO<*FBszBUobe>?|LtS^IUFN05*07t>=OOGM2L# z9Cir^bIDgT^&&_fRn+SvRCMj@!-8C#-fP@EYV~!taoq~$E15LpI0^dtH=?QTuGw8x z6@GtE!9h;VyPnXhU~=#d=c54O*OSid%1cm)SMI*&i%$||LP)_5iO7?`YP6`VcGIjBo#yC&SHn=IYS_Fa2&ObJ5Mheigt4k1Dwy>mbG5v1a8 z+3HVmWz^)E0>{VFWW7IEZD2rDN<#z@j}_k_m?TFBcgWzbj3<-Cgc)M?W>fO8Gjx!8 z$lLkm_^Ukskl@>-;71RK9wX~8aRG=0xsZrb1vzMWgQX-0$ZUYAVijqH73g@_!obWmIy}jd!)BuU8D+lL%t`A9KCXnvozf6^Z z+(mCka%wDb-~SB40fz-$m)^3KMu3a=5cc*C2%y_XJDx7Mrp9PB?=C#t0<-Ia>?!kB z>I}__IF2SFgvlIXgP>EQR`N+a{Wr`Oi0vB%!u{2S+1+2cT!SX(kyGn9Pe@vilTr>m zDfAxee{=MlVY-%!89cZ@`YgF}zK8SDClxWUy+a40agAIUsHrl93S*(G{?K7YWr6)s zSqWZy54dW7QX;9y{-ehREFy>6)8PLFztUb8t_^=2gw$8p82_y0bZ#4Tj1pthy9Hr2 zOn*E#aiP?vOq}DJLO?D&IzFp>0jgx~DP&!&svz=&S4aC_8x2C7d%-6==D{RxJLugS zzgN}(2hsYwcK8)AHS&vt4xF-hGcogz<)W%V1u5rQ6{o~~vi90F{zedJ4%Hdrt(ucK z%>GqzAXqCJvbTD1uhzpwrP_7r8gJdmj@L#u6iD54jX4Pn&&o?Nqg`_)r&t6f`PpAO z-n$LaOpdQbEDTgBCC_ZannK+2LzcpJ_-(PP>X0BjJ8f(V{M8d_^M z2@crrAky++>jafyL&m*V!UXLq$t0_Uk=y0W=}p)=$^yem_$;2k@};Zmbp z@CR1rSCpIT50{SKRNL}KRwrQ^igpVKpbyXbWO{$;GmrKrw{D;Mr2B+!v&0hM_8c&C z9xF8kVwzzL#+W5Ya3^f?`}EX)d0S;4OD9HH-6Eb>zZa=>aMZJ(Z9y>MOH_9t6JIj7$}?8y zJ>dT<0=4bHyQET$%oi4n@d4aLU@8E0zAfVTEW_Csr=54yysx>`|}a z3-9;{Ce8EXv&Sb@idl)r5{PIx z)g=m~svZT9BZ4pN#V6j4C>>OWD{;Vzhv}H;+wIp|0yf;i8ch3~ zuZUu;{$pYTaTKWWdU_UxXRe{n@L|2(xbh6p-T2I#OX}V$<(Z9CXwZ>m99Ee6>oTO- z*j2W(Hiar&mlm?j*8x$URu>A=Ja|JHqk?xyV!r@s(W>U(ibw9N&vEC%ARjN5*( zoU}T2j`y5L_C0psojbnCE(Ldbv-MzEhEFGP{>oJ^2t=X*g>DQSh*eK z)GZhQItVlg1bvP{Q@a&%>JOnSL@M1e(Vn^pzc_u3c(XSBb{&9z60;@X90EL7<*UA= z^W{u-l3iafjeKw+Uz!FmGm#*1cdM=*H^qN$<9p$d0E9J^usG$_rmM|Jt6xEC=*Bm7 zz>_RepKcQp{>n0fG<2D9E+~pKYrikwxHyA=9N6G@K#?w- zjrD2=tfy(^!t?F5gT^-aIRQYUJ0K4#;Q>i7cxEhO$EqU-)Dai?{BBD?trvzgO+u+e z@~srkjGNAveB(e!3tofjV?FG=omsaTFyGj1JuIu{GC2%|oGJ&0nNq?ik}F5mCVH6Wp

(^k&Y+GLiPL@x(VX{hy& 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 Date: Thu, 13 Feb 2025 21:54:54 +0000 Subject: [PATCH 6/9] Some fixes from badge conflict resolution --- .../app/v3/services/triggerTask.server.ts | 105 +++++++++++------- .../database/prisma/schema.prisma | 2 + 2 files changed, 67 insertions(+), 40 deletions(-) diff --git a/apps/webapp/app/v3/services/triggerTask.server.ts b/apps/webapp/app/v3/services/triggerTask.server.ts index aed88d5bba..91a201e9cf 100644 --- a/apps/webapp/app/v3/services/triggerTask.server.ts +++ b/apps/webapp/app/v3/services/triggerTask.server.ts @@ -1,34 +1,36 @@ import { IOPacket, + packetRequiresOffloading, QueueOptions, SemanticInternalAttributes, + taskRunErrorEnhancer, + taskRunErrorToString, TriggerTaskRequestBody, - packetRequiresOffloading, } from "@trigger.dev/core/v3"; +import { parseNaturalLanguageDuration } from "@trigger.dev/core/v3/apps"; +import { Prisma, TaskRun } from "@trigger.dev/database"; import { env } from "~/env.server"; +import { sanitizeQueueName } from "~/models/taskQueue.server"; +import { createTag, MAX_TAGS_PER_RUN } from "~/models/taskRunTag.server"; import { AuthenticatedEnvironment } from "~/services/apiAuth.server"; import { autoIncrementCounter } from "~/services/autoIncrementCounter.server"; -import { workerQueue } from "~/services/worker.server"; +import { logger } from "~/services/logger.server"; +import { getEntitlement } from "~/services/platform.v3.server"; +import { resolveIdempotencyKeyTTL } from "~/utils/idempotencyKeys.server"; +import { handleMetadataPacket } from "~/utils/packets"; import { marqs } from "~/v3/marqs/index.server"; import { eventRepository } from "../eventRepository.server"; import { generateFriendlyId } from "../friendlyIdentifiers"; -import { uploadPacketToObjectStore } from "../r2.server"; -import { startActiveSpan } from "../tracer.server"; -import { getEntitlement } from "~/services/platform.v3.server"; -import { BaseService, ServiceValidationError } from "./baseService.server"; -import { logger } from "~/services/logger.server"; -import { isFinalAttemptStatus, isFinalRunStatus } from "../taskStatus"; -import { createTag, MAX_TAGS_PER_RUN } from "~/models/taskRunTag.server"; import { findCurrentWorkerFromEnvironment } from "../models/workerDeployment.server"; -import { handleMetadataPacket } from "~/utils/packets"; -import { parseNaturalLanguageDuration } from "@trigger.dev/core/v3/apps"; -import { ExpireEnqueuedRunService } from "./expireEnqueuedRun.server"; import { guardQueueSizeLimitsForEnv } from "../queueSizeLimits.server"; +import { uploadPacketToObjectStore } from "../r2.server"; +import { isFinalAttemptStatus, isFinalRunStatus } from "../taskStatus"; +import { startActiveSpan } from "../tracer.server"; import { clampMaxDuration } from "../utils/maxDuration"; -import { resolveIdempotencyKeyTTL } from "~/utils/idempotencyKeys.server"; -import { Prisma, TaskRun } from "@trigger.dev/database"; -import { sanitizeQueueName } from "~/models/taskQueue.server"; +import { BaseService, ServiceValidationError } from "./baseService.server"; import { EnqueueDelayedRunService } from "./enqueueDelayedRun.server"; +import { enqueueRun } from "./enqueueRun.server"; +import { ExpireEnqueuedRunService } from "./expireEnqueuedRun.server"; import { getTaskEventStore } from "../taskEventStore.server"; export type TriggerTaskServiceOptions = { @@ -187,6 +189,7 @@ export class TriggerTaskService extends BaseService { rootTaskRunId: true, depth: true, queueTimestamp: true, + queue: true, }, }, }, @@ -244,6 +247,7 @@ export class TriggerTaskService extends BaseService { rootTaskRunId: true, depth: true, queueTimestamp: true, + queue: true, }, }, }, @@ -296,7 +300,7 @@ export class TriggerTaskService extends BaseService { : undefined; try { - return await eventRepository.traceEvent( + const result = await eventRepository.traceEvent( taskId, { context: options.traceContext, @@ -556,40 +560,61 @@ export class TriggerTaskService extends BaseService { this._prisma ); - // If this is a triggerAndWait or batchTriggerAndWait, - // we need to add the parent run to the reserve concurrency set - // to free up concurrency for the children to run - if (dependentAttempt) { - await marqs?.reserveConcurrency(dependentAttempt.taskRun.id); - } else if (dependentBatchRun?.dependentTaskAttempt) { - await marqs?.reserveConcurrency(dependentBatchRun.dependentTaskAttempt.taskRun.id); - } - if (!run) { return; } - // We need to enqueue the task run into the appropriate queue. This is done after the tx completes to prevent a race condition where the task run hasn't been created yet by the time we dequeue. + // Now enqueue the run if it's not delayed if (run.status === "PENDING") { - await marqs?.enqueueMessage( - environment, - run.queue, - run.id, - { - type: "EXECUTE", - taskIdentifier: taskId, - projectId: environment.projectId, - environmentId: environment.id, - environmentType: environment.type, - }, - body.options?.concurrencyKey, - run.queueTimestamp ?? undefined - ); + const enqueueResult = await enqueueRun({ + env: environment, + run, + dependentRun: + dependentAttempt?.taskRun ?? dependentBatchRun?.dependentTaskAttempt?.taskRun, + }); + + if (!enqueueResult.ok) { + // Now we need to fail the run with enqueueResult.error and make sure and + // set the traced event to failed as well + await this._prisma.taskRun.update({ + where: { id: run.id }, + data: { + status: "SYSTEM_FAILURE", + completedAt: new Date(), + error: enqueueResult.error, + }, + }); + + event.failWithError(enqueueResult.error); + + return { + run, + isCached: false, + error: enqueueResult.error, + }; + } } return { run, isCached: false }; } ); + + if (result?.error) { + throw new ServiceValidationError( + taskRunErrorToString(taskRunErrorEnhancer(result.error)) + ); + } + + const run = result?.run; + + if (!run) { + return; + } + + return { + run, + isCached: result?.isCached, + }; } catch (error) { // Detect a prisma transaction Unique constraint violation if (error instanceof Prisma.PrismaClientKnownRequestError) { diff --git a/internal-packages/database/prisma/schema.prisma b/internal-packages/database/prisma/schema.prisma index 9373f0620c..7ff86d95e1 100644 --- a/internal-packages/database/prisma/schema.prisma +++ b/internal-packages/database/prisma/schema.prisma @@ -1731,6 +1731,8 @@ model TaskRun { taskEventStore String @default("taskEvent") + queueTimestamp DateTime? + batchItems BatchTaskRunItem[] dependency TaskRunDependency? CheckpointRestoreEvent CheckpointRestoreEvent[] From 61fab3d46b41b9e27bdd4e03010188492f2982dc Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Sat, 15 Feb 2025 09:59:06 +0000 Subject: [PATCH 7/9] WIP priority queues --- .../envPriorityDequeuingStrategy.server.ts | 58 +++ .../v3/marqs/fairDequeuingStrategy.server.ts | 46 ++- apps/webapp/app/v3/marqs/index.server.ts | 384 ++++++++++-------- .../app/v3/marqs/marqsKeyProducer.server.ts | 112 ++++- apps/webapp/app/v3/marqs/types.ts | 55 ++- .../envPriorityDequeueingStrategy.test.ts | 360 ++++++++++++++++ .../webapp/test/fairDequeuingStrategy.test.ts | 45 +- apps/webapp/test/marqsKeyProducer.test.ts | 178 ++++++++ .../core/src/v3/utils/flattenAttributes.ts | 6 +- 9 files changed, 1000 insertions(+), 244 deletions(-) create mode 100644 apps/webapp/app/v3/marqs/envPriorityDequeuingStrategy.server.ts create mode 100644 apps/webapp/test/envPriorityDequeueingStrategy.test.ts create mode 100644 apps/webapp/test/marqsKeyProducer.test.ts diff --git a/apps/webapp/app/v3/marqs/envPriorityDequeuingStrategy.server.ts b/apps/webapp/app/v3/marqs/envPriorityDequeuingStrategy.server.ts new file mode 100644 index 0000000000..89a9af2b85 --- /dev/null +++ b/apps/webapp/app/v3/marqs/envPriorityDequeuingStrategy.server.ts @@ -0,0 +1,58 @@ +import { EnvQueues, MarQSFairDequeueStrategy, MarQSKeyProducer } from "./types"; + +export type EnvPriorityDequeuingStrategyOptions = { + keys: MarQSKeyProducer; + delegate: MarQSFairDequeueStrategy; +}; + +export class EnvPriorityDequeuingStrategy implements MarQSFairDequeueStrategy { + private _delegate: MarQSFairDequeueStrategy; + + constructor(private options: EnvPriorityDequeuingStrategyOptions) { + this._delegate = options.delegate; + } + + async distributeFairQueuesFromParentQueue( + parentQueue: string, + consumerId: string + ): Promise> { + const envQueues = await this._delegate.distributeFairQueuesFromParentQueue( + parentQueue, + consumerId + ); + + return this.#sortQueuesInEnvironmentsByPriority(envQueues); + } + + #sortQueuesInEnvironmentsByPriority(envs: EnvQueues[]): EnvQueues[] { + return envs.map((env) => { + return this.#sortQueuesInEnvironmentByPriority(env); + }); + } + + // Sorts the queues by priority. A higher priority means the queue should be dequeued first. + // All the queues with the same priority should keep the order they were in the original list. + // So that means if all the queues have the same priority, the order should be preserved. + #sortQueuesInEnvironmentByPriority(env: EnvQueues): EnvQueues { + const queues = env.queues; + + const sortedQueues = [...queues].sort((a, b) => { + const aPriority = this.#getQueuePriority(a); + const bPriority = this.#getQueuePriority(b); + + if (aPriority === bPriority) { + return 0; + } + + return aPriority > bPriority ? -1 : 1; + }); + + return { envId: env.envId, queues: sortedQueues }; + } + + #getQueuePriority(queue: string): number { + const queueRecord = this.options.keys.queueDescriptorFromQueue(queue); + + return queueRecord.priority ?? 0; + } +} diff --git a/apps/webapp/app/v3/marqs/fairDequeuingStrategy.server.ts b/apps/webapp/app/v3/marqs/fairDequeuingStrategy.server.ts index 64e44b9562..986f75c525 100644 --- a/apps/webapp/app/v3/marqs/fairDequeuingStrategy.server.ts +++ b/apps/webapp/app/v3/marqs/fairDequeuingStrategy.server.ts @@ -3,7 +3,7 @@ import { createCache, DefaultStatefulContext, Namespace, Cache as UnkeyCache } f import { MemoryStore } from "@unkey/cache/stores"; import { randomUUID } from "crypto"; import { Redis } from "ioredis"; -import { MarQSFairDequeueStrategy, MarQSKeyProducer } from "./types"; +import { EnvQueues, MarQSFairDequeueStrategy, MarQSKeyProducer } from "./types"; import seedrandom from "seedrandom"; import { Tracer } from "@opentelemetry/api"; import { startSpan } from "../tracing.server"; @@ -111,7 +111,7 @@ export class FairDequeuingStrategy implements MarQSFairDequeueStrategy { async distributeFairQueuesFromParentQueue( parentQueue: string, consumerId: string - ): Promise> { + ): Promise> { return await startSpan( this.options.tracer, "distributeFairQueuesFromParentQueue", @@ -132,21 +132,27 @@ export class FairDequeuingStrategy implements MarQSFairDequeueStrategy { return []; } - const shuffledQueues = this.#shuffleQueuesByEnv(snapshot); + const envQueues = this.#shuffleQueuesByEnv(snapshot); - span.setAttribute("shuffled_queue_count", shuffledQueues.length); + span.setAttribute( + "shuffled_queue_count", + envQueues.reduce((sum, env) => sum + env.queues.length, 0) + ); - if (shuffledQueues[0]) { - span.setAttribute("winning_env", this.options.keys.envIdFromQueue(shuffledQueues[0])); - span.setAttribute("winning_org", this.options.keys.orgIdFromQueue(shuffledQueues[0])); + if (envQueues[0]?.queues[0]) { + span.setAttribute("winning_env", envQueues[0].envId); + span.setAttribute( + "winning_org", + this.options.keys.orgIdFromQueue(envQueues[0].queues[0]) + ); } - return shuffledQueues; + return envQueues; } ); } - #shuffleQueuesByEnv(snapshot: FairQueueSnapshot): Array { + #shuffleQueuesByEnv(snapshot: FairQueueSnapshot): Array { const envs = Object.keys(snapshot.envs); const biases = this.options.biases ?? defaultBiases; @@ -212,7 +218,8 @@ export class FairDequeuingStrategy implements MarQSFairDequeueStrategy { } // Helper method to maintain DRY principle - #orderQueuesByEnvs(envs: string[], snapshot: FairQueueSnapshot): Array { + // Update return type + #orderQueuesByEnvs(envs: string[], snapshot: FairQueueSnapshot): Array { const queuesByEnv = snapshot.queues.reduce((acc, queue) => { if (!acc[queue.env]) { acc[queue.env] = []; @@ -221,15 +228,20 @@ export class FairDequeuingStrategy implements MarQSFairDequeueStrategy { return acc; }, {} as Record>); - const queues = envs.reduce((acc, envId) => { + return envs.reduce((acc, envId) => { if (queuesByEnv[envId]) { - // Instead of sorting by age, use weighted random selection - acc.push(...this.#weightedRandomQueueOrder(queuesByEnv[envId])); + // Get ordered queues for this env + const orderedQueues = this.#weightedRandomQueueOrder(queuesByEnv[envId]); + // Only add the env if it has queues + if (orderedQueues.length > 0) { + acc.push({ + envId, + queues: orderedQueues.map((queue) => queue.id), + }); + } } return acc; - }, [] as Array); - - return queues.map((queue) => queue.id); + }, [] as Array); } #weightedRandomQueueOrder(queues: FairQueue[]): FairQueue[] { @@ -558,7 +570,7 @@ export class NoopFairDequeuingStrategy implements MarQSFairDequeueStrategy { async distributeFairQueuesFromParentQueue( parentQueue: string, consumerId: string - ): Promise> { + ): Promise> { return []; } } diff --git a/apps/webapp/app/v3/marqs/index.server.ts b/apps/webapp/app/v3/marqs/index.server.ts index d26a766161..49b151c80a 100644 --- a/apps/webapp/app/v3/marqs/index.server.ts +++ b/apps/webapp/app/v3/marqs/index.server.ts @@ -32,6 +32,8 @@ import { VisibilityTimeoutStrategy, } from "./types"; import { V3LegacyRunEngineWorkerVisibilityTimeout } from "./v3VisibilityTimeout.server"; +import { flattenAttributes } from "@trigger.dev/core/v3"; +import { EnvPriorityDequeuingStrategy } from "./envPriorityDequeuingStrategy.server"; const KEY_PREFIX = "marqs:"; @@ -176,12 +178,13 @@ export class MarQS { messageData: Record, concurrencyKey?: string, timestamp?: number | Date, - reserve?: EnqueueMessageReserveConcurrencyOptions + reserve?: EnqueueMessageReserveConcurrencyOptions, + priority?: number ) { return await this.#trace( "enqueueMessage", async (span) => { - const messageQueue = this.keys.queueKey(env, queue, concurrencyKey); + const messageQueue = this.keys.queueKey(env, queue, concurrencyKey, priority); const parentQueue = this.keys.envSharedQueueKey(env); @@ -234,6 +237,72 @@ export class MarQS { ); } + public async replaceMessage( + messageId: string, + messageData: Record, + timestamp?: number, + inplace?: boolean + ) { + return this.#trace( + "replaceMessage", + async (span) => { + const oldMessage = await this.readMessage(messageId); + + if (!oldMessage) { + return; + } + + span.setAttributes({ + [SemanticAttributes.QUEUE]: oldMessage.queue, + [SemanticAttributes.MESSAGE_ID]: oldMessage.messageId, + [SemanticAttributes.CONCURRENCY_KEY]: oldMessage.concurrencyKey, + [SemanticAttributes.PARENT_QUEUE]: oldMessage.parentQueue, + }); + + const traceContext = { + traceparent: oldMessage.data.traceparent, + tracestate: oldMessage.data.tracestate, + }; + + const newMessage: MessagePayload = { + version: "1", + // preserve original trace context + data: { ...oldMessage.data, ...messageData, ...traceContext }, + queue: oldMessage.queue, + concurrencyKey: oldMessage.concurrencyKey, + timestamp: timestamp ?? Date.now(), + messageId, + parentQueue: oldMessage.parentQueue, + }; + + if (inplace) { + await this.#callReplaceMessage(newMessage); + return; + } + + await this.options.visibilityTimeoutStrategy.cancelHeartbeat(messageId); + + await this.#callAcknowledgeMessage({ + parentQueue: oldMessage.parentQueue, + messageQueue: oldMessage.queue, + messageId, + }); + + await this.#callEnqueueMessage(newMessage); + + await this.options.subscriber?.messageReplaced(newMessage); + }, + { + kind: SpanKind.CONSUMER, + attributes: { + [SEMATTRS_MESSAGING_OPERATION]: "replace", + [SEMATTRS_MESSAGE_ID]: messageId, + [SEMATTRS_MESSAGING_SYSTEM]: "marqs", + }, + } + ); + } + public async dequeueMessageInEnv(env: AuthenticatedEnvironment) { return this.#trace( "dequeueMessageInEnv", @@ -244,12 +313,15 @@ export class MarQS { span.setAttribute(SemanticAttributes.CONSUMER_ID, env.id); // Get prioritized list of queues to try - const queues = + const environments = await this.options.envQueuePriorityStrategy.distributeFairQueuesFromParentQueue( parentQueue, env.id ); + const queues = environments.flatMap((e) => e.queues); + + span.setAttribute("env_count", environments.length); span.setAttribute("queue_count", queues.length); for (const messageQueue of queues) { @@ -328,64 +400,78 @@ export class MarQS { span.setAttribute(SemanticAttributes.PARENT_QUEUE, parentQueue); // Get prioritized list of queues to try - const queues = await this.options.queuePriorityStrategy.distributeFairQueuesFromParentQueue( - parentQueue, - consumerId - ); + const envQueues = + await this.options.queuePriorityStrategy.distributeFairQueuesFromParentQueue( + parentQueue, + consumerId + ); - span.setAttribute("queue_count", queues.length); + span.setAttribute("environment_count", envQueues.length); - if (queues.length === 0) { + if (envQueues.length === 0) { return; } + let attemptedEnvs = 0; + let attemptedQueues = 0; + // Try each queue in order until we successfully dequeue a message - for (const messageQueue of queues) { - try { - const messageData = await this.#callDequeueMessage({ - messageQueue, - parentQueue, - }); + for (const env of envQueues) { + attemptedEnvs++; - if (!messageData) { - continue; // Try next queue if no message was dequeued - } + for (const messageQueue of env.queues) { + attemptedQueues++; - const message = await this.readMessage(messageData.messageId); - - if (message) { - const ageOfMessageInMs = Date.now() - message.timestamp; - - span.setAttributes({ - [SEMATTRS_MESSAGE_ID]: message.messageId, - [SemanticAttributes.QUEUE]: message.queue, - [SemanticAttributes.MESSAGE_ID]: message.messageId, - [SemanticAttributes.CONCURRENCY_KEY]: message.concurrencyKey, - [SemanticAttributes.PARENT_QUEUE]: message.parentQueue, - age_in_seconds: ageOfMessageInMs / 1000, - attempted_queues: queues.indexOf(messageQueue) + 1, // How many queues we tried before success - message_timestamp: message.timestamp, - message_age: Date.now() - message.timestamp, + try { + const messageData = await this.#callDequeueMessage({ + messageQueue, + parentQueue, }); - await this.options.subscriber?.messageDequeued(message); - - await this.options.visibilityTimeoutStrategy.heartbeat( - messageData.messageId, - this.visibilityTimeoutInMs - ); - - return message; + if (!messageData) { + continue; // Try next queue if no message was dequeued + } + + const message = await this.readMessage(messageData.messageId); + + if (message) { + const ageOfMessageInMs = Date.now() - message.timestamp; + + span.setAttributes({ + [SEMATTRS_MESSAGE_ID]: message.messageId, + [SemanticAttributes.QUEUE]: message.queue, + [SemanticAttributes.MESSAGE_ID]: message.messageId, + [SemanticAttributes.CONCURRENCY_KEY]: message.concurrencyKey, + [SemanticAttributes.PARENT_QUEUE]: message.parentQueue, + age_in_seconds: ageOfMessageInMs / 1000, + attempted_queues: attemptedQueues, // How many queues we tried before success + attempted_envs: attemptedEnvs, // How many environments we tried before success + message_timestamp: message.timestamp, + message_age: Date.now() - message.timestamp, + ...flattenAttributes(message.data, "message"), + }); + + await this.options.subscriber?.messageDequeued(message); + + await this.options.visibilityTimeoutStrategy.heartbeat( + messageData.messageId, + this.visibilityTimeoutInMs + ); + + return message; + } + } catch (error) { + // Log error but continue trying other queues + logger.warn(`[${this.name}] Failed to dequeue from queue ${messageQueue}`, { error }); + continue; } - } catch (error) { - // Log error but continue trying other queues - logger.warn(`[${this.name}] Failed to dequeue from queue ${messageQueue}`, { error }); - continue; } } // If we get here, we tried all queues but couldn't dequeue a message - span.setAttribute("attempted_queues", queues.length); + span.setAttribute("attempted_queues", attemptedQueues); + span.setAttribute("attempted_envs", attemptedEnvs); + return; }, { @@ -442,65 +528,81 @@ export class MarQS { ); } - public async replaceMessage( + /** + * Negative acknowledge a message, which will requeue the message. + * Returns whether it went back into the queue or not. + */ + public async nackMessage( messageId: string, - messageData: Record, - timestamp?: number, - inplace?: boolean + retryAt: number = Date.now(), + updates?: Record ) { return this.#trace( - "replaceMessage", + "nackMessage", async (span) => { - const oldMessage = await this.readMessage(messageId); + const message = await this.readMessage(messageId); - if (!oldMessage) { - return; + if (!message) { + logger.debug(`[${this.name}].nackMessage() message not found`, { + messageId, + retryAt, + updates, + service: this.name, + }); + return false; } - span.setAttributes({ - [SemanticAttributes.QUEUE]: oldMessage.queue, - [SemanticAttributes.MESSAGE_ID]: oldMessage.messageId, - [SemanticAttributes.CONCURRENCY_KEY]: oldMessage.concurrencyKey, - [SemanticAttributes.PARENT_QUEUE]: oldMessage.parentQueue, - }); + const nackCount = await this.#getNackCount(messageId); - const traceContext = { - traceparent: oldMessage.data.traceparent, - tracestate: oldMessage.data.tracestate, - }; + span.setAttribute("nack_count", nackCount); - const newMessage: MessagePayload = { - version: "1", - // preserve original trace context - data: { ...oldMessage.data, ...messageData, ...traceContext }, - queue: oldMessage.queue, - concurrencyKey: oldMessage.concurrencyKey, - timestamp: timestamp ?? Date.now(), - messageId, - parentQueue: oldMessage.parentQueue, - }; + if (nackCount >= this.options.maximumNackCount) { + logger.debug(`[${this.name}].nackMessage() maximum nack count reached`, { + messageId, + retryAt, + updates, + service: this.name, + }); - if (inplace) { - await this.#callReplaceMessage(newMessage); - return; + span.setAttribute("maximum_nack_count_reached", true); + + // If we have reached the maximum nack count, we will ack the message + await this.acknowledgeMessage(messageId, "maximum nack count reached"); + return false; + } + + span.setAttributes({ + [SemanticAttributes.QUEUE]: message.queue, + [SemanticAttributes.MESSAGE_ID]: message.messageId, + [SemanticAttributes.CONCURRENCY_KEY]: message.concurrencyKey, + [SemanticAttributes.PARENT_QUEUE]: message.parentQueue, + }); + + if (updates) { + await this.replaceMessage(messageId, updates, retryAt, true); } await this.options.visibilityTimeoutStrategy.cancelHeartbeat(messageId); - await this.#callAcknowledgeMessage({ - parentQueue: oldMessage.parentQueue, - messageQueue: oldMessage.queue, + await this.#callNackMessage({ + messageKey: this.keys.messageKey(messageId), + messageQueue: message.queue, + parentQueue: message.parentQueue, + concurrencyKey: this.keys.currentConcurrencyKeyFromQueue(message.queue), + envConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(message.queue), + nackCounterKey: this.keys.nackCounterKey(messageId), messageId, + messageScore: retryAt, }); - await this.#callEnqueueMessage(newMessage); + await this.options.subscriber?.messageNacked(message); - await this.options.subscriber?.messageReplaced(newMessage); + return true; }, { kind: SpanKind.CONSUMER, attributes: { - [SEMATTRS_MESSAGING_OPERATION]: "replace", + [SEMATTRS_MESSAGING_OPERATION]: "nack", [SEMATTRS_MESSAGE_ID]: messageId, [SEMATTRS_MESSAGING_SYSTEM]: "marqs", }, @@ -565,88 +667,6 @@ export class MarQS { ); } - /** - * Negative acknowledge a message, which will requeue the message. - * Returns whether it went back into the queue or not. - */ - public async nackMessage( - messageId: string, - retryAt: number = Date.now(), - updates?: Record - ) { - return this.#trace( - "nackMessage", - async (span) => { - const message = await this.readMessage(messageId); - - if (!message) { - logger.debug(`[${this.name}].nackMessage() message not found`, { - messageId, - retryAt, - updates, - service: this.name, - }); - return false; - } - - const nackCount = await this.#getNackCount(messageId); - - span.setAttribute("nack_count", nackCount); - - if (nackCount >= this.options.maximumNackCount) { - logger.debug(`[${this.name}].nackMessage() maximum nack count reached`, { - messageId, - retryAt, - updates, - service: this.name, - }); - - span.setAttribute("maximum_nack_count_reached", true); - - // If we have reached the maximum nack count, we will ack the message - await this.acknowledgeMessage(messageId, "maximum nack count reached"); - return false; - } - - span.setAttributes({ - [SemanticAttributes.QUEUE]: message.queue, - [SemanticAttributes.MESSAGE_ID]: message.messageId, - [SemanticAttributes.CONCURRENCY_KEY]: message.concurrencyKey, - [SemanticAttributes.PARENT_QUEUE]: message.parentQueue, - }); - - if (updates) { - await this.replaceMessage(messageId, updates, retryAt, true); - } - - await this.options.visibilityTimeoutStrategy.cancelHeartbeat(messageId); - - await this.#callNackMessage({ - messageKey: this.keys.messageKey(messageId), - messageQueue: message.queue, - parentQueue: message.parentQueue, - concurrencyKey: this.keys.currentConcurrencyKeyFromQueue(message.queue), - envConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(message.queue), - nackCounterKey: this.keys.nackCounterKey(messageId), - messageId, - messageScore: retryAt, - }); - - await this.options.subscriber?.messageNacked(message); - - return true; - }, - { - kind: SpanKind.CONSUMER, - attributes: { - [SEMATTRS_MESSAGING_OPERATION]: "nack", - [SEMATTRS_MESSAGE_ID]: messageId, - [SEMATTRS_MESSAGING_SYSTEM]: "marqs", - }, - } - ); - } - async #getNackCount(messageId: string): Promise { const result = await this.redis.get(this.keys.nackCounterKey(messageId)); @@ -1068,6 +1088,7 @@ export class MarQS { const envCurrentConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue); const envReserveConcurrencyKey = this.keys.envReserveConcurrencyKeyFromQueue(messageQueue); const queueReserveConcurrencyKey = this.keys.queueReserveConcurrencyKeyFromQueue(messageQueue); + const envQueueKey = this.keys.envQueueKeyFromQueue(messageQueue); const result = await this.redis.dequeueMessage( messageQueue, @@ -1078,7 +1099,7 @@ export class MarQS { queueReserveConcurrencyKey, envCurrentConcurrencyKey, envReserveConcurrencyKey, - this.keys.envQueueKeyFromQueue(messageQueue), + envQueueKey, messageQueue, String(Date.now()), String(this.options.defaultEnvConcurrency) @@ -1129,6 +1150,7 @@ export class MarQS { const envConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue); const envReserveConcurrencyKey = this.keys.envReserveConcurrencyKeyFromQueue(messageQueue); const queueReserveConcurrencyKey = this.keys.queueReserveConcurrencyKeyFromQueue(messageQueue); + const envQueueKey = this.keys.envQueueKeyFromQueue(messageQueue); logger.debug("Calling acknowledgeMessage", { messageKey, @@ -1137,6 +1159,7 @@ export class MarQS { envConcurrencyKey, messageId, parentQueue, + envQueueKey, service: this.name, }); @@ -1148,7 +1171,7 @@ export class MarQS { queueReserveConcurrencyKey, envConcurrencyKey, envReserveConcurrencyKey, - this.keys.envQueueKeyFromQueue(messageQueue), + envQueueKey, messageId, messageQueue ); @@ -1789,19 +1812,22 @@ function getMarQSClient() { tracer: trace.getTracer("marqs"), keysProducer, visibilityTimeoutStrategy: new V3LegacyRunEngineWorkerVisibilityTimeout(), - queuePriorityStrategy: new FairDequeuingStrategy({ - tracer: tracer, - redis, - parentQueueLimit: env.MARQS_SHARED_QUEUE_LIMIT, + queuePriorityStrategy: new EnvPriorityDequeuingStrategy({ keys: keysProducer, - defaultEnvConcurrency: env.DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT, - biases: { - concurrencyLimitBias: env.MARQS_CONCURRENCY_LIMIT_BIAS, - availableCapacityBias: env.MARQS_AVAILABLE_CAPACITY_BIAS, - queueAgeRandomization: env.MARQS_QUEUE_AGE_RANDOMIZATION_BIAS, - }, - reuseSnapshotCount: env.MARQS_REUSE_SNAPSHOT_COUNT, - maximumEnvCount: env.MARQS_MAXIMUM_ENV_COUNT, + delegate: new FairDequeuingStrategy({ + tracer: tracer, + redis, + parentQueueLimit: env.MARQS_SHARED_QUEUE_LIMIT, + keys: keysProducer, + defaultEnvConcurrency: env.DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT, + biases: { + concurrencyLimitBias: env.MARQS_CONCURRENCY_LIMIT_BIAS, + availableCapacityBias: env.MARQS_AVAILABLE_CAPACITY_BIAS, + queueAgeRandomization: env.MARQS_QUEUE_AGE_RANDOMIZATION_BIAS, + }, + reuseSnapshotCount: env.MARQS_REUSE_SNAPSHOT_COUNT, + maximumEnvCount: env.MARQS_MAXIMUM_ENV_COUNT, + }), }), envQueuePriorityStrategy: new FairDequeuingStrategy({ tracer: tracer, diff --git a/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts b/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts index bdead92785..702bacc254 100644 --- a/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts +++ b/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts @@ -1,5 +1,4 @@ -import { AuthenticatedEnvironment } from "~/services/apiAuth.server"; -import { MarQSKeyProducer } from "./types"; +import { MarQSKeyProducer, MarQSKeyProducerEnv, QueueDescriptor } from "./types"; const constants = { SHARED_QUEUE: "sharedQueue", @@ -12,8 +11,15 @@ const constants = { CONCURRENCY_KEY_PART: "ck", MESSAGE_PART: "message", RESERVE_CONCURRENCY_PART: "reserveConcurrency", + PRIORITY_PART: "priority", } as const; +const ORG_REGEX = /org:(\w+):/; +const ENV_REGEX = /env:(\w+):/; +const QUEUE_REGEX = /queue:([^:]+)(?::|$)/; +const CONCURRENCY_KEY_REGEX = /ck:([^:]+)(?::|$)/; +const PRIORITY_REGEX = /priority:(\d+)(?::|$)/; + export class MarQSShortKeyProducer implements MarQSKeyProducer { constructor(private _prefix: string) {} @@ -33,26 +39,38 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { return key; } - queueConcurrencyLimitKey(env: AuthenticatedEnvironment, queue: string) { + queueConcurrencyLimitKey(env: MarQSKeyProducerEnv, queue: string) { return [this.queueKey(env, queue), constants.CONCURRENCY_LIMIT_PART].join(":"); } envConcurrencyLimitKey(envId: string): string; - envConcurrencyLimitKey(env: AuthenticatedEnvironment): string; - envConcurrencyLimitKey(envOrId: AuthenticatedEnvironment | string): string { + envConcurrencyLimitKey(env: MarQSKeyProducerEnv): string; + envConcurrencyLimitKey(envOrId: MarQSKeyProducerEnv | string): string { return [ this.envKeySection(typeof envOrId === "string" ? envOrId : envOrId.id), constants.CONCURRENCY_LIMIT_PART, ].join(":"); } - queueKey(orgId: string, envId: string, queue: string, concurrencyKey?: string): string; - queueKey(env: AuthenticatedEnvironment, queue: string, concurrencyKey?: string): string; queueKey( - envOrOrgId: AuthenticatedEnvironment | string, + orgId: string, + envId: string, + queue: string, + concurrencyKey?: string, + priority?: number + ): string; + queueKey( + env: MarQSKeyProducerEnv, + queue: string, + concurrencyKey?: string, + priority?: number + ): string; + queueKey( + envOrOrgId: MarQSKeyProducerEnv | string, queueOrEnvId: string, queueOrConcurrencyKey: string, - concurrencyKey?: string + concurrencyKeyOrPriority?: string | number, + priority?: number ): string { if (typeof envOrOrgId === "string") { return [ @@ -60,7 +78,12 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { this.envKeySection(queueOrEnvId), this.queueSection(queueOrConcurrencyKey), ] - .concat(concurrencyKey ? this.concurrencyKeySection(concurrencyKey) : []) + .concat( + typeof concurrencyKeyOrPriority === "string" + ? this.concurrencyKeySection(concurrencyKeyOrPriority) + : [] + ) + .concat(typeof priority === "number" && priority ? this.prioritySection(priority) : []) .join(":"); } else { return [ @@ -69,11 +92,16 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { this.queueSection(queueOrEnvId), ] .concat(queueOrConcurrencyKey ? this.concurrencyKeySection(queueOrConcurrencyKey) : []) + .concat( + typeof concurrencyKeyOrPriority === "number" && concurrencyKeyOrPriority + ? this.prioritySection(concurrencyKeyOrPriority) + : [] + ) .join(":"); } } - envSharedQueueKey(env: AuthenticatedEnvironment) { + envSharedQueueKey(env: MarQSKeyProducerEnv) { if (env.type === "DEVELOPMENT") { return [ this.orgKeySection(env.organizationId), @@ -95,19 +123,19 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { return `${concurrencyQueueName}:${constants.CONCURRENCY_LIMIT_PART}`; } + // TODO: if the queue passed in has a priority, we need to strip that out + // before adding the currentConcurrency part currentConcurrencyKeyFromQueue(queue: string) { return `${queue}:${constants.CURRENT_CONCURRENCY_PART}`; } + // TODO: if the queue passed in has a priority, we need to strip that out + // before adding the currentConcurrency part queueReserveConcurrencyKeyFromQueue(queue: string) { return `${queue}:${constants.RESERVE_CONCURRENCY_PART}`; } - currentConcurrencyKey( - env: AuthenticatedEnvironment, - queue: string, - concurrencyKey?: string - ): string { + currentConcurrencyKey(env: MarQSKeyProducerEnv, queue: string, concurrencyKey?: string): string { return [this.queueKey(env, queue, concurrencyKey), constants.CURRENT_CONCURRENCY_PART].join( ":" ); @@ -136,8 +164,8 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { } envCurrentConcurrencyKey(envId: string): string; - envCurrentConcurrencyKey(env: AuthenticatedEnvironment): string; - envCurrentConcurrencyKey(envOrId: AuthenticatedEnvironment | string): string { + envCurrentConcurrencyKey(env: MarQSKeyProducerEnv): string; + envCurrentConcurrencyKey(envOrId: MarQSKeyProducerEnv | string): string { return [ this.envKeySection(typeof envOrId === "string" ? envOrId : envOrId.id), constants.CURRENT_CONCURRENCY_PART, @@ -150,7 +178,7 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { return `${constants.ENV_PART}:${envId}:${constants.QUEUE_PART}`; } - envQueueKey(env: AuthenticatedEnvironment): string { + envQueueKey(env: MarQSKeyProducerEnv): string { return [constants.ENV_PART, this.shortId(env.id), constants.QUEUE_PART].join(":"); } @@ -170,6 +198,48 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { return this.normalizeQueue(queue).split(":")[3]; } + queueDescriptorFromQueue(queue: string): QueueDescriptor { + const match = queue.match(QUEUE_REGEX); + + if (!match) { + throw new Error(`Invalid queue: ${queue}`); + } + + const [, queueName] = match; + + const envMatch = queue.match(ENV_REGEX); + + if (!envMatch) { + throw new Error(`Invalid queue: ${queue}`); + } + + const [, envId] = envMatch; + + const orgMatch = queue.match(ORG_REGEX); + + if (!orgMatch) { + throw new Error(`Invalid queue: ${queue}`); + } + + const [, orgId] = orgMatch; + + const concurrencyKeyMatch = queue.match(CONCURRENCY_KEY_REGEX); + + const concurrencyKey = concurrencyKeyMatch ? concurrencyKeyMatch[1] : undefined; + + const priorityMatch = queue.match(PRIORITY_REGEX); + + const priority = priorityMatch ? parseInt(priorityMatch[1], 10) : undefined; + + return { + name: queueName, + environment: envId, + organization: orgId, + concurrencyKey, + priority, + }; + } + private shortId(id: string) { // Return the last 12 characters of the id return id.slice(-12); @@ -191,6 +261,10 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { return `${constants.CONCURRENCY_KEY_PART}:${concurrencyKey}`; } + private prioritySection(priority: number) { + return `${constants.PRIORITY_PART}:${priority}`; + } + // This removes the leading prefix from the queue name if it exists private normalizeQueue(queue: string) { if (queue.startsWith(this._prefix)) { diff --git a/apps/webapp/app/v3/marqs/types.ts b/apps/webapp/app/v3/marqs/types.ts index 03a270c92b..29db54f031 100644 --- a/apps/webapp/app/v3/marqs/types.ts +++ b/apps/webapp/app/v3/marqs/types.ts @@ -1,34 +1,55 @@ +import { RuntimeEnvironmentType } from "@trigger.dev/database"; import { z } from "zod"; -import { type AuthenticatedEnvironment } from "~/services/apiAuth.server"; export type QueueRange = { offset: number; count: number }; +export type QueueDescriptor = { + organization: string; + environment: string; + name: string; + concurrencyKey?: string; + priority?: number; +}; + +export type MarQSKeyProducerEnv = { + id: string; + organizationId: string; + type: RuntimeEnvironmentType; +}; + export interface MarQSKeyProducer { - queueConcurrencyLimitKey(env: AuthenticatedEnvironment, queue: string): string; + queueConcurrencyLimitKey(env: MarQSKeyProducerEnv, queue: string): string; envConcurrencyLimitKey(envId: string): string; - envConcurrencyLimitKey(env: AuthenticatedEnvironment): string; + envConcurrencyLimitKey(env: MarQSKeyProducerEnv): string; envCurrentConcurrencyKey(envId: string): string; - envCurrentConcurrencyKey(env: AuthenticatedEnvironment): string; + envCurrentConcurrencyKey(env: MarQSKeyProducerEnv): string; envReserveConcurrencyKey(envId: string): string; - queueKey(orgId: string, envId: string, queue: string, concurrencyKey?: string): string; - queueKey(env: AuthenticatedEnvironment, queue: string, concurrencyKey?: string): string; + queueKey( + orgId: string, + envId: string, + queue: string, + concurrencyKey?: string, + priority?: number + ): string; + queueKey( + env: MarQSKeyProducerEnv, + queue: string, + concurrencyKey?: string, + priority?: number + ): string; - envQueueKey(env: AuthenticatedEnvironment): string; - envSharedQueueKey(env: AuthenticatedEnvironment): string; + envQueueKey(env: MarQSKeyProducerEnv): string; + envSharedQueueKey(env: MarQSKeyProducerEnv): string; sharedQueueKey(): string; sharedQueueScanPattern(): string; queueCurrentConcurrencyScanPattern(): string; concurrencyLimitKeyFromQueue(queue: string): string; currentConcurrencyKeyFromQueue(queue: string): string; - currentConcurrencyKey( - env: AuthenticatedEnvironment, - queue: string, - concurrencyKey?: string - ): string; + currentConcurrencyKey(env: MarQSKeyProducerEnv, queue: string, concurrencyKey?: string): string; envConcurrencyLimitKeyFromQueue(queue: string): string; envCurrentConcurrencyKeyFromQueue(queue: string): string; envReserveConcurrencyKeyFromQueue(queue: string): string; @@ -40,13 +61,19 @@ export interface MarQSKeyProducer { envIdFromQueue(queue: string): string; queueReserveConcurrencyKeyFromQueue(queue: string): string; + queueDescriptorFromQueue(queue: string): QueueDescriptor; } +export type EnvQueues = { + envId: string; + queues: string[]; +}; + export interface MarQSFairDequeueStrategy { distributeFairQueuesFromParentQueue( parentQueue: string, consumerId: string - ): Promise>; + ): Promise>; } export const MessagePayload = z.object({ diff --git a/apps/webapp/test/envPriorityDequeueingStrategy.test.ts b/apps/webapp/test/envPriorityDequeueingStrategy.test.ts new file mode 100644 index 0000000000..e550f6cf46 --- /dev/null +++ b/apps/webapp/test/envPriorityDequeueingStrategy.test.ts @@ -0,0 +1,360 @@ +import { describe, expect, it } from "vitest"; +import type { EnvQueues, MarQSFairDequeueStrategy } from "~/v3/marqs/types.js"; +import { EnvPriorityDequeuingStrategy } from "../app/v3/marqs/envPriorityDequeuingStrategy.server.js"; +import { createKeyProducer } from "./utils/marqs.js"; + +const keyProducer = createKeyProducer("test"); + +describe("EnvPriorityDequeuingStrategy", () => { + class TestDelegate implements MarQSFairDequeueStrategy { + constructor(private queues: EnvQueues[]) {} + + async distributeFairQueuesFromParentQueue(): Promise> { + return this.queues; + } + } + + describe("distributeFairQueuesFromParentQueue", () => { + it("should preserve order when all queues have the same priority", async () => { + const inputQueues: EnvQueues[] = [ + { + envId: "env1", + queues: [ + "org:org1:env:env1:queue:queue1:priority:1", + "org:org1:env:env1:queue:queue2:priority:1", + "org:org1:env:env1:queue:queue3:priority:1", + ], + }, + ]; + + const delegate = new TestDelegate(inputQueues); + const strategy = new EnvPriorityDequeuingStrategy({ + delegate, + keys: keyProducer, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue("parentQueue", "consumer1"); + + expect(result).toEqual(inputQueues); + expect(result[0].queues).toEqual(inputQueues[0].queues); + }); + + it("should sort queues by priority in descending order", async () => { + const inputQueues: EnvQueues[] = [ + { + envId: "env1", + queues: [ + "org:org1:env:env1:queue:queue1:priority:1", + "org:org1:env:env1:queue:queue2:priority:3", + "org:org1:env:env1:queue:queue3:priority:2", + ], + }, + ]; + + const delegate = new TestDelegate(inputQueues); + const strategy = new EnvPriorityDequeuingStrategy({ + delegate, + keys: keyProducer, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue("parentQueue", "consumer1"); + + expect(result[0].queues).toEqual([ + "org:org1:env:env1:queue:queue2:priority:3", + "org:org1:env:env1:queue:queue3:priority:2", + "org:org1:env:env1:queue:queue1:priority:1", + ]); + }); + + it("should handle queues without priority by treating them as priority 0", async () => { + const inputQueues: EnvQueues[] = [ + { + envId: "env1", + queues: [ + "org:org1:env:env1:queue:queue1", + "org:org1:env:env1:queue:queue2:priority:2", + "org:org1:env:env1:queue:queue3", + ], + }, + ]; + + const delegate = new TestDelegate(inputQueues); + const strategy = new EnvPriorityDequeuingStrategy({ + delegate, + keys: keyProducer, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue("parentQueue", "consumer1"); + + expect(result[0].queues).toEqual([ + "org:org1:env:env1:queue:queue2:priority:2", + "org:org1:env:env1:queue:queue1", + "org:org1:env:env1:queue:queue3", + ]); + }); + + it("should handle multiple environments", async () => { + const inputQueues: EnvQueues[] = [ + { + envId: "env1", + queues: [ + "org:org1:env:env1:queue:queue1:priority:1", + "org:org1:env:env1:queue:queue2:priority:2", + ], + }, + { + envId: "env2", + queues: [ + "org:org1:env:env2:queue:queue3:priority:3", + "org:org1:env:env2:queue:queue4:priority:1", + ], + }, + ]; + + const delegate = new TestDelegate(inputQueues); + const strategy = new EnvPriorityDequeuingStrategy({ + delegate, + keys: keyProducer, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue("parentQueue", "consumer1"); + + expect(result).toHaveLength(2); + expect(result[0].queues).toEqual([ + "org:org1:env:env1:queue:queue2:priority:2", + "org:org1:env:env1:queue:queue1:priority:1", + ]); + expect(result[1].queues).toEqual([ + "org:org1:env:env2:queue:queue3:priority:3", + "org:org1:env:env2:queue:queue4:priority:1", + ]); + }); + + it("should handle negative priorities correctly", async () => { + const inputQueues: EnvQueues[] = [ + { + envId: "env1", + queues: [ + "org:org1:env:env1:queue:queue1:priority:-1", + "org:org1:env:env1:queue:queue2:priority:1", + "org:org1:env:env1:queue:queue3:priority:-2", + ], + }, + ]; + + const delegate = new TestDelegate(inputQueues); + const strategy = new EnvPriorityDequeuingStrategy({ + delegate, + keys: keyProducer, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue("parentQueue", "consumer1"); + + expect(result[0].queues).toEqual([ + "org:org1:env:env1:queue:queue2:priority:1", + "org:org1:env:env1:queue:queue1:priority:-1", + "org:org1:env:env1:queue:queue3:priority:-2", + ]); + }); + + it("should maintain stable sort for mixed priority and non-priority queues", async () => { + const inputQueues: EnvQueues[] = [ + { + envId: "env1", + queues: [ + "org:org1:env:env1:queue:queue1", + "org:org1:env:env1:queue:queue2:priority:1", + "org:org1:env:env1:queue:queue3", + "org:org1:env:env1:queue:queue4:priority:1", + "org:org1:env:env1:queue:queue5", + ], + }, + ]; + + const delegate = new TestDelegate(inputQueues); + const strategy = new EnvPriorityDequeuingStrategy({ + delegate, + keys: keyProducer, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue("parentQueue", "consumer1"); + + // Check that queue2 and queue4 (priority 1) maintain their relative order + // and queue1, queue3, and queue5 (priority 0) maintain their relative order + expect(result[0].queues).toEqual([ + "org:org1:env:env1:queue:queue2:priority:1", + "org:org1:env:env1:queue:queue4:priority:1", + "org:org1:env:env1:queue:queue1", + "org:org1:env:env1:queue:queue3", + "org:org1:env:env1:queue:queue5", + ]); + }); + + it("should handle empty queue arrays", async () => { + const inputQueues: EnvQueues[] = [ + { + envId: "env1", + queues: [], + }, + ]; + + const delegate = new TestDelegate(inputQueues); + const strategy = new EnvPriorityDequeuingStrategy({ + delegate, + keys: keyProducer, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue("parentQueue", "consumer1"); + + expect(result).toEqual(inputQueues); + expect(result[0].queues).toEqual([]); + }); + + it("should handle empty environments array", async () => { + const inputQueues: EnvQueues[] = []; + + const delegate = new TestDelegate(inputQueues); + const strategy = new EnvPriorityDequeuingStrategy({ + delegate, + keys: keyProducer, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue("parentQueue", "consumer1"); + + expect(result).toEqual([]); + }); + + it("should handle large priority differences", async () => { + const inputQueues: EnvQueues[] = [ + { + envId: "env1", + queues: [ + "org:org1:env:env1:queue:queue1:priority:1", + "org:org1:env:env1:queue:queue2:priority:1000", + "org:org1:env:env1:queue:queue3:priority:500", + ], + }, + ]; + + const delegate = new TestDelegate(inputQueues); + const strategy = new EnvPriorityDequeuingStrategy({ + delegate, + keys: keyProducer, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue("parentQueue", "consumer1"); + + expect(result[0].queues).toEqual([ + "org:org1:env:env1:queue:queue2:priority:1000", + "org:org1:env:env1:queue:queue3:priority:500", + "org:org1:env:env1:queue:queue1:priority:1", + ]); + }); + + it("should handle multiple environments with mixed priority patterns", async () => { + const inputQueues: EnvQueues[] = [ + { + envId: "env1", + queues: [ + "org:org1:env:env1:queue:queue1", // priority 0 + "org:org1:env:env1:queue:queue2:priority:2", + ], + }, + { + envId: "env2", + queues: [ + "org:org1:env:env2:queue:queue3:priority:1", + "org:org1:env:env2:queue:queue4", // priority 0 + ], + }, + { + envId: "env3", + queues: [ + "org:org1:env:env3:queue:queue5:priority:1", + "org:org1:env:env3:queue:queue6:priority:1", + ], + }, + ]; + + const delegate = new TestDelegate(inputQueues); + const strategy = new EnvPriorityDequeuingStrategy({ + delegate, + keys: keyProducer, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue("parentQueue", "consumer1"); + + expect(result).toHaveLength(3); + expect(result[0].queues).toEqual([ + "org:org1:env:env1:queue:queue2:priority:2", + "org:org1:env:env1:queue:queue1", + ]); + expect(result[1].queues).toEqual([ + "org:org1:env:env2:queue:queue3:priority:1", + "org:org1:env:env2:queue:queue4", + ]); + expect(result[2].queues).toEqual([ + "org:org1:env:env3:queue:queue5:priority:1", + "org:org1:env:env3:queue:queue6:priority:1", + ]); + }); + + it("should sort queues with concurrency keys while maintaining priority order", async () => { + const inputQueues: EnvQueues[] = [ + { + envId: "env1", + queues: [ + "org:org1:env:env1:queue:queue1:ck:key1:priority:1", + "org:org1:env:env1:queue:queue2:ck:key1:priority:3", + "org:org1:env:env1:queue:queue3:ck:key2:priority:2", + ], + }, + ]; + + const delegate = new TestDelegate(inputQueues); + const strategy = new EnvPriorityDequeuingStrategy({ + delegate, + keys: keyProducer, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue("parentQueue", "consumer1"); + + expect(result[0].queues).toEqual([ + "org:org1:env:env1:queue:queue2:ck:key1:priority:3", + "org:org1:env:env1:queue:queue3:ck:key2:priority:2", + "org:org1:env:env1:queue:queue1:ck:key1:priority:1", + ]); + }); + + it("should handle mixed queues with and without concurrency keys", async () => { + const inputQueues: EnvQueues[] = [ + { + envId: "env1", + queues: [ + "org:org1:env:env1:queue:queue1:priority:1", + "org:org1:env:env1:queue:queue2:ck:shared-key:priority:2", + "org:org1:env:env1:queue:queue3:ck:shared-key:priority:1", + "org:org1:env:env1:queue:queue4:priority:3", + "org:org1:env:env1:queue:queue5:ck:other-key:priority:2", + ], + }, + ]; + + const delegate = new TestDelegate(inputQueues); + const strategy = new EnvPriorityDequeuingStrategy({ + delegate, + keys: keyProducer, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue("parentQueue", "consumer1"); + + expect(result[0].queues).toEqual([ + "org:org1:env:env1:queue:queue4:priority:3", + "org:org1:env:env1:queue:queue2:ck:shared-key:priority:2", + "org:org1:env:env1:queue:queue5:ck:other-key:priority:2", + "org:org1:env:env1:queue:queue1:priority:1", + "org:org1:env:env1:queue:queue3:ck:shared-key:priority:1", + ]); + }); + }); +}); diff --git a/apps/webapp/test/fairDequeuingStrategy.test.ts b/apps/webapp/test/fairDequeuingStrategy.test.ts index c608863198..5d0b8e4af8 100644 --- a/apps/webapp/test/fairDequeuingStrategy.test.ts +++ b/apps/webapp/test/fairDequeuingStrategy.test.ts @@ -8,6 +8,7 @@ import { setupQueue, } from "./utils/marqs.js"; import { trace } from "@opentelemetry/api"; +import { EnvQueues } from "~/v3/marqs/types.js"; const tracer = trace.getTracer("test"); @@ -25,7 +26,6 @@ describe("FairDequeuingStrategy", () => { seed: "test-seed-1", // for deterministic shuffling }); - // Setup a single queue await setupQueue({ redis, keyProducer, @@ -39,7 +39,10 @@ describe("FairDequeuingStrategy", () => { const result = await strategy.distributeFairQueuesFromParentQueue("parent-queue", "consumer-1"); expect(result).toHaveLength(1); - expect(result[0]).toBe("org:org-1:env:env-1:queue:queue-1"); + expect(result[0]).toEqual({ + envId: "env-1", + queues: ["org:org-1:env:env-1:queue:queue-1"], + }); }); redisTest("should respect env concurrency limits", async ({ redis }) => { @@ -107,6 +110,10 @@ describe("FairDequeuingStrategy", () => { "consumer-1" ); expect(result).toHaveLength(1); + expect(result[0]).toEqual({ + envId: "env-1", + queues: ["org:org-1:env:env-1:queue:queue-1"], + }); } ); @@ -156,11 +163,13 @@ describe("FairDequeuingStrategy", () => { const result = await strategy.distributeFairQueuesFromParentQueue("parent-queue", "consumer-1"); - expect(result).toHaveLength(2); - // Should only get the two oldest queues + expect(result).toHaveLength(1); const queue1 = keyProducer.queueKey("org-1", "env-1", "queue-1"); const queue2 = keyProducer.queueKey("org-1", "env-1", "queue-2"); - expect(result).toEqual([queue1, queue2]); + expect(result[0]).toEqual({ + envId: "env-1", + queues: [queue1, queue2], + }); }); redisTest("should reuse snapshots across calls for the same consumer", async ({ redis }) => { @@ -209,7 +218,11 @@ describe("FairDequeuingStrategy", () => { const startDistribute1 = performance.now(); - const result = await strategy.distributeFairQueuesFromParentQueue("parent-queue", "consumer-1"); + const envResult = await strategy.distributeFairQueuesFromParentQueue( + "parent-queue", + "consumer-1" + ); + const result = flattenResults(envResult); const distribute1Duration = performance.now() - startDistribute1; @@ -318,10 +331,11 @@ describe("FairDequeuingStrategy", () => { // Run multiple iterations for (let i = 0; i < iterations; i++) { - const result = await strategy.distributeFairQueuesFromParentQueue( + const envResult = await strategy.distributeFairQueuesFromParentQueue( "parent-queue", `consumer-${i % 3}` // Simulate 3 different consumers ); + const result = flattenResults(envResult); // Track positions of queues result.forEach((queueId, position) => { @@ -474,10 +488,11 @@ describe("FairDequeuingStrategy", () => { env: { id: "env-2", currentConcurrency: 0, limit: 5 }, }); - const result = await strategy.distributeFairQueuesFromParentQueue( + const envResult = await strategy.distributeFairQueuesFromParentQueue( "parent-queue", "consumer-1" ); + const result = flattenResults(envResult); // Group queues by environment const queuesByEnv = result.reduce((acc, queueId) => { @@ -586,10 +601,11 @@ describe("FairDequeuingStrategy", () => { const firstPositionCounts: Record = {}; for (let i = 0; i < iterationsPerStrategy; i++) { - const result = await strategy.distributeFairQueuesFromParentQueue( + const envResult = await strategy.distributeFairQueuesFromParentQueue( "parent-queue", `consumer-${i % 3}` ); + const result = flattenResults(envResult); expect(result.length).toBeGreaterThan(0); @@ -668,10 +684,11 @@ describe("FairDequeuingStrategy", () => { const iterations = 1000; for (let i = 0; i < iterations; i++) { - const result = await strategy.distributeFairQueuesFromParentQueue( + const envResult = await strategy.distributeFairQueuesFromParentQueue( "parent-queue", "consumer-1" ); + const result = flattenResults(envResult); result.forEach((queueId, position) => { const baseQueueId = queueId.split(":").pop()!; @@ -798,10 +815,11 @@ describe("FairDequeuingStrategy", () => { const selectedEnvCounts: Record = {}; for (let i = 0; i < iterations; i++) { - const result = await strategy.distributeFairQueuesFromParentQueue( + const envResult = await strategy.distributeFairQueuesFromParentQueue( "parent-queue", `consumer-${i}` ); + const result = flattenResults(envResult); // Track which orgs were included in the result const selectedEnvs = new Set(result.map((queueId) => keyProducer.envIdFromQueue(queueId))); @@ -853,3 +871,8 @@ describe("FairDequeuingStrategy", () => { } ); }); + +// Helper function to flatten results for counting +function flattenResults(results: Array): string[] { + return results.flatMap((envQueue) => envQueue.queues); +} diff --git a/apps/webapp/test/marqsKeyProducer.test.ts b/apps/webapp/test/marqsKeyProducer.test.ts new file mode 100644 index 0000000000..dc531f94ce --- /dev/null +++ b/apps/webapp/test/marqsKeyProducer.test.ts @@ -0,0 +1,178 @@ +import { describe, it, expect } from "vitest"; +import { MarQSShortKeyProducer } from "../app/v3/marqs/marqsKeyProducer.server.js"; +import { MarQSKeyProducerEnv } from "~/v3/marqs/types.js"; + +describe("MarQSShortKeyProducer", () => { + const prefix = "test:"; + const producer = new MarQSShortKeyProducer(prefix); + + // Sample test data + const sampleEnv: MarQSKeyProducerEnv = { + id: "123456789012345678901234", + organizationId: "987654321098765432109876", + type: "PRODUCTION", + }; + + const devEnv: MarQSKeyProducerEnv = { + id: "123456789012345678901234", + organizationId: "987654321098765432109876", + type: "DEVELOPMENT", + }; + + describe("sharedQueueScanPattern", () => { + it("should return correct shared queue scan pattern", () => { + expect(producer.sharedQueueScanPattern()).toBe("test:*sharedQueue"); + }); + }); + + describe("queueCurrentConcurrencyScanPattern", () => { + it("should return correct queue current concurrency scan pattern", () => { + expect(producer.queueCurrentConcurrencyScanPattern()).toBe( + "test:org:*:env:*:queue:*:currentConcurrency" + ); + }); + }); + + describe("stripKeyPrefix", () => { + it("should strip prefix from key if present", () => { + expect(producer.stripKeyPrefix("test:someKey")).toBe("someKey"); + }); + + it("should return original key if prefix not present", () => { + expect(producer.stripKeyPrefix("someKey")).toBe("someKey"); + }); + }); + + describe("queueKey", () => { + it("should generate queue key with environment object", () => { + expect(producer.queueKey(sampleEnv, "testQueue")).toBe( + "org:765432109876:env:345678901234:queue:testQueue" + ); + }); + + it("should generate queue key with separate parameters", () => { + expect(producer.queueKey("org123", "env456", "testQueue")).toBe( + "org:org123:env:env456:queue:testQueue" + ); + }); + + it("should include concurrency key when provided", () => { + expect(producer.queueKey(sampleEnv, "testQueue", "concKey")).toBe( + "org:765432109876:env:345678901234:queue:testQueue:ck:concKey" + ); + }); + + it("should include priority when provided", () => { + expect(producer.queueKey(sampleEnv, "testQueue", undefined, 1)).toBe( + "org:765432109876:env:345678901234:queue:testQueue:priority:1" + ); + }); + + it("should NOT include priority when provided with 0", () => { + expect(producer.queueKey(sampleEnv, "testQueue", undefined, 0)).toBe( + "org:765432109876:env:345678901234:queue:testQueue" + ); + }); + + it("should include priority when provided with overloaded call", () => { + expect( + producer.queueKey(sampleEnv.organizationId, sampleEnv.id, "testQueue", undefined, 1) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:priority:1"); + }); + }); + + describe("envSharedQueueKey", () => { + it("should return organization-specific shared queue for development environment", () => { + expect(producer.envSharedQueueKey(devEnv)).toBe( + "org:765432109876:env:345678901234:sharedQueue" + ); + }); + + it("should return global shared queue for production environment", () => { + expect(producer.envSharedQueueKey(sampleEnv)).toBe("sharedQueue"); + }); + }); + + describe("queueDescriptorFromQueue", () => { + it("should parse queue string into descriptor", () => { + const queueString = "org:123:env:456:queue:testQueue:ck:concKey:priority:5"; + const descriptor = producer.queueDescriptorFromQueue(queueString); + + expect(descriptor).toEqual({ + name: "testQueue", + environment: "456", + organization: "123", + concurrencyKey: "concKey", + priority: 5, + }); + }); + + it("should parse queue string without optional parameters", () => { + const queueString = "org:123:env:456:queue:testQueue"; + const descriptor = producer.queueDescriptorFromQueue(queueString); + + expect(descriptor).toEqual({ + name: "testQueue", + environment: "456", + organization: "123", + concurrencyKey: undefined, + priority: undefined, + }); + }); + + it("should throw error for invalid queue string", () => { + const invalidQueue = "invalid:queue:string"; + expect(() => producer.queueDescriptorFromQueue(invalidQueue)).toThrow("Invalid queue"); + }); + }); + + describe("messageKey", () => { + it("should generate correct message key", () => { + expect(producer.messageKey("msg123")).toBe("message:msg123"); + }); + }); + + describe("nackCounterKey", () => { + it("should generate correct nack counter key", () => { + expect(producer.nackCounterKey("msg123")).toBe("message:msg123:nacks"); + }); + }); + + describe("currentConcurrencyKey", () => { + it("should generate correct current concurrency key", () => { + expect(producer.currentConcurrencyKey(sampleEnv, "testQueue")).toBe( + "org:765432109876:env:345678901234:queue:testQueue:currentConcurrency" + ); + }); + + it("should include concurrency key when provided", () => { + expect(producer.currentConcurrencyKey(sampleEnv, "testQueue", "concKey")).toBe( + "org:765432109876:env:345678901234:queue:testQueue:ck:concKey:currentConcurrency" + ); + }); + }); + + describe("envCurrentConcurrencyKey", () => { + it("should generate correct env current concurrency key with environment object", () => { + expect(producer.envCurrentConcurrencyKey(sampleEnv)).toBe( + "env:345678901234:currentConcurrency" + ); + }); + + it("should generate correct env current concurrency key with env id", () => { + expect(producer.envCurrentConcurrencyKey("env456")).toBe("env:env456:currentConcurrency"); + }); + }); + + describe("orgIdFromQueue and envIdFromQueue", () => { + it("should extract org id from queue string", () => { + const queue = "org:123:env:456:queue:testQueue"; + expect(producer.orgIdFromQueue(queue)).toBe("123"); + }); + + it("should extract env id from queue string", () => { + const queue = "org:123:env:456:queue:testQueue"; + expect(producer.envIdFromQueue(queue)).toBe("456"); + }); + }); +}); diff --git a/packages/core/src/v3/utils/flattenAttributes.ts b/packages/core/src/v3/utils/flattenAttributes.ts index 545b0184e6..e2791f21ac 100644 --- a/packages/core/src/v3/utils/flattenAttributes.ts +++ b/packages/core/src/v3/utils/flattenAttributes.ts @@ -5,7 +5,7 @@ export const CIRCULAR_REFERENCE_SENTINEL = "$@circular(("; export function flattenAttributes( obj: Record | Array | string | boolean | number | null | undefined, - prefix?: string , + prefix?: string, seen: WeakSet = new WeakSet() ): Attributes { const result: Attributes = {}; @@ -51,14 +51,13 @@ export function flattenAttributes( seen.add(obj); } - for (const [key, value] of Object.entries(obj)) { const newPrefix = `${prefix ? `${prefix}.` : ""}${Array.isArray(obj) ? `[${key}]` : key}`; if (Array.isArray(value)) { for (let i = 0; i < value.length; i++) { if (typeof value[i] === "object" && value[i] !== null) { // update null check here as well - Object.assign(result, flattenAttributes(value[i], `${newPrefix}.[${i}]`,seen)); + Object.assign(result, flattenAttributes(value[i], `${newPrefix}.[${i}]`, seen)); } else { if (value[i] === null) { result[`${newPrefix}.[${i}]`] = NULL_SENTINEL; @@ -152,7 +151,6 @@ export function unflattenAttributes( if (lastPart !== undefined) { current[lastPart] = rehydrateNull(rehydrateCircular(value)); - } } From 32817f993513e84ac7c69faad67a897bfff90b4d Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 19 Feb 2025 10:31:58 +0000 Subject: [PATCH 8/9] Implement MarQS priority queues --- apps/webapp/app/components/admin/debugRun.tsx | 340 +++++++++++ .../route.tsx | 11 +- ...dmin.api.v1.environments.$environmentId.ts | 81 ++- .../route.tsx | 2 + .../resources.taskruns.$runParam.debug.ts | 73 +++ .../envPriorityDequeuingStrategy.server.ts | 41 +- apps/webapp/app/v3/marqs/index.server.ts | 262 +++++---- ...Producer.server.ts => marqsKeyProducer.ts} | 102 ++-- apps/webapp/app/v3/marqs/types.ts | 14 +- apps/webapp/app/v3/marqs/v2.server.ts | 2 +- .../app/v3/services/completeAttempt.server.ts | 14 +- .../v3/services/createCheckpoint.server.ts | 6 +- .../v3/services/enqueueDelayedRun.server.ts | 40 +- .../app/v3/services/enqueueRun.server.ts | 4 +- .../app/v3/services/resumeBatchRun.server.ts | 38 +- .../services/resumeTaskDependency.server.ts | 9 +- .../envPriorityDequeueingStrategy.test.ts | 28 + apps/webapp/test/marqsKeyProducer.test.ts | 143 ++++- apps/webapp/test/utils/marqs.ts | 2 +- pnpm-lock.yaml | 19 + references/test-tasks/package.json | 18 + .../test-reserve-concurrency-system.ts | 545 ++++++++++++++++++ references/test-tasks/src/utils.ts | 109 ++++ references/test-tasks/trigger.config.ts | 19 + references/test-tasks/tsconfig.json | 20 + 25 files changed, 1728 insertions(+), 214 deletions(-) create mode 100644 apps/webapp/app/components/admin/debugRun.tsx create mode 100644 apps/webapp/app/routes/resources.taskruns.$runParam.debug.ts rename apps/webapp/app/v3/marqs/{marqsKeyProducer.server.ts => marqsKeyProducer.ts} (67%) create mode 100644 references/test-tasks/package.json create mode 100644 references/test-tasks/src/trigger/test-reserve-concurrency-system.ts create mode 100644 references/test-tasks/src/utils.ts create mode 100644 references/test-tasks/trigger.config.ts create mode 100644 references/test-tasks/tsconfig.json diff --git a/apps/webapp/app/components/admin/debugRun.tsx b/apps/webapp/app/components/admin/debugRun.tsx new file mode 100644 index 0000000000..894a2a3bc9 --- /dev/null +++ b/apps/webapp/app/components/admin/debugRun.tsx @@ -0,0 +1,340 @@ +import { useIsImpersonating } from "~/hooks/useOrganizations"; +import { useHasAdminAccess } from "~/hooks/useUser"; +import { Button } from "../primitives/Buttons"; +import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "../primitives/Dialog"; +import { Cog6ToothIcon } from "@heroicons/react/20/solid"; +import { type loader } from "~/routes/resources.taskruns.$runParam.debug"; +import { UseDataFunctionReturn, useTypedFetcher } from "remix-typedjson"; +import { useEffect } from "react"; +import { Spinner } from "../primitives/Spinner"; +import * as Property from "~/components/primitives/PropertyTable"; +import { ClipboardField } from "../primitives/ClipboardField"; +import { MarQSShortKeyProducer } from "~/v3/marqs/marqsKeyProducer"; + +export function AdminDebugRun({ friendlyId }: { friendlyId: string }) { + const hasAdminAccess = useHasAdminAccess(); + const isImpersonating = useIsImpersonating(); + + if (!hasAdminAccess && !isImpersonating) { + return null; + } + + return ( + + + + + + + ); +} + +export function DebugRunDialog({ friendlyId }: { friendlyId: string }) { + return ( + + + + ); +} + +function DebugRunContent({ friendlyId }: { friendlyId: string }) { + const fetcher = useTypedFetcher(); + const isLoading = fetcher.state === "loading"; + + useEffect(() => { + fetcher.load(`/resources/taskruns/${friendlyId}/debug`); + }, [friendlyId]); + + return ( + <> + Debugging run + {isLoading ? ( +
+ +
+ ) : fetcher.data ? ( + + ) : ( + <>Failed to get run debug data + )} + + ); +} + +function DebugRunData({ + run, + queueConcurrencyLimit, + queueCurrentConcurrency, + envConcurrencyLimit, + envCurrentConcurrency, + queueReserveConcurrency, + envReserveConcurrency, +}: UseDataFunctionReturn) { + const keys = new MarQSShortKeyProducer("marqs:"); + + const withPrefix = (key: string) => `marqs:${key}`; + + return ( + + + ID + + + + + + Message key + + + + + + GET message + + + + + + Queue key + + + + + + Get queue set + + + + + + Queue current concurrency key + + + + + + + Get queue current concurrency + + + + + + Queue current concurrency + + {queueCurrentConcurrency ?? "0"} + + + + Queue reserve concurrency key + + + + + + + Get queue reserve concurrency + + + + + + Queue reserve concurrency + + {queueReserveConcurrency ?? "0"} + + + + Queue concurrency limit key + + + + + + GET queue concurrency limit + + + + + + Queue concurrency limit + + {queueConcurrencyLimit ?? "Not set"} + + + + Env current concurrency key + + + + + + Get env current concurrency + + + + + + Env current concurrency + + {envCurrentConcurrency ?? "0"} + + + + Env reserve concurrency key + + + + + + Get env reserve concurrency + + + + + + Env reserve concurrency + + {envReserveConcurrency ?? "0"} + + + + Env concurrency limit key + + + + + + GET env concurrency limit + + + + + + Env concurrency limit + + {envConcurrencyLimit ?? "Not set"} + + + + Shared queue key + + + + + + Get shared queue set + + + + + + ); +} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam/route.tsx index 6b975a1a14..cf368c8473 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam/route.tsx @@ -10,7 +10,6 @@ import { MagnifyingGlassPlusIcon, StopCircleIcon, } from "@heroicons/react/20/solid"; -import type { Location } from "@remix-run/react"; import { useLoaderData, useParams, useRevalidator } from "@remix-run/react"; import { LoaderFunctionArgs, SerializeFrom, json } from "@remix-run/server-runtime"; import { Virtualizer } from "@tanstack/react-virtual"; @@ -66,7 +65,9 @@ import { useProject } from "~/hooks/useProject"; import { useReplaceSearchParams } from "~/hooks/useReplaceSearchParams"; import { Shortcut, useShortcutKeys } from "~/hooks/useShortcutKeys"; import { useUser } from "~/hooks/useUser"; -import { Run, RunPresenter } from "~/presenters/v3/RunPresenter.server"; +import { RunPresenter } from "~/presenters/v3/RunPresenter.server"; +import { getImpersonationId } from "~/services/impersonation.server"; +import { getResizableSnapshot } from "~/services/resizablePanel.server"; import { requireUserId } from "~/services/session.server"; import { cn } from "~/utils/cn"; import { lerp } from "~/utils/lerp"; @@ -79,10 +80,8 @@ import { v3RunStreamingPath, v3RunsPath, } from "~/utils/pathBuilder"; -import { SpanView } from "../resources.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam.spans.$spanParam/route"; import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route"; -import { getResizableSnapshot } from "~/services/resizablePanel.server"; -import { getImpersonationId } from "~/services/impersonation.server"; +import { SpanView } from "../resources.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam.spans.$spanParam/route"; const resizableSettings = { parent: { @@ -205,7 +204,7 @@ export default function Page() { LeadingIcon={ArrowUturnLeftIcon} shortcut={{ key: "R" }} > - Replay run… + Replay run )} +
{run.logsDeletedAt === null ? ( { + // Group queues by their base name (without priority) + const queueGroups = new Map(); + + queues.forEach((queue) => { + const descriptor = this.options.keys.queueDescriptorFromQueue(queue); + const baseQueueName = this.options.keys.queueKey( + descriptor.organization, + descriptor.environment, + descriptor.name, + descriptor.concurrencyKey + ); + + if (!queueGroups.has(baseQueueName)) { + queueGroups.set(baseQueueName, []); + } + + queueGroups.get(baseQueueName)!.push(queue); + }); + + // For each group, keep only the highest priority queue + const resultQueues: string[] = []; + queueGroups.forEach((groupQueues) => { + const sortedGroupQueues = [...groupQueues].sort((a, b) => { + const aPriority = this.#getQueuePriority(a); + const bPriority = this.#getQueuePriority(b); + + if (aPriority === bPriority) { + return 0; + } + + return bPriority - aPriority; + }); + + resultQueues.push(sortedGroupQueues[0]); + }); + + // Sort the final result by priority + const sortedQueues = resultQueues.sort((a, b) => { const aPriority = this.#getQueuePriority(a); const bPriority = this.#getQueuePriority(b); @@ -44,7 +81,7 @@ export class EnvPriorityDequeuingStrategy implements MarQSFairDequeueStrategy { return 0; } - return aPriority > bPriority ? -1 : 1; + return bPriority - aPriority; }); return { envId: env.envId, queues: sortedQueues }; diff --git a/apps/webapp/app/v3/marqs/index.server.ts b/apps/webapp/app/v3/marqs/index.server.ts index 49b151c80a..cef0e95024 100644 --- a/apps/webapp/app/v3/marqs/index.server.ts +++ b/apps/webapp/app/v3/marqs/index.server.ts @@ -22,11 +22,12 @@ import { concurrencyTracker } from "../services/taskRunConcurrencyTracker.server import { attributesFromAuthenticatedEnv, tracer } from "../tracer.server"; import { AsyncWorker } from "./asyncWorker.server"; import { FairDequeuingStrategy } from "./fairDequeuingStrategy.server"; -import { MarQSShortKeyProducer } from "./marqsKeyProducer.server"; +import { MarQSShortKeyProducer } from "./marqsKeyProducer"; import { EnqueueMessageReserveConcurrencyOptions, MarQSFairDequeueStrategy, MarQSKeyProducer, + MarQSKeyProducerEnv, MessagePayload, MessageQueueSubscriber, VisibilityTimeoutStrategy, @@ -64,6 +65,11 @@ export type MarQSOptions = { subscriber?: MessageQueueSubscriber; }; +export const MarQSPriorityLevel = { + resume: 100, + retry: 10, +} as const; + /** * MarQS - Multitenant Asynchronous Reliable Queueing System (pronounced "markus") */ @@ -115,13 +121,13 @@ export class MarQS { }); } - public async getQueueConcurrencyLimit(env: AuthenticatedEnvironment, queue: string) { + public async getQueueConcurrencyLimit(env: MarQSKeyProducerEnv, queue: string) { const result = await this.redis.get(this.keys.queueConcurrencyLimitKey(env, queue)); return result ? Number(result) : undefined; } - public async getEnvConcurrencyLimit(env: AuthenticatedEnvironment) { + public async getEnvConcurrencyLimit(env: MarQSKeyProducerEnv) { const result = await this.redis.get(this.keys.envConcurrencyLimitKey(env)); return result ? Number(result) : this.options.defaultEnvConcurrency; @@ -135,7 +141,7 @@ export class MarQS { return this.redis.zcard(this.keys.queueKey(env, queue, concurrencyKey)); } - public async lengthOfEnvQueue(env: AuthenticatedEnvironment) { + public async lengthOfEnvQueue(env: MarQSKeyProducerEnv) { return this.redis.zcard(this.keys.envQueueKey(env)); } @@ -160,17 +166,31 @@ export class MarQS { } public async currentConcurrencyOfQueue( - env: AuthenticatedEnvironment, + env: MarQSKeyProducerEnv, + queue: string, + concurrencyKey?: string + ) { + return this.redis.scard(this.keys.queueCurrentConcurrencyKey(env, queue, concurrencyKey)); + } + + public async reserveConcurrencyOfQueue( + env: MarQSKeyProducerEnv, queue: string, concurrencyKey?: string ) { - return this.redis.scard(this.keys.currentConcurrencyKey(env, queue, concurrencyKey)); + return this.redis.scard( + this.keys.queueReserveConcurrencyKeyFromQueue(this.keys.queueKey(env, queue, concurrencyKey)) + ); } - public async currentConcurrencyOfEnvironment(env: AuthenticatedEnvironment) { + public async currentConcurrencyOfEnvironment(env: MarQSKeyProducerEnv) { return this.redis.scard(this.keys.envCurrentConcurrencyKey(env)); } + public async reserveConcurrencyOfEnvironment(env: MarQSKeyProducerEnv) { + return this.redis.scard(this.keys.envReserveConcurrencyKey(env.id)); + } + public async enqueueMessage( env: AuthenticatedEnvironment, queue: string, @@ -241,6 +261,7 @@ export class MarQS { messageId: string, messageData: Record, timestamp?: number, + priority?: number, inplace?: boolean ) { return this.#trace( @@ -252,8 +273,10 @@ export class MarQS { return; } + const queue = this.keys.queueKeyFromQueue(oldMessage.queue, priority); + span.setAttributes({ - [SemanticAttributes.QUEUE]: oldMessage.queue, + [SemanticAttributes.QUEUE]: queue, [SemanticAttributes.MESSAGE_ID]: oldMessage.messageId, [SemanticAttributes.CONCURRENCY_KEY]: oldMessage.concurrencyKey, [SemanticAttributes.PARENT_QUEUE]: oldMessage.parentQueue, @@ -267,8 +290,8 @@ export class MarQS { const newMessage: MessagePayload = { version: "1", // preserve original trace context - data: { ...oldMessage.data, ...messageData, ...traceContext }, - queue: oldMessage.queue, + data: { ...oldMessage.data, ...messageData, ...traceContext, queue }, + queue, concurrencyKey: oldMessage.concurrencyKey, timestamp: timestamp ?? Date.now(), messageId, @@ -579,21 +602,12 @@ export class MarQS { }); if (updates) { - await this.replaceMessage(messageId, updates, retryAt, true); + await this.replaceMessage(messageId, updates, retryAt, undefined, true); } await this.options.visibilityTimeoutStrategy.cancelHeartbeat(messageId); - await this.#callNackMessage({ - messageKey: this.keys.messageKey(messageId), - messageQueue: message.queue, - parentQueue: message.parentQueue, - concurrencyKey: this.keys.currentConcurrencyKeyFromQueue(message.queue), - envConcurrencyKey: this.keys.envCurrentConcurrencyKeyFromQueue(message.queue), - nackCounterKey: this.keys.nackCounterKey(messageId), - messageId, - messageScore: retryAt, - }); + await this.#callNackMessage(messageId, message, retryAt); await this.options.subscriber?.messageNacked(message); @@ -882,7 +896,7 @@ export class MarQS { const queueKey = message.queue; const parentQueueKey = message.parentQueue; const messageKey = this.keys.messageKey(message.messageId); - const queueCurrentConcurrencyKey = this.keys.currentConcurrencyKeyFromQueue(message.queue); + const queueCurrentConcurrencyKey = this.keys.queueCurrentConcurrencyKeyFromQueue(message.queue); const queueReserveConcurrencyKey = this.keys.queueReserveConcurrencyKeyFromQueue(message.queue); const envCurrentConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(message.queue); const envReserveConcurrencyKey = this.keys.envReserveConcurrencyKeyFromQueue(message.queue); @@ -1009,7 +1023,7 @@ export class MarQS { return true; } else { - const queueConcurrencyLimitKey = this.keys.concurrencyLimitKeyFromQueue(message.queue); + const queueConcurrencyLimitKey = this.keys.queueConcurrencyLimitKeyFromQueue(message.queue); logger.debug("Calling enqueueMessageWithReservingConcurrencyForRecursiveQueue", { service: this.name, @@ -1082,20 +1096,33 @@ export class MarQS { messageQueue: string; parentQueue: string; }) { - const concurrencyLimitKey = this.keys.concurrencyLimitKeyFromQueue(messageQueue); - const currentConcurrencyKey = this.keys.currentConcurrencyKeyFromQueue(messageQueue); + const queueConcurrencyLimitKey = this.keys.queueConcurrencyLimitKeyFromQueue(messageQueue); + const queueCurrentConcurrencyKey = this.keys.queueCurrentConcurrencyKeyFromQueue(messageQueue); const envConcurrencyLimitKey = this.keys.envConcurrencyLimitKeyFromQueue(messageQueue); const envCurrentConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue); const envReserveConcurrencyKey = this.keys.envReserveConcurrencyKeyFromQueue(messageQueue); const queueReserveConcurrencyKey = this.keys.queueReserveConcurrencyKeyFromQueue(messageQueue); const envQueueKey = this.keys.envQueueKeyFromQueue(messageQueue); + logger.debug("Calling dequeueMessage", { + messageQueue, + parentQueue, + queueConcurrencyLimitKey, + envConcurrencyLimitKey, + queueCurrentConcurrencyKey, + queueReserveConcurrencyKey, + envCurrentConcurrencyKey, + envReserveConcurrencyKey, + envQueueKey, + service: this.name, + }); + const result = await this.redis.dequeueMessage( messageQueue, parentQueue, - concurrencyLimitKey, + queueConcurrencyLimitKey, envConcurrencyLimitKey, - currentConcurrencyKey, + queueCurrentConcurrencyKey, queueReserveConcurrencyKey, envCurrentConcurrencyKey, envReserveConcurrencyKey, @@ -1146,7 +1173,7 @@ export class MarQS { messageId: string; }) { const messageKey = this.keys.messageKey(messageId); - const concurrencyKey = this.keys.currentConcurrencyKeyFromQueue(messageQueue); + const concurrencyKey = this.keys.queueCurrentConcurrencyKeyFromQueue(messageQueue); const envConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(messageQueue); const envReserveConcurrencyKey = this.keys.envReserveConcurrencyKeyFromQueue(messageQueue); const queueReserveConcurrencyKey = this.keys.queueReserveConcurrencyKeyFromQueue(messageQueue); @@ -1177,46 +1204,38 @@ export class MarQS { ); } - async #callNackMessage({ - messageKey, - messageQueue, - parentQueue, - concurrencyKey, - envConcurrencyKey, - nackCounterKey, - messageId, - messageScore, - }: { - messageKey: string; - messageQueue: string; - parentQueue: string; - concurrencyKey: string; - envConcurrencyKey: string; - nackCounterKey: string; - messageId: string; - messageScore: number; - }) { + async #callNackMessage(messageId: string, message: MessagePayload, messageScore: number) { + const messageKey = this.keys.messageKey(message.messageId); + const queueKey = message.queue; + const parentQueueKey = message.parentQueue; + const queueCurrentConcurrencyKey = this.keys.queueCurrentConcurrencyKeyFromQueue(message.queue); + const envCurrentConcurrencyKey = this.keys.envCurrentConcurrencyKeyFromQueue(message.queue); + const nackCounterKey = this.keys.nackCounterKey(message.messageId); + const envQueueKey = this.keys.envQueueKeyFromQueue(message.queue); + const queueName = message.queue; + logger.debug("Calling nackMessage", { messageKey, - messageQueue, - parentQueue, - concurrencyKey, - envConcurrencyKey, + queueKey, + parentQueueKey, + queueCurrentConcurrencyKey, + envCurrentConcurrencyKey, nackCounterKey, messageId, messageScore, + envQueueKey, service: this.name, }); return this.redis.nackMessage( messageKey, - messageQueue, - parentQueue, - concurrencyKey, - envConcurrencyKey, - this.keys.envQueueKeyFromQueue(messageQueue), + queueKey, + parentQueueKey, + queueCurrentConcurrencyKey, + envCurrentConcurrencyKey, + envQueueKey, nackCounterKey, - messageQueue, + queueName, messageId, String(Date.now()), String(messageScore) @@ -1444,17 +1463,17 @@ return true this.redis.defineCommand("dequeueMessage", { numberOfKeys: 9, lua: ` -local childQueue = KEYS[1] -local parentQueue = KEYS[2] -local concurrencyLimitKey = KEYS[3] +local queueKey = KEYS[1] +local parentQueueKey = KEYS[2] +local queueConcurrencyLimitKey = KEYS[3] local envConcurrencyLimitKey = KEYS[4] -local currentConcurrencyKey = KEYS[5] +local queueCurrentConcurrencyKey = KEYS[5] local queueReserveConcurrencyKey = KEYS[6] local envCurrentConcurrencyKey = KEYS[7] local envReserveConcurrencyKey = KEYS[8] local envQueueKey = KEYS[9] -local childQueueName = ARGV[1] +local queueName = ARGV[1] local currentTime = tonumber(ARGV[2]) local defaultEnvConcurrencyLimit = ARGV[3] @@ -1469,18 +1488,18 @@ if envCurrentConcurrency >= totalEnvConcurrencyLimit then end -- Check current queue concurrency against the limit -local currentConcurrency = tonumber(redis.call('SCARD', currentConcurrencyKey) or '0') -local concurrencyLimit = math.min(tonumber(redis.call('GET', concurrencyLimitKey) or '1000000'), envConcurrencyLimit) +local queueCurrentConcurrency = tonumber(redis.call('SCARD', queueCurrentConcurrencyKey) or '0') +local queueConcurrencyLimit = math.min(tonumber(redis.call('GET', queueConcurrencyLimitKey) or '1000000'), envConcurrencyLimit) local queueReserveConcurrency = tonumber(redis.call('SCARD', queueReserveConcurrencyKey) or '0') -local totalQueueConcurrencyLimit = concurrencyLimit + queueReserveConcurrency +local totalQueueConcurrencyLimit = queueConcurrencyLimit + queueReserveConcurrency -- Check condition only if concurrencyLimit exists -if currentConcurrency >= totalQueueConcurrencyLimit then +if queueCurrentConcurrency >= totalQueueConcurrencyLimit then return nil end -- Attempt to dequeue the next message -local messages = redis.call('ZRANGEBYSCORE', childQueue, '-inf', currentTime, 'WITHSCORES', 'LIMIT', 0, 1) +local messages = redis.call('ZRANGEBYSCORE', queueKey, '-inf', currentTime, 'WITHSCORES', 'LIMIT', 0, 1) if #messages == 0 then return nil @@ -1490,9 +1509,9 @@ local messageId = messages[1] local messageScore = tonumber(messages[2]) -- Remove the message from the queue and update concurrency -redis.call('ZREM', childQueue, messageId) +redis.call('ZREM', queueKey, messageId) redis.call('ZREM', envQueueKey, messageId) -redis.call('SADD', currentConcurrencyKey, messageId) +redis.call('SADD', queueCurrentConcurrencyKey, messageId) redis.call('SADD', envCurrentConcurrencyKey, messageId) -- Remove the message from the reserve concurrency set @@ -1502,11 +1521,11 @@ redis.call('SREM', envReserveConcurrencyKey, messageId) redis.call('SREM', queueReserveConcurrencyKey, messageId) -- Rebalance the parent queue -local earliestMessage = redis.call('ZRANGE', childQueue, 0, 0, 'WITHSCORES') +local earliestMessage = redis.call('ZRANGE', queueKey, 0, 0, 'WITHSCORES') if #earliestMessage == 0 then - redis.call('ZREM', parentQueue, childQueueName) + redis.call('ZREM', parentQueueKey, queueName) else - redis.call('ZADD', parentQueue, earliestMessage[2], childQueueName) + redis.call('ZADD', parentQueueKey, earliestMessage[2], queueName) end return {messageId, messageScore} -- Return message details @@ -1535,45 +1554,36 @@ redis.call('SET', messageKey, messageData, 'GET') this.redis.defineCommand("acknowledgeMessage", { numberOfKeys: 8, lua: ` -local parentQueue = KEYS[1] +local parentQueueKey = KEYS[1] local messageKey = KEYS[2] -local messageQueue = KEYS[3] -local concurrencyKey = KEYS[4] +local queueKey = KEYS[3] +local queueConcurrencyKey = KEYS[4] local queueReserveConcurrencyKey = KEYS[5] local envCurrentConcurrencyKey = KEYS[6] local envReserveConcurrencyKey = KEYS[7] local envQueueKey = KEYS[8] --- Args: messageId, messageQueueName local messageId = ARGV[1] -local messageQueueName = ARGV[2] - --- Remove the message from the message key -redis.call('DEL', messageKey) +local queueName = ARGV[2] -- Remove the message from the queue -redis.call('ZREM', messageQueue, messageId) - --- Remove the message from the env queue -redis.call('ZREM', envQueueKey, messageId) - --- Remove the message from the reserve concurrency set -redis.call('SREM', envReserveConcurrencyKey, messageId) - --- Remove the message from the queue reserve concurrency set -redis.call('SREM', queueReserveConcurrencyKey, messageId) +redis.call('ZREM', queueKey, messageId) -- Rebalance the parent queue -local earliestMessage = redis.call('ZRANGE', messageQueue, 0, 0, 'WITHSCORES') +local earliestMessage = redis.call('ZRANGE', queueKey, 0, 0, 'WITHSCORES') if #earliestMessage == 0 then - redis.call('ZREM', parentQueue, messageQueueName) + redis.call('ZREM', parentQueueKey, queueName) else - redis.call('ZADD', parentQueue, earliestMessage[2], messageQueueName) + redis.call('ZADD', parentQueueKey, earliestMessage[2], queueName) end -- Update the concurrency keys -redis.call('SREM', concurrencyKey, messageId) +redis.call('SREM', queueConcurrencyKey, messageId) redis.call('SREM', envCurrentConcurrencyKey, messageId) +redis.call('SREM', envReserveConcurrencyKey, messageId) +redis.call('SREM', queueReserveConcurrencyKey, messageId) +redis.call('ZREM', envQueueKey, messageId) +redis.call('DEL', messageKey) `, }); @@ -1581,25 +1591,24 @@ redis.call('SREM', envCurrentConcurrencyKey, messageId) numberOfKeys: 7, lua: ` local messageKey = KEYS[1] -local childQueueKey = KEYS[2] +local queueKey = KEYS[2] local parentQueueKey = KEYS[3] -local concurrencyKey = KEYS[4] -local envConcurrencyKey = KEYS[5] +local queueCurrentConcurrencyKey = KEYS[4] +local envCurrentConcurrencyKey = KEYS[5] local envQueueKey = KEYS[6] local nackCounterKey = KEYS[7] --- Args: childQueueName, messageId, currentTime, messageScore -local childQueueName = ARGV[1] +local queueName = ARGV[1] local messageId = ARGV[2] local currentTime = tonumber(ARGV[3]) local messageScore = tonumber(ARGV[4]) --- Update the concurrency keys -redis.call('SREM', concurrencyKey, messageId) -redis.call('SREM', envConcurrencyKey, messageId) +-- Update the current concurrency keys +redis.call('SREM', queueCurrentConcurrencyKey, messageId) +redis.call('SREM', envCurrentConcurrencyKey, messageId) -- Enqueue the message into the queue -redis.call('ZADD', childQueueKey, messageScore, messageId) +redis.call('ZADD', queueKey, messageScore, messageId) -- Enqueue the message into the env queue redis.call('ZADD', envQueueKey, messageScore, messageId) @@ -1609,11 +1618,11 @@ redis.call('INCR', nackCounterKey) redis.call('EXPIRE', nackCounterKey, 2592000) -- Rebalance the parent queue -local earliestMessage = redis.call('ZRANGE', childQueueKey, 0, 0, 'WITHSCORES') +local earliestMessage = redis.call('ZRANGE', queueKey, 0, 0, 'WITHSCORES') if #earliestMessage == 0 then - redis.call('ZREM', parentQueueKey, childQueueName) + redis.call('ZREM', parentQueueKey, queueName) else - redis.call('ZADD', parentQueueKey, earliestMessage[2], childQueueName) + redis.call('ZADD', parentQueueKey, earliestMessage[2], queueName) end `, }); @@ -1719,16 +1728,16 @@ declare module "ioredis" { ): Result; dequeueMessage( - childQueue: string, - parentQueue: string, - concurrencyLimitKey: string, + queueKey: string, + parentQueueKey: string, + queueConcurrencyLimitKey: string, envConcurrencyLimitKey: string, - currentConcurrencyKey: string, + queueCurrentConcurrencyKey: string, queueReserveConcurrencyKey: string, envCurrentConcurrencyKey: string, envReserveConcurrencyKey: string, envQueueKey: string, - childQueueName: string, + queueName: string, currentTime: string, defaultEnvConcurrencyLimit: string, callback?: Callback<[string, string]> @@ -1756,13 +1765,13 @@ declare module "ioredis" { nackMessage( messageKey: string, - childQueueKey: string, + queueKey: string, parentQueueKey: string, - concurrencyKey: string, - envConcurrencyKey: string, + queueCurrentConcurrencyKey: string, + envCurrentConcurrencyKey: string, envQueueKey: string, nackCounterKey: string, - childQueueName: string, + queueName: string, messageId: string, currentTime: string, messageScore: string, @@ -1829,17 +1838,20 @@ function getMarQSClient() { maximumEnvCount: env.MARQS_MAXIMUM_ENV_COUNT, }), }), - envQueuePriorityStrategy: new FairDequeuingStrategy({ - tracer: tracer, - redis, - parentQueueLimit: env.MARQS_DEV_QUEUE_LIMIT, + envQueuePriorityStrategy: new EnvPriorityDequeuingStrategy({ keys: keysProducer, - defaultEnvConcurrency: env.DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT, - biases: { - concurrencyLimitBias: 0.0, - availableCapacityBias: 0.0, - queueAgeRandomization: 0.1, - }, + delegate: new FairDequeuingStrategy({ + tracer: tracer, + redis, + parentQueueLimit: env.MARQS_DEV_QUEUE_LIMIT, + keys: keysProducer, + defaultEnvConcurrency: env.DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT, + biases: { + concurrencyLimitBias: 0.0, + availableCapacityBias: 0.0, + queueAgeRandomization: 0.1, + }, + }), }), workers: 1, redis, diff --git a/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts b/apps/webapp/app/v3/marqs/marqsKeyProducer.ts similarity index 67% rename from apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts rename to apps/webapp/app/v3/marqs/marqsKeyProducer.ts index 702bacc254..a4fbfb6a62 100644 --- a/apps/webapp/app/v3/marqs/marqsKeyProducer.server.ts +++ b/apps/webapp/app/v3/marqs/marqsKeyProducer.ts @@ -14,8 +14,8 @@ const constants = { PRIORITY_PART: "priority", } as const; -const ORG_REGEX = /org:(\w+):/; -const ENV_REGEX = /env:(\w+):/; +const ORG_REGEX = /org:([^:]+):/; +const ENV_REGEX = /env:([^:]+):/; const QUEUE_REGEX = /queue:([^:]+)(?::|$)/; const CONCURRENCY_KEY_REGEX = /ck:([^:]+)(?::|$)/; const PRIORITY_REGEX = /priority:(\d+)(?::|$)/; @@ -101,6 +101,18 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { } } + queueKeyFromQueue(queue: string, priority?: number): string { + const descriptor = this.queueDescriptorFromQueue(queue); + + return this.queueKey( + descriptor.organization, + descriptor.environment, + descriptor.name, + descriptor.concurrencyKey, + descriptor.priority ?? priority + ); + } + envSharedQueueKey(env: MarQSKeyProducerEnv) { if (env.type === "DEVELOPMENT") { return [ @@ -117,50 +129,53 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { return constants.SHARED_QUEUE; } - concurrencyLimitKeyFromQueue(queue: string) { - const concurrencyQueueName = queue.replace(/:ck:.+$/, ""); + queueConcurrencyLimitKeyFromQueue(queue: string) { + const descriptor = this.queueDescriptorFromQueue(queue); - return `${concurrencyQueueName}:${constants.CONCURRENCY_LIMIT_PART}`; + return this.queueConcurrencyLimitKeyFromDescriptor(descriptor); } - // TODO: if the queue passed in has a priority, we need to strip that out - // before adding the currentConcurrency part - currentConcurrencyKeyFromQueue(queue: string) { - return `${queue}:${constants.CURRENT_CONCURRENCY_PART}`; + queueCurrentConcurrencyKeyFromQueue(queue: string) { + const descriptor = this.queueDescriptorFromQueue(queue); + return this.currentConcurrencyKeyFromDescriptor(descriptor); } - // TODO: if the queue passed in has a priority, we need to strip that out - // before adding the currentConcurrency part queueReserveConcurrencyKeyFromQueue(queue: string) { - return `${queue}:${constants.RESERVE_CONCURRENCY_PART}`; + const descriptor = this.queueDescriptorFromQueue(queue); + + return this.queueReserveConcurrencyKeyFromDescriptor(descriptor); } - currentConcurrencyKey(env: MarQSKeyProducerEnv, queue: string, concurrencyKey?: string): string { + queueCurrentConcurrencyKey( + env: MarQSKeyProducerEnv, + queue: string, + concurrencyKey?: string + ): string { return [this.queueKey(env, queue, concurrencyKey), constants.CURRENT_CONCURRENCY_PART].join( ":" ); } envConcurrencyLimitKeyFromQueue(queue: string) { - const envId = this.normalizeQueue(queue).split(":")[3]; + const descriptor = this.queueDescriptorFromQueue(queue); - return `${constants.ENV_PART}:${envId}:${constants.CONCURRENCY_LIMIT_PART}`; + return `${constants.ENV_PART}:${descriptor.environment}:${constants.CONCURRENCY_LIMIT_PART}`; } envCurrentConcurrencyKeyFromQueue(queue: string) { - const envId = this.normalizeQueue(queue).split(":")[3]; + const descriptor = this.queueDescriptorFromQueue(queue); - return `${constants.ENV_PART}:${envId}:${constants.CURRENT_CONCURRENCY_PART}`; + return `${constants.ENV_PART}:${descriptor.environment}:${constants.CURRENT_CONCURRENCY_PART}`; } envReserveConcurrencyKeyFromQueue(queue: string) { - const envId = this.normalizeQueue(queue).split(":")[3]; + const descriptor = this.queueDescriptorFromQueue(queue); - return this.envReserveConcurrencyKey(envId); + return this.envReserveConcurrencyKey(descriptor.environment); } envReserveConcurrencyKey(envId: string): string { - return `${constants.ENV_PART}:${envId}:${constants.RESERVE_CONCURRENCY_PART}`; + return `${constants.ENV_PART}:${this.shortId(envId)}:${constants.RESERVE_CONCURRENCY_PART}`; } envCurrentConcurrencyKey(envId: string): string; @@ -173,9 +188,9 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { } envQueueKeyFromQueue(queue: string) { - const envId = this.normalizeQueue(queue).split(":")[3]; + const descriptor = this.queueDescriptorFromQueue(queue); - return `${constants.ENV_PART}:${envId}:${constants.QUEUE_PART}`; + return `${constants.ENV_PART}:${descriptor.environment}:${constants.QUEUE_PART}`; } envQueueKey(env: MarQSKeyProducerEnv): string { @@ -191,18 +206,22 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { } orgIdFromQueue(queue: string) { - return this.normalizeQueue(queue).split(":")[1]; + const descriptor = this.queueDescriptorFromQueue(queue); + + return descriptor.organization; } envIdFromQueue(queue: string) { - return this.normalizeQueue(queue).split(":")[3]; + const descriptor = this.queueDescriptorFromQueue(queue); + + return descriptor.environment; } queueDescriptorFromQueue(queue: string): QueueDescriptor { const match = queue.match(QUEUE_REGEX); if (!match) { - throw new Error(`Invalid queue: ${queue}`); + throw new Error(`Invalid queue: ${queue}, no queue name found`); } const [, queueName] = match; @@ -210,7 +229,7 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { const envMatch = queue.match(ENV_REGEX); if (!envMatch) { - throw new Error(`Invalid queue: ${queue}`); + throw new Error(`Invalid queue: ${queue}, no environment found`); } const [, envId] = envMatch; @@ -218,7 +237,7 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { const orgMatch = queue.match(ORG_REGEX); if (!orgMatch) { - throw new Error(`Invalid queue: ${queue}`); + throw new Error(`Invalid queue: ${queue}, no organization found`); } const [, orgId] = orgMatch; @@ -265,12 +284,29 @@ export class MarQSShortKeyProducer implements MarQSKeyProducer { return `${constants.PRIORITY_PART}:${priority}`; } - // This removes the leading prefix from the queue name if it exists - private normalizeQueue(queue: string) { - if (queue.startsWith(this._prefix)) { - return queue.slice(this._prefix.length); - } + private currentConcurrencyKeyFromDescriptor(descriptor: QueueDescriptor) { + return [ + this.queueKey( + descriptor.organization, + descriptor.environment, + descriptor.name, + descriptor.concurrencyKey + ), + constants.CURRENT_CONCURRENCY_PART, + ].join(":"); + } - return queue; + private queueReserveConcurrencyKeyFromDescriptor(descriptor: QueueDescriptor) { + return [ + this.queueKey(descriptor.organization, descriptor.environment, descriptor.name), + constants.RESERVE_CONCURRENCY_PART, + ].join(":"); + } + + private queueConcurrencyLimitKeyFromDescriptor(descriptor: QueueDescriptor) { + return [ + this.queueKey(descriptor.organization, descriptor.environment, descriptor.name), + constants.CONCURRENCY_LIMIT_PART, + ].join(":"); } } diff --git a/apps/webapp/app/v3/marqs/types.ts b/apps/webapp/app/v3/marqs/types.ts index 29db54f031..339f1bded9 100644 --- a/apps/webapp/app/v3/marqs/types.ts +++ b/apps/webapp/app/v3/marqs/types.ts @@ -1,4 +1,4 @@ -import { RuntimeEnvironmentType } from "@trigger.dev/database"; +import type { RuntimeEnvironmentType } from "@trigger.dev/database"; import { z } from "zod"; export type QueueRange = { offset: number; count: number }; @@ -42,14 +42,20 @@ export interface MarQSKeyProducer { priority?: number ): string; + queueKeyFromQueue(queue: string, priority?: number): string; + envQueueKey(env: MarQSKeyProducerEnv): string; envSharedQueueKey(env: MarQSKeyProducerEnv): string; sharedQueueKey(): string; sharedQueueScanPattern(): string; queueCurrentConcurrencyScanPattern(): string; - concurrencyLimitKeyFromQueue(queue: string): string; - currentConcurrencyKeyFromQueue(queue: string): string; - currentConcurrencyKey(env: MarQSKeyProducerEnv, queue: string, concurrencyKey?: string): string; + queueConcurrencyLimitKeyFromQueue(queue: string): string; + queueCurrentConcurrencyKeyFromQueue(queue: string): string; + queueCurrentConcurrencyKey( + env: MarQSKeyProducerEnv, + queue: string, + concurrencyKey?: string + ): string; envConcurrencyLimitKeyFromQueue(queue: string): string; envCurrentConcurrencyKeyFromQueue(queue: string): string; envReserveConcurrencyKeyFromQueue(queue: string): string; diff --git a/apps/webapp/app/v3/marqs/v2.server.ts b/apps/webapp/app/v3/marqs/v2.server.ts index 6d620536cf..3492f42c57 100644 --- a/apps/webapp/app/v3/marqs/v2.server.ts +++ b/apps/webapp/app/v3/marqs/v2.server.ts @@ -9,7 +9,7 @@ import { PerformRunExecutionV3Service } from "~/services/runs/performRunExecutio import { singleton } from "~/utils/singleton"; import { generateFriendlyId } from "../friendlyIdentifiers"; import { MarQS } from "./index.server"; -import { MarQSShortKeyProducer } from "./marqsKeyProducer.server"; +import { MarQSShortKeyProducer } from "./marqsKeyProducer"; import { RequeueV2Message } from "./requeueV2Message.server"; import { VisibilityTimeoutStrategy } from "./types"; import Redis from "ioredis"; diff --git a/apps/webapp/app/v3/services/completeAttempt.server.ts b/apps/webapp/app/v3/services/completeAttempt.server.ts index 235ae87352..ae5beb5bbc 100644 --- a/apps/webapp/app/v3/services/completeAttempt.server.ts +++ b/apps/webapp/app/v3/services/completeAttempt.server.ts @@ -22,7 +22,7 @@ import { env } from "~/env.server"; import { AuthenticatedEnvironment } from "~/services/apiAuth.server"; import { logger } from "~/services/logger.server"; import { safeJsonParse } from "~/utils/json"; -import { marqs } from "~/v3/marqs/index.server"; +import { marqs, MarQSPriorityLevel } from "~/v3/marqs/index.server"; import { createExceptionPropertiesFromError, eventRepository } from "../eventRepository.server"; import { FailedTaskRunRetryHelper } from "../failedTaskRun.server"; import { FAILED_RUN_STATUSES, isFinalAttemptStatus, isFinalRunStatus } from "../taskStatus"; @@ -476,7 +476,8 @@ export class CompleteAttemptService extends BaseService { checkpointEventId: this.opts.supportsRetryCheckpoints ? checkpointEventId : undefined, retryCheckpointsDisabled: !this.opts.supportsRetryCheckpoints, }, - executionRetry.timestamp + executionRetry.timestamp, + MarQSPriorityLevel.retry ); }; @@ -614,8 +615,13 @@ export class CompleteAttemptService extends BaseService { }); if (environment.type === "DEVELOPMENT") { - // This is already an EXECUTE message so we can just NACK - await marqs?.nackMessage(taskRunAttempt.taskRunId, executionRetry.timestamp); + marqs.replaceMessage( + taskRunAttempt.taskRunId, + {}, + executionRetry.timestamp, + MarQSPriorityLevel.retry + ); + return "RETRIED"; } diff --git a/apps/webapp/app/v3/services/createCheckpoint.server.ts b/apps/webapp/app/v3/services/createCheckpoint.server.ts index bbd8618898..15cdf697d9 100644 --- a/apps/webapp/app/v3/services/createCheckpoint.server.ts +++ b/apps/webapp/app/v3/services/createCheckpoint.server.ts @@ -2,7 +2,7 @@ import { CoordinatorToPlatformMessages, ManualCheckpointMetadata } from "@trigge import type { InferSocketMessageSchema } from "@trigger.dev/core/v3/zodSocket"; import type { Checkpoint, CheckpointRestoreEvent } from "@trigger.dev/database"; import { logger } from "~/services/logger.server"; -import { marqs } from "~/v3/marqs/index.server"; +import { marqs, MarQSPriorityLevel } from "~/v3/marqs/index.server"; import { generateFriendlyId } from "../friendlyIdentifiers"; import { isFreezableAttemptStatus, isFreezableRunStatus } from "../taskStatus"; import { BaseService } from "./baseService.server"; @@ -174,7 +174,8 @@ export class CreateCheckpointService extends BaseService { resumableAttemptId: attempt.id, checkpointEventId: checkpointEvent.id, }, - restoreAtUnixTimeMs + restoreAtUnixTimeMs, + MarQSPriorityLevel.resume ); return { @@ -302,6 +303,7 @@ export class CreateCheckpointService extends BaseService { checkpointEventId: checkpointEvent.id, }, undefined, + undefined, true ); diff --git a/apps/webapp/app/v3/services/enqueueDelayedRun.server.ts b/apps/webapp/app/v3/services/enqueueDelayedRun.server.ts index 6cc3268940..fd0f22f3c2 100644 --- a/apps/webapp/app/v3/services/enqueueDelayedRun.server.ts +++ b/apps/webapp/app/v3/services/enqueueDelayedRun.server.ts @@ -6,6 +6,7 @@ import { BaseService } from "./baseService.server"; import { ExpireEnqueuedRunService } from "./expireEnqueuedRun.server"; import { commonWorker } from "../commonWorker.server"; import { workerQueue } from "~/services/worker.server"; +import { enqueueRun } from "./enqueueRun.server"; export class EnqueueDelayedRunService extends BaseService { public static async enqueue(runId: string, runAt?: Date) { @@ -44,6 +45,24 @@ export class EnqueueDelayedRunService extends BaseService { project: true, }, }, + dependency: { + include: { + dependentBatchRun: { + include: { + dependentTaskAttempt: { + include: { + taskRun: true, + }, + }, + }, + }, + dependentAttempt: { + include: { + taskRun: true, + }, + }, + }, + }, }, }); @@ -83,19 +102,12 @@ export class EnqueueDelayedRunService extends BaseService { } }); - await marqs.enqueueMessage( - run.runtimeEnvironment, - run.queue, - run.id, - { - type: "EXECUTE", - taskIdentifier: run.taskIdentifier, - projectId: run.runtimeEnvironment.projectId, - environmentId: run.runtimeEnvironment.id, - environmentType: run.runtimeEnvironment.type, - }, - run.concurrencyKey ?? undefined, - run.queueTimestamp ?? undefined - ); + await enqueueRun({ + env: run.runtimeEnvironment, + run: run, + dependentRun: + run.dependency?.dependentAttempt?.taskRun ?? + run.dependency?.dependentBatchRun?.dependentTaskAttempt?.taskRun, + }); } } diff --git a/apps/webapp/app/v3/services/enqueueRun.server.ts b/apps/webapp/app/v3/services/enqueueRun.server.ts index e290b64ba6..cb091d70d9 100644 --- a/apps/webapp/app/v3/services/enqueueRun.server.ts +++ b/apps/webapp/app/v3/services/enqueueRun.server.ts @@ -1,9 +1,7 @@ +import { TaskRunError, TaskRunErrorCodes } from "@trigger.dev/core/v3/schemas"; import { TaskRun } from "@trigger.dev/database"; import { AuthenticatedEnvironment } from "~/services/apiAuth.server"; import { marqs } from "../marqs/index.server"; -import { prisma } from "~/db.server"; -import { sanitizeError } from "@trigger.dev/core/v3/errors"; -import { TaskRunError, TaskRunErrorCodes } from "@trigger.dev/core/v3/schemas"; export type EnqueueRunOptions = { env: AuthenticatedEnvironment; diff --git a/apps/webapp/app/v3/services/resumeBatchRun.server.ts b/apps/webapp/app/v3/services/resumeBatchRun.server.ts index c4a8180bca..68f5a59cf7 100644 --- a/apps/webapp/app/v3/services/resumeBatchRun.server.ts +++ b/apps/webapp/app/v3/services/resumeBatchRun.server.ts @@ -1,6 +1,6 @@ import { PrismaClientOrTransaction } from "~/db.server"; import { workerQueue } from "~/services/worker.server"; -import { marqs } from "~/v3/marqs/index.server"; +import { marqs, MarQSPriorityLevel } from "~/v3/marqs/index.server"; import { BaseService } from "./baseService.server"; import { logger } from "~/services/logger.server"; import { BatchTaskRun } from "@trigger.dev/database"; @@ -152,6 +152,8 @@ export class ResumeBatchRunService extends BaseService { queue: true, taskIdentifier: true, concurrencyKey: true, + createdAt: true, + queueTimestamp: true, }, }, }, @@ -201,7 +203,10 @@ export class ResumeBatchRunService extends BaseService { environmentId: environment.id, environmentType: environment.type, }, - dependentRun.concurrencyKey ?? undefined + dependentRun.concurrencyKey ?? undefined, + dependentRun.queueTimestamp ?? dependentRun.createdAt, + undefined, + MarQSPriorityLevel.resume ); return "COMPLETED"; @@ -247,16 +252,25 @@ export class ResumeBatchRunService extends BaseService { hasCheckpointEvent: !!batchRun.checkpointEventId, }); - await marqs?.replaceMessage(dependentRun.id, { - type: "RESUME", - completedAttemptIds: batchRun.items.map((item) => item.taskRunAttemptId).filter(Boolean), - resumableAttemptId: dependentTaskAttempt.id, - checkpointEventId: batchRun.checkpointEventId ?? undefined, - taskIdentifier: dependentTaskAttempt.taskRun.taskIdentifier, - projectId: environment.projectId, - environmentId: environment.id, - environmentType: environment.type, - }); + await marqs?.replaceMessage( + dependentRun.id, + { + type: "RESUME", + completedAttemptIds: batchRun.items + .map((item) => item.taskRunAttemptId) + .filter(Boolean), + resumableAttemptId: dependentTaskAttempt.id, + checkpointEventId: batchRun.checkpointEventId ?? undefined, + taskIdentifier: dependentTaskAttempt.taskRun.taskIdentifier, + projectId: environment.projectId, + environmentId: environment.id, + environmentType: environment.type, + }, + ( + dependentTaskAttempt.taskRun.queueTimestamp ?? dependentTaskAttempt.taskRun.createdAt + ).getTime(), + MarQSPriorityLevel.resume + ); return "COMPLETED"; } else { diff --git a/apps/webapp/app/v3/services/resumeTaskDependency.server.ts b/apps/webapp/app/v3/services/resumeTaskDependency.server.ts index b424730414..3151de18a0 100644 --- a/apps/webapp/app/v3/services/resumeTaskDependency.server.ts +++ b/apps/webapp/app/v3/services/resumeTaskDependency.server.ts @@ -1,6 +1,6 @@ import { PrismaClientOrTransaction } from "~/db.server"; import { workerQueue } from "~/services/worker.server"; -import { marqs } from "~/v3/marqs/index.server"; +import { MarQS, marqs, MarQSPriorityLevel } from "~/v3/marqs/index.server"; import { BaseService } from "./baseService.server"; import { logger } from "~/services/logger.server"; @@ -66,7 +66,9 @@ export class ResumeTaskDependencyService extends BaseService { environmentType: dependency.taskRun.runtimeEnvironment.type, }, dependentRun.concurrencyKey ?? undefined, - dependentRun.createdAt.getTime() + dependentRun.queueTimestamp ?? dependentRun.createdAt, + undefined, + MarQSPriorityLevel.resume ); } else { logger.debug("Task dependency resume: Attempt is not paused or there's no checkpoint event", { @@ -99,7 +101,8 @@ export class ResumeTaskDependencyService extends BaseService { environmentId: dependency.taskRun.runtimeEnvironment.id, environmentType: dependency.taskRun.runtimeEnvironment.type, }, - dependentRun.createdAt.getTime() + (dependentRun.queueTimestamp ?? dependentRun.createdAt).getTime(), + MarQSPriorityLevel.resume ); } } diff --git a/apps/webapp/test/envPriorityDequeueingStrategy.test.ts b/apps/webapp/test/envPriorityDequeueingStrategy.test.ts index e550f6cf46..8335136923 100644 --- a/apps/webapp/test/envPriorityDequeueingStrategy.test.ts +++ b/apps/webapp/test/envPriorityDequeueingStrategy.test.ts @@ -356,5 +356,33 @@ describe("EnvPriorityDequeuingStrategy", () => { "org:org1:env:env1:queue:queue3:ck:shared-key:priority:1", ]); }); + + it("should only return the highest priority queue of the same queue", async () => { + const inputQueues: EnvQueues[] = [ + { + envId: "env1", + queues: [ + "org:org1:env:env1:queue:queue1", + "org:org1:env:env1:queue:queue1:priority:1", + "org:org1:env:env1:queue:queue1:priority:2", + "org:org1:env:env1:queue:queue1:priority:3", + "org:org1:env:env1:queue:queue2", + ], + }, + ]; + + const delegate = new TestDelegate(inputQueues); + const strategy = new EnvPriorityDequeuingStrategy({ + delegate, + keys: keyProducer, + }); + + const result = await strategy.distributeFairQueuesFromParentQueue("parentQueue", "consumer1"); + + expect(result[0].queues).toEqual([ + "org:org1:env:env1:queue:queue1:priority:3", + "org:org1:env:env1:queue:queue2", + ]); + }); }); }); diff --git a/apps/webapp/test/marqsKeyProducer.test.ts b/apps/webapp/test/marqsKeyProducer.test.ts index dc531f94ce..1f6af24545 100644 --- a/apps/webapp/test/marqsKeyProducer.test.ts +++ b/apps/webapp/test/marqsKeyProducer.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest"; -import { MarQSShortKeyProducer } from "../app/v3/marqs/marqsKeyProducer.server.js"; +import { MarQSShortKeyProducer } from "../app/v3/marqs/marqsKeyProducer.js"; import { MarQSKeyProducerEnv } from "~/v3/marqs/types.js"; describe("MarQSShortKeyProducer", () => { @@ -81,6 +81,41 @@ describe("MarQSShortKeyProducer", () => { }); }); + describe("queueKeyFromQueue", () => { + it("should generate queue key", () => { + expect(producer.queueKeyFromQueue("org:765432109876:env:345678901234:queue:testQueue")).toBe( + "org:765432109876:env:345678901234:queue:testQueue" + ); + }); + + it("should include concurrency key when provided", () => { + expect( + producer.queueKeyFromQueue("org:765432109876:env:345678901234:queue:testQueue:ck:concKey") + ).toBe("org:765432109876:env:345678901234:queue:testQueue:ck:concKey"); + }); + + it("should include priority when provided", () => { + expect( + producer.queueKeyFromQueue("org:765432109876:env:345678901234:queue:testQueue", 1) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:priority:1"); + }); + + it("should NOT include priority when provided with 0", () => { + expect( + producer.queueKeyFromQueue("org:765432109876:env:345678901234:queue:testQueue", 0) + ).toBe("org:765432109876:env:345678901234:queue:testQueue"); + }); + + it("should NOT change the priority when provided", () => { + expect( + producer.queueKeyFromQueue( + "org:765432109876:env:345678901234:queue:testQueue:priority:1", + 10 + ) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:priority:1"); + }); + }); + describe("envSharedQueueKey", () => { it("should return organization-specific shared queue for development environment", () => { expect(producer.envSharedQueueKey(devEnv)).toBe( @@ -140,18 +175,120 @@ describe("MarQSShortKeyProducer", () => { describe("currentConcurrencyKey", () => { it("should generate correct current concurrency key", () => { - expect(producer.currentConcurrencyKey(sampleEnv, "testQueue")).toBe( + expect(producer.queueCurrentConcurrencyKey(sampleEnv, "testQueue")).toBe( "org:765432109876:env:345678901234:queue:testQueue:currentConcurrency" ); }); it("should include concurrency key when provided", () => { - expect(producer.currentConcurrencyKey(sampleEnv, "testQueue", "concKey")).toBe( + expect(producer.queueCurrentConcurrencyKey(sampleEnv, "testQueue", "concKey")).toBe( "org:765432109876:env:345678901234:queue:testQueue:ck:concKey:currentConcurrency" ); }); }); + describe("currentConcurrencyKeyFromQueue", () => { + it("should generate correct current concurrency key", () => { + expect( + producer.queueCurrentConcurrencyKeyFromQueue( + "org:765432109876:env:345678901234:queue:testQueue" + ) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:currentConcurrency"); + }); + + it("should include concurrency key when provided", () => { + expect( + producer.queueCurrentConcurrencyKeyFromQueue( + "org:765432109876:env:345678901234:queue:testQueue:ck:concKey" + ) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:ck:concKey:currentConcurrency"); + }); + + it("should remove the priority bit when provided", () => { + expect( + producer.queueCurrentConcurrencyKeyFromQueue( + "org:765432109876:env:345678901234:queue:testQueue:priority:1" + ) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:currentConcurrency"); + }); + + it("should remove the priority bit when provided, but keep the concurrency key", () => { + expect( + producer.queueCurrentConcurrencyKeyFromQueue( + "org:765432109876:env:345678901234:queue:testQueue:ck:concKey:priority:1" + ) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:ck:concKey:currentConcurrency"); + }); + }); + + describe("queueReserveConcurrencyKeyFromQueue", () => { + it("should generate correct queue reserve concurrency key", () => { + expect( + producer.queueReserveConcurrencyKeyFromQueue( + "org:765432109876:env:345678901234:queue:testQueue" + ) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:reserveConcurrency"); + }); + + it("should NOT include the concurrency key when provided", () => { + expect( + producer.queueReserveConcurrencyKeyFromQueue( + "org:765432109876:env:345678901234:queue:testQueue:ck:concKey" + ) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:reserveConcurrency"); + }); + + it("should remove the priority bit when provided", () => { + expect( + producer.queueReserveConcurrencyKeyFromQueue( + "org:765432109876:env:345678901234:queue:testQueue:priority:1" + ) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:reserveConcurrency"); + }); + + it("should remove the priority bit when provided, AND remove the concurrency key", () => { + expect( + producer.queueReserveConcurrencyKeyFromQueue( + "org:765432109876:env:345678901234:queue:testQueue:ck:concKey:priority:1" + ) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:reserveConcurrency"); + }); + }); + + describe("queueConcurrencyLimitKeyFromQueue", () => { + it("should generate correct queue concurrency limit key", () => { + expect( + producer.queueConcurrencyLimitKeyFromQueue( + "org:765432109876:env:345678901234:queue:testQueue" + ) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:concurrency"); + }); + + it("should NOT include the concurrency key when provided", () => { + expect( + producer.queueConcurrencyLimitKeyFromQueue( + "org:765432109876:env:345678901234:queue:testQueue:ck:concKey" + ) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:concurrency"); + }); + + it("should remove the priority bit when provided", () => { + expect( + producer.queueConcurrencyLimitKeyFromQueue( + "org:765432109876:env:345678901234:queue:testQueue:priority:1" + ) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:concurrency"); + }); + + it("should remove the priority bit when provided, AND remove the concurrency key", () => { + expect( + producer.queueConcurrencyLimitKeyFromQueue( + "org:765432109876:env:345678901234:queue:testQueue:ck:concKey:priority:1" + ) + ).toBe("org:765432109876:env:345678901234:queue:testQueue:concurrency"); + }); + }); + describe("envCurrentConcurrencyKey", () => { it("should generate correct env current concurrency key with environment object", () => { expect(producer.envCurrentConcurrencyKey(sampleEnv)).toBe( diff --git a/apps/webapp/test/utils/marqs.ts b/apps/webapp/test/utils/marqs.ts index b303aa15b1..4fc3884926 100644 --- a/apps/webapp/test/utils/marqs.ts +++ b/apps/webapp/test/utils/marqs.ts @@ -1,5 +1,5 @@ import { MarQSKeyProducer } from "~/v3/marqs/types"; -import { MarQSShortKeyProducer } from "~/v3/marqs/marqsKeyProducer.server.js"; +import { MarQSShortKeyProducer } from "~/v3/marqs/marqsKeyProducer.js"; import Redis from "ioredis"; export function createKeyProducer(prefix: string): MarQSKeyProducer { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d14d7f89d..af762410b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1744,6 +1744,25 @@ importers: specifier: ^5 version: 5.5.4 + references/test-tasks: + dependencies: + '@trigger.dev/sdk': + specifier: workspace:* + version: link:../../packages/trigger-sdk + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@trigger.dev/build': + specifier: workspace:* + version: link:../../packages/build + trigger.dev: + specifier: workspace:* + version: link:../../packages/cli-v3 + typescript: + specifier: ^5.5.4 + version: 5.5.4 + references/v3-catalog: dependencies: '@effect/schema': diff --git a/references/test-tasks/package.json b/references/test-tasks/package.json new file mode 100644 index 0000000000..6739b44b30 --- /dev/null +++ b/references/test-tasks/package.json @@ -0,0 +1,18 @@ +{ + "name": "references-test-tasks", + "private": true, + "type": "module", + "scripts": { + "dev": "trigger dev", + "deploy": "trigger deploy --self-hosted --load-image" + }, + "dependencies": { + "@trigger.dev/sdk": "workspace:*", + "zod": "3.23.8" + }, + "devDependencies": { + "@trigger.dev/build": "workspace:*", + "trigger.dev": "workspace:*", + "typescript": "^5.5.4" + } +} \ No newline at end of file diff --git a/references/test-tasks/src/trigger/test-reserve-concurrency-system.ts b/references/test-tasks/src/trigger/test-reserve-concurrency-system.ts new file mode 100644 index 0000000000..bf33753e8d --- /dev/null +++ b/references/test-tasks/src/trigger/test-reserve-concurrency-system.ts @@ -0,0 +1,545 @@ +import { BatchResult, logger, queue, task, wait } from "@trigger.dev/sdk/v3"; +import assert from "assert"; +import { + updateEnvironmentConcurrencyLimit, + waitForRunStatus, + getEnvironmentStats, +} from "../utils.js"; + +export const describeReserveConcurrencySystem = task({ + id: "describe/reserve-concurrency-system", + retry: { + maxAttempts: 1, + }, + run: async (payload: any, { ctx }) => { + await testRetryPriority.triggerAndWait({ holdDelayMs: 10_000 }).unwrap(); + + logger.info("✅ Tested retry priority, now testing resume priority"); + + await testResumePriority.triggerAndWait({ initialDelayMs: 5_000, useBatch: false }).unwrap(); + await testResumePriority.triggerAndWait({ initialDelayMs: 30_000, useBatch: false }).unwrap(); + + logger.info("✅ Tested resume priority with triggerAndWait"); + + await testResumePriority.triggerAndWait({ initialDelayMs: 5_000, useBatch: true }).unwrap(); + await testResumePriority.triggerAndWait({ initialDelayMs: 30_000, useBatch: true }).unwrap(); + + logger.info("✅ Tested resume priority with batchTriggerAndWait"); + + await testResumeDurationPriority.triggerAndWait({ waitDurationInSeconds: 30 }).unwrap(); + await testResumeDurationPriority.triggerAndWait({ waitDurationInSeconds: 65 }).unwrap(); + + logger.info("✅ Tested resume duration priority with wait.for"); + + await testEnvReserveConcurrency + .triggerAndWait({ envConcurrencyLimit: 4, holdTaskCount: 1, useBatch: false }) + .unwrap(); + + logger.info("✅ Tested env reserve concurrency system with triggerAndWait"); + + await testEnvReserveConcurrency + .triggerAndWait({ envConcurrencyLimit: 4, holdTaskCount: 1, useBatch: true }) + .unwrap(); + + logger.info("✅ Tested env reserve concurrency system with batchTriggerAndWait"); + + await testQueueReserveConcurrency.triggerAndWait({ useBatch: false }).unwrap(); + + logger.info("✅ Tested queue reserve concurrency system with triggerAndWait"); + + await testQueueReserveConcurrency.triggerAndWait({ useBatch: true }).unwrap(); + + logger.info("✅ Tested queue reserve concurrency system with batchTriggerAndWait"); + }, +}); + +export const testRetryPriority = task({ + id: "test/retry-priority", + retry: { + maxAttempts: 1, + }, + run: async ({ holdDelayMs = 10_000 }: { holdDelayMs: number }, { ctx }) => { + const startEnvStats = await getEnvironmentStats(ctx.environment.id); + + // We need to test the reserve concurrency system + // 1. Retries are prioritized over new runs + // Setup: Trigger a run that fails and will re-attempt in 5 seconds + // Trigger another run that uses the same concurrency, and hits the max concurrency of that queue + // Trigger a run on that same queue before the retry is attempted + // The "hold" run will then complete and the retry should be dequeued + // Once the retry completes successfully, the 3rd run should be dequeued + + const failureRun = await retryTask.trigger( + { delayMs: 0, throwError: true, failureCount: 1 }, + { tags: ["failure"] } + ); + await waitForRunStatus(failureRun.id, ["EXECUTING", "REATTEMPTING"]); + + logger.info("Failure run is executing, triggering a run that will hit the concurrency limit"); + + const holdRun = await retryTask.trigger( + { delayMs: holdDelayMs, throwError: false, failureCount: 0 }, + { tags: ["hold"] } + ); + await waitForRunStatus(holdRun.id, ["EXECUTING"]); + + logger.info("Hold run is executing, triggering a run that will be queued"); + + const queuedRun = await retryTask.trigger( + { delayMs: 0, throwError: false, failureCount: 0 }, + { tags: ["queued"] } + ); + + logger.info("Queued run is queued, waiting for the hold run to complete"); + + const completedFailureRun = await waitForRunStatus(failureRun.id, ["COMPLETED"]); + const completedQueuedRun = await waitForRunStatus(queuedRun.id, ["COMPLETED"]); + + logger.info("Runs completed", { + completedFailureRun, + completedQueuedRun, + }); + + // Now we need to assert the completedFailureRun.completedAt is before completedQueuedRun.completedAt + assert( + completedFailureRun.finishedAt! < completedQueuedRun.finishedAt!, + "Failure run should complete before queued run" + ); + + // Now lets make sure all the runs are completed + await waitForRunStatus(holdRun.id, ["COMPLETED"]); + + const envStats = await getEnvironmentStats(ctx.environment.id); + + logger.info("Environment stats", envStats); + + assert( + startEnvStats.reserveConcurrency - envStats.reserveConcurrency === 0, + "Reserve concurrency should be 0" + ); + + logger.info("✅ Failure run completed before queued run"); + }, +}); + +export const testResumePriority = task({ + id: "test/resume-priority", + retry: { + maxAttempts: 1, + }, + run: async ( + { initialDelayMs = 5_000, useBatch = false }: { initialDelayMs: number; useBatch: boolean }, + { ctx } + ) => { + const startEnvStats = await getEnvironmentStats(ctx.environment.id); + + // 2. Resumed runs are prioritized over new runs + const resumeRun = await resumeParentTask.trigger( + { delayMs: initialDelayMs, triggerChildTask: true, useBatch }, + { tags: ["resume"] } + ); + await waitForRunStatus(resumeRun.id, ["EXECUTING", "FROZEN"]); + + logger.info("Resume run is executing, triggering a run that should be queued"); + const queuedRun = await resumeParentTask.trigger( + { delayMs: 1_000, triggerChildTask: false, useBatch }, + { tags: ["queued"] } + ); + await waitForRunStatus(queuedRun.id, ["QUEUED"]); + + const completedResumeRun = await waitForRunStatus(resumeRun.id, ["COMPLETED"]); + const completedQueuedRun = await waitForRunStatus(queuedRun.id, ["COMPLETED"]); + + logger.info("Runs completed", { + completedResumeRun, + completedQueuedRun, + }); + + // Now we need to assert the completedResumeRun.completedAt is before completedQueuedRun.completedAt + assert( + completedResumeRun.finishedAt! < completedQueuedRun.finishedAt!, + "Resume run should complete before queued run" + ); + + const envStats = await getEnvironmentStats(ctx.environment.id); + + assert( + startEnvStats.reserveConcurrency - envStats.reserveConcurrency === 0, + "Reserve concurrency should be 0" + ); + + logger.info("✅ Resume run completed before queued run"); + }, +}); + +export const testResumeDurationPriority = task({ + id: "test/resume-duration-priority", + retry: { + maxAttempts: 1, + }, + run: async ({ waitDurationInSeconds = 5 }: { waitDurationInSeconds: number }, { ctx }) => { + const startEnvStats = await getEnvironmentStats(ctx.environment.id); + + // 2. Resumed runs are prioritized over new runs + const resumeRun = await durationWaitTask.trigger( + { waitDurationInSeconds, doWait: true }, + { tags: ["resume"] } + ); + await waitForRunStatus(resumeRun.id, ["EXECUTING", "FROZEN"]); + + logger.info( + "Resume run is executing, triggering a run that will hold the concurrency until both the resume run and the queued run are in the queue" + ); + + if (ctx.environment.type !== "DEVELOPMENT") { + const holdRun = await durationWaitTask.trigger( + { waitDurationInSeconds: waitDurationInSeconds + 10, doWait: false }, + { tags: ["hold"] } + ); + await waitForRunStatus(holdRun.id, ["EXECUTING"]); + + logger.info("Hold run is executing, triggering a run that should be queued"); + } + + const queuedRun = await durationWaitTask.trigger( + { waitDurationInSeconds: 1, doWait: false }, + { tags: ["queued"] } + ); + await waitForRunStatus(queuedRun.id, ["QUEUED"]); + + const completedResumeRun = await waitForRunStatus(resumeRun.id, ["COMPLETED"]); + const completedQueuedRun = await waitForRunStatus(queuedRun.id, ["COMPLETED"]); + + logger.info("Runs completed", { + completedResumeRun, + completedQueuedRun, + }); + + // Now we need to assert the completedResumeRun.completedAt is before completedQueuedRun.completedAt + assert( + completedResumeRun.finishedAt! < completedQueuedRun.finishedAt!, + "Resume run should complete before queued run" + ); + + const envStats = await getEnvironmentStats(ctx.environment.id); + + assert( + startEnvStats.reserveConcurrency - envStats.reserveConcurrency === 0, + "Reserve concurrency should be 0" + ); + + logger.info("✅ Resume run completed before queued run"); + }, +}); + +export const testEnvReserveConcurrency = task({ + id: "test/env-reserve-concurrency", + retry: { + maxAttempts: 1, + }, + run: async ( + { + envConcurrencyLimit = 3, + holdTaskCount = 1, + useBatch = false, + }: { envConcurrencyLimit: number; holdTaskCount: number; useBatch: boolean }, + { ctx } + ) => { + const startEnvStats = await getEnvironmentStats(ctx.environment.id); + + // 3. When a task triggerAndWaits another task, the parent run should be added to the envs reserve concurrency + // Giving the environment "back" another concurrency slot. Another task (not the parent task) can then be dequeued + // We need to be able to "fill" the env concurrency (sans 1), then trigger the parent task. The parent task then triggerAndWaits + // a child task. We need to make sure the child task executes + await updateEnvironmentConcurrencyLimit(ctx.environment.id, envConcurrencyLimit); + + const holdBatch = await delayTask.batchTrigger( + Array.from({ length: holdTaskCount }, (_, i) => ({ + payload: { delayMs: 30_000 }, + options: { tags: ["hold"] }, + })) + ); + + // Wait for the hold tasks to be executing + await Promise.all(holdBatch.runs.map((run) => waitForRunStatus(run.id, ["EXECUTING"]))); + + // Now we will trigger a parent task that will trigger a child task + const parentRun = await genericParentTask.trigger( + { delayMs: 1_000, triggerChildTask: true, useBatch }, + { tags: ["parent"] } + ); + + // Once the parentRun starts executing, we will be at the max concurrency limit + await waitForRunStatus(parentRun.id, ["EXECUTING"], 5); // timeout after 5 seconds, to ensure the parent task is executing + + // But because the parent task triggers a child task, the env reserve concurrency will allow the child task to execute + logger.info("Parent task is executing, waiting for child task to complete"); + + await waitForRunStatus(parentRun.id, ["COMPLETED"], 10); // timeout after 10 seconds, to ensure the child task finished before the delay runs + + logger.info( + "Parent task completed, which means the child task completed. Now waiting for the hold tasks to complete" + ); + + const envStats = await getEnvironmentStats(ctx.environment.id, "task/generic-parent-task"); + + assert( + startEnvStats.reserveConcurrency - envStats.reserveConcurrency === 0, + "Reserve concurrency should be 0" + ); + assert( + envStats.queueCurrentConcurrency === 0, + "generic-parent-task current concurrency should be 0" + ); + assert( + envStats.queueReserveConcurrency === 0, + "generic-parent-task reserve concurrency should be 0" + ); + + const childStats = await getEnvironmentStats(ctx.environment.id, "task/generic-child-task"); + + assert( + childStats.queueReserveConcurrency === 0, + "generic-child-task reserve concurrency should be 0" + ); + assert( + childStats.queueCurrentConcurrency === 0, + "generic-child-task current concurrency should be 0" + ); + + // Wait for the hold tasks to be completed + await Promise.all(holdBatch.runs.map((run) => waitForRunStatus(run.id, ["COMPLETED"]))); + + await updateEnvironmentConcurrencyLimit(ctx.environment.id, 100); + + logger.info("✅ Environment reserve concurrency system is working as expected"); + }, +}); + +export const testQueueReserveConcurrency = task({ + id: "test/queue-reserve-concurrency", + retry: { + maxAttempts: 1, + }, + run: async ({ useBatch = false }: { useBatch: boolean }, { ctx }) => { + const startEnvStats = await getEnvironmentStats(ctx.environment.id); + // This test ensures that when triggerAndWait is called where the parent and the child share a queue, + // the queue reserve concurrency is used to allow the child to execute. + // We also want to test that the queue can only "reserve" at most up to the concurrency limit, and if + // the reservation fails, the child task will fail + const rootRecursiveRun = await recursiveTask.trigger( + { delayMs: 1_000, depth: 1, useBatch }, + { tags: ["root"] } + ); + + const completedRootRun = await waitForRunStatus(rootRecursiveRun.id, ["COMPLETED"], 20); + + assert(completedRootRun.status === "COMPLETED", "Root recursive run should be completed"); + + const failingRootRecursiveRun = await recursiveTask.trigger( + { delayMs: 1_000, depth: 2, useBatch }, + { tags: ["failing-root"] } + ); + + const failedRootRun = await waitForRunStatus(failingRootRecursiveRun.id, ["COMPLETED"], 20); + + assert(!failedRootRun.output?.ok, "Child of failing root run should fail"); + + const envStats = await getEnvironmentStats(ctx.environment.id, "task/recursive-task"); + + logger.info("Environment stats", envStats); + + assert( + startEnvStats.reserveConcurrency - envStats.reserveConcurrency === 0, + "Env reserve concurrency should be 0" + ); + assert( + envStats.queueCurrentConcurrency === 0, + "queue-reserve-concurrency current concurrency should be 0" + ); + assert( + envStats.queueReserveConcurrency === 0, + "queue-reserve-concurrency reserve concurrency should be 0" + ); + }, +}); + +export const recursiveTask = task({ + id: "recursive-task", + queue: { + concurrencyLimit: 1, + }, + retry: { + maxAttempts: 1, + }, + run: async ( + { delayMs, depth, useBatch = false }: { delayMs: number; depth: number; useBatch: boolean }, + { ctx } + ) => { + if (depth === 0) { + return; + } + + await new Promise((resolve) => setTimeout(resolve, delayMs)); + + if (useBatch) { + const batchResult = await recursiveTask.batchTriggerAndWait([ + { + payload: { delayMs, depth: depth - 1, useBatch }, + options: { tags: ["recursive"] }, + }, + ]); + + const firstRun = batchResult.runs[0] as any; + + return { + ok: firstRun.ok, + }; + } else { + const result = (await recursiveTask.triggerAndWait({ + delayMs, + depth: depth - 1, + useBatch, + })) as any; + + return { + ok: result.ok, + }; + } + }, +}); + +export const singleQueue = queue({ + name: "single-queue", + concurrencyLimit: 1, +}); + +export const delayTask = task({ + id: "delay-task", + retry: { + maxAttempts: 1, + }, + run: async (payload: { delayMs: number }, { ctx }) => { + await new Promise((resolve) => setTimeout(resolve, payload.delayMs)); + }, +}); + +export const retryTask = task({ + id: "retry-task", + queue: singleQueue, + retry: { + maxAttempts: 10, + minTimeoutInMs: 5_000, // Will retry in 5 seconds + maxTimeoutInMs: 5_000, + }, + run: async (payload: { delayMs: number; throwError: boolean; failureCount: number }, { ctx }) => { + await new Promise((resolve) => setTimeout(resolve, payload.delayMs)); + + if (payload.throwError && ctx.attempt.number <= payload.failureCount) { + throw new Error("Error"); + } + }, +}); + +export const durationWaitTask = task({ + id: "duration-wait-task", + queue: { + concurrencyLimit: 1, + }, + run: async ( + { + waitDurationInSeconds = 5, + doWait = true, + }: { waitDurationInSeconds: number; doWait: boolean }, + { ctx } + ) => { + if (doWait) { + await wait.for({ seconds: waitDurationInSeconds }); + } else { + await new Promise((resolve) => setTimeout(resolve, waitDurationInSeconds * 1000)); + } + }, +}); + +export const resumeParentTask = task({ + id: "resume-parent-task", + queue: { + concurrencyLimit: 1, + }, + run: async ( + { + delayMs = 5_000, + triggerChildTask, + useBatch = false, + }: { delayMs: number; triggerChildTask: boolean; useBatch: boolean }, + { ctx } + ) => { + if (triggerChildTask) { + if (useBatch) { + const batchResult = await resumeChildTask.batchTriggerAndWait([ + { + payload: { delayMs }, + options: { tags: ["resume-child"] }, + }, + ]); + + unwrapBatchResult(batchResult); + } else { + await resumeChildTask.triggerAndWait({ delayMs }, { tags: ["resume-child"] }).unwrap(); + } + } else { + await new Promise((resolve) => setTimeout(resolve, delayMs)); + } + }, +}); + +export const resumeChildTask = task({ + id: "resume-child-task", + run: async (payload: { delayMs: number }, { ctx }) => { + await new Promise((resolve) => setTimeout(resolve, payload.delayMs)); + }, +}); + +export const genericParentTask = task({ + id: "generic-parent-task", + run: async ( + { + delayMs = 5_000, + triggerChildTask, + useBatch = false, + }: { delayMs: number; triggerChildTask: boolean; useBatch: boolean }, + { ctx } + ) => { + if (triggerChildTask) { + if (useBatch) { + const batchResult = await genericChildTask.batchTriggerAndWait([ + { + payload: { delayMs }, + options: { tags: ["resume-child"] }, + }, + ]); + + return unwrapBatchResult(batchResult); + } else { + await genericChildTask.triggerAndWait({ delayMs }, { tags: ["resume-child"] }).unwrap(); + } + } else { + await new Promise((resolve) => setTimeout(resolve, delayMs)); + } + }, +}); + +function unwrapBatchResult(batchResult: BatchResult) { + if (batchResult.runs.some((run) => !run.ok)) { + throw new Error(`Child task failed: ${batchResult.runs.find((run) => !run.ok)?.error}`); + } + + return batchResult.runs; +} + +export const genericChildTask = task({ + id: "generic-child-task", + run: async (payload: { delayMs: number }, { ctx }) => { + await new Promise((resolve) => setTimeout(resolve, payload.delayMs)); + }, +}); diff --git a/references/test-tasks/src/utils.ts b/references/test-tasks/src/utils.ts new file mode 100644 index 0000000000..21a83e1037 --- /dev/null +++ b/references/test-tasks/src/utils.ts @@ -0,0 +1,109 @@ +import { runs } from "@trigger.dev/sdk/v3"; +import { z } from "zod"; + +export type RunStatus = Awaited>["status"]; + +export async function waitForRunStatus( + id: string, + statuses: RunStatus[], + timeoutInSeconds?: number +) { + const run = await runs.retrieve(id); + + if (statuses.includes(run.status)) { + return run; + } + + const start = Date.now(); + + while (Date.now() - start < (timeoutInSeconds ?? 300) * 1_000) { + const run = await runs.retrieve(id); + + if (statuses.includes(run.status)) { + return run; + } + + await new Promise((resolve) => setTimeout(resolve, 1_000)); + } + + throw new Error( + `Run did not reach status ${statuses.join(" or ")} within ${timeoutInSeconds ?? 300} seconds` + ); +} + +export async function updateEnvironmentConcurrencyLimit( + environmentId: string, + concurrencyLimit: number +) { + if (!process.env.TRIGGER_API_URL) { + throw new Error("TRIGGER_API_URL is not set"); + } + + if (!process.env.TRIGGER_ACCESS_TOKEN) { + throw new Error("TRIGGER_ACCESS_TOKEN is not set"); + } + + // We need to make a request to baseURL + `/admin/api/v1/environments/${environmentId}` with a POST request + // The body needs to be a JSON object with the key `envMaximumConcurrencyLimit` and the value `concurrencyLimit`, and the key `orgMaximumConcurrencyLimit` with the value `concurrencyLimit` + // we also need a Authorization header that has the personal access token from process.env.TRIGGER_ACCESS_TOKEN + + const response = await fetch( + `${process.env.TRIGGER_API_URL}/admin/api/v1/environments/${environmentId}`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${process.env.TRIGGER_ACCESS_TOKEN}`, + }, + body: JSON.stringify({ + envMaximumConcurrencyLimit: concurrencyLimit, + orgMaximumConcurrencyLimit: concurrencyLimit, + }), + } + ); + + if (!response.ok) { + throw new Error(`Failed to update environment concurrency limit: ${response.statusText}`); + } + + return response.json(); +} + +const EnvironmentStatsResponseBody = z.object({ + id: z.string(), + concurrencyLimit: z.number(), + currentConcurrency: z.number(), + reserveConcurrency: z.number(), + queueConcurrency: z.number().optional(), + queueReserveConcurrency: z.number().optional(), + queueCurrentConcurrency: z.number().optional(), +}); + +export type EnvironmentStatsResponseBody = z.infer; + +export async function getEnvironmentStats( + environmentId: string, + queue?: string +): Promise { + const url = new URL(`${process.env.TRIGGER_API_URL}/admin/api/v1/environments/${environmentId}`); + + if (queue) { + url.searchParams.set("queue", queue); + } + + const response = await fetch(url.toString(), { + method: "GET", + headers: { + Accept: "application/json", + Authorization: `Bearer ${process.env.TRIGGER_ACCESS_TOKEN}`, + }, + }); + + if (!response.ok) { + throw new Error(`Failed to fetch environment stats: ${response.statusText}`); + } + + const responseBody = await response.json(); + + return EnvironmentStatsResponseBody.parse(responseBody); +} diff --git a/references/test-tasks/trigger.config.ts b/references/test-tasks/trigger.config.ts new file mode 100644 index 0000000000..b515908f41 --- /dev/null +++ b/references/test-tasks/trigger.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from "@trigger.dev/sdk/v3"; + +export default defineConfig({ + runtime: "node", + project: "proj_qwdshjjnuoepcuupfuvo", + machine: "small-1x", + maxDuration: 3600, + dirs: ["./src/trigger"], + retries: { + enabledInDev: true, + default: { + maxAttempts: 10, + minTimeoutInMs: 5_000, + maxTimeoutInMs: 30_000, + factor: 2, + randomize: true, + }, + }, +}); diff --git a/references/test-tasks/tsconfig.json b/references/test-tasks/tsconfig.json new file mode 100644 index 0000000000..635ecdb766 --- /dev/null +++ b/references/test-tasks/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "strict": true, + "outDir": "dist", + "skipLibCheck": true, + "customConditions": ["@triggerdotdev/source"], + "jsx": "preserve", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "lib": ["DOM", "DOM.Iterable"], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["./src/**/*.ts", "trigger.config.ts"] +} From 84f5563a3a4eaa82aa010b327fbec716fb25930d Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 19 Feb 2025 11:20:21 +0000 Subject: [PATCH 9/9] Fix the migrations --- .../migration.sql | 11 ++++------- .../migration.sql | 10 +++------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/internal-packages/database/prisma/migrations/20250210164232_add_queue_timestamp_to_run/migration.sql b/internal-packages/database/prisma/migrations/20250210164232_add_queue_timestamp_to_run/migration.sql index b6e7ac1c91..ef3add8f6d 100644 --- a/internal-packages/database/prisma/migrations/20250210164232_add_queue_timestamp_to_run/migration.sql +++ b/internal-packages/database/prisma/migrations/20250210164232_add_queue_timestamp_to_run/migration.sql @@ -1,8 +1,5 @@ --- DropIndex -DROP INDEX "SecretStore_key_idx"; - -- AlterTable -ALTER TABLE "TaskRun" ADD COLUMN "queueTimestamp" TIMESTAMP(3); - --- CreateIndex -CREATE INDEX "SecretStore_key_idx" ON "SecretStore"("key" text_pattern_ops); +ALTER TABLE + "TaskRun" +ADD + COLUMN "queueTimestamp" TIMESTAMP(3); \ No newline at end of file diff --git a/internal-packages/database/prisma/migrations/20250211150836_add_aborted_batch_task_run_status/migration.sql b/internal-packages/database/prisma/migrations/20250211150836_add_aborted_batch_task_run_status/migration.sql index f00d71d2a3..4fcc2da707 100644 --- a/internal-packages/database/prisma/migrations/20250211150836_add_aborted_batch_task_run_status/migration.sql +++ b/internal-packages/database/prisma/migrations/20250211150836_add_aborted_batch_task_run_status/migration.sql @@ -1,8 +1,4 @@ -- AlterEnum -ALTER TYPE "BatchTaskRunStatus" ADD VALUE 'ABORTED'; - --- DropIndex -DROP INDEX "SecretStore_key_idx"; - --- CreateIndex -CREATE INDEX "SecretStore_key_idx" ON "SecretStore"("key" text_pattern_ops); +ALTER TYPE "BatchTaskRunStatus" +ADD + VALUE 'ABORTED'; \ No newline at end of file