From bc39340322e7bb3008ab56acad2768b679506493 Mon Sep 17 00:00:00 2001 From: almog8k Date: Wed, 15 Jul 2026 14:03:04 +0300 Subject: [PATCH 1/2] fix: ignore task notifications for aborted/expired jobs(MAPCO-11121) --- package-lock.json | 47 +++++--------- src/tasks/models/tasksManager.ts | 13 ++++ tests/unit/tasks/models/tasksManager.spec.ts | 64 ++++++++++++++++++++ 3 files changed, 91 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6a25788..30846c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -161,7 +161,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.2.tgz", "integrity": "sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ==", "dev": true, - "peer": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -3287,7 +3286,6 @@ "resolved": "https://registry.npmjs.org/@map-colonies/mc-utils/-/mc-utils-5.1.0.tgz", "integrity": "sha512-4eL4ykgJFrFSPP18AbeJUJklwmzJieormWIvEc8c8PFRXU5eN1lRUal60xAhghpmzEQnudRG1kV3NWIibUjw2w==", "license": "ISC", - "peer": true, "dependencies": { "@map-colonies/types": "^1.9.0", "@turf/turf": "^6.5.0", @@ -3440,8 +3438,7 @@ "version": "1.18.0", "resolved": "https://registry.npmjs.org/@map-colonies/schemas/-/schemas-1.18.0.tgz", "integrity": "sha512-G6OB/xogqzcLrDU+sXok8ECFbdzQDjI9AE+eMZP6Bcl8BZArkkFI/wNz14HCD2NE81p4SNaeAKUuyGZBqzRJyg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@map-colonies/telemetry": { "version": "6.0.0", @@ -3467,7 +3464,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz", "integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -3772,7 +3768,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.6.0.tgz", "integrity": "sha512-HLM1v2cbZ4TgYN6KEOj+Bbj8rAKriOdkF9Ed3tG25FoprSiQl7kYc+RRT6fUZGOvx0oMi5U67GoFdT+XUn8zEg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, @@ -4945,7 +4940,6 @@ "resolved": "https://registry.npmjs.org/@map-colonies/types/-/types-1.9.0.tgz", "integrity": "sha512-TUM82wWLCV49vFTs16h4OYo15FjlnhMeMGLE922CeA0QNJyEkyaJG7IHvrM0LG793Ep4qeBMeB1izJQaN2fVYQ==", "license": "ISC", - "peer": true, "dependencies": { "@types/geojson": "^7946.0.16", "@types/mime-types": "^2.1.1", @@ -5022,7 +5016,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -5031,7 +5024,6 @@ "version": "0.46.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.46.0.tgz", "integrity": "sha512-+9BcqfiEDGPXEIo+o3tso/aqGM5dGbGwAkGVp3FPpZ8GlkK1YlaKRd9gMVyPaeRATwvO5wYGGnCsAc/sMMM9Qw==", - "peer": true, "dependencies": { "@opentelemetry/api": "^1.0.0" }, @@ -9626,6 +9618,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=10" } @@ -9642,6 +9635,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=10" } @@ -9658,6 +9652,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=10" } @@ -9674,6 +9669,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=10" } @@ -9690,6 +9686,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=10" } @@ -9706,6 +9703,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=10" } @@ -9722,6 +9720,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=10" } @@ -9738,6 +9737,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=10" } @@ -9754,6 +9754,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=10" } @@ -9770,6 +9771,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=10" } @@ -9802,6 +9804,7 @@ "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@swc/counter": "^0.1.3" } @@ -11824,8 +11827,7 @@ "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "peer": true + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "node_modules/@types/keygrip": { "version": "1.0.6", @@ -11940,8 +11942,7 @@ "version": "20.5.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -12145,7 +12146,6 @@ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -12681,7 +12681,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -12745,7 +12744,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -12914,7 +12912,6 @@ "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "license": "MIT", - "peer": true, "dependencies": { "follow-redirects": "^1.14.0" } @@ -13341,7 +13338,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -14213,7 +14209,6 @@ "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -15096,7 +15091,6 @@ "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -15929,7 +15923,6 @@ "integrity": "sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/types": "^8.35.0", "comment-parser": "^1.4.1", @@ -16006,7 +15999,6 @@ "integrity": "sha512-ZCGr7vTH2WSo2hrK5oM2RULFmMruQ7W3cX7YfwoTiPfzTGTFBMmrVIz45jZHd++cGKj/kWf02li/RhTGcANJSA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/utils": "^8.0.0" }, @@ -16519,7 +16511,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -18383,7 +18374,6 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", "dev": true, - "peer": true, "dependencies": { "@jest/core": "^29.5.0", "@jest/types": "^29.5.0", @@ -21050,7 +21040,6 @@ "integrity": "sha512-UczzB+0nnwGotYSgllfARAqWCJ5e/skuV2K/l+Zyck/H6pJIhLXuBnz+6vn2i211o7DtbE78HQtsYEKICHGI+g==", "dev": true, "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" @@ -22342,7 +22331,6 @@ "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", "license": "MIT", - "peer": true, "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", @@ -22613,7 +22601,6 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -22766,7 +22753,6 @@ "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.3.tgz", "integrity": "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/api": "^1.4.0", "tdigest": "^0.1.1" @@ -23008,7 +22994,6 @@ "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -23019,7 +23004,6 @@ "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -24377,7 +24361,6 @@ "integrity": "sha512-J72R4ltw0UBVUlEjTzI0gg2STOqlI9JBhQOL4Dxt7aJOnnSesy0qJDn4PYfMCafk9cWOaVg129Pesl5o+DIh0Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@emotion/is-prop-valid": "1.4.0", "@emotion/unitless": "0.10.0", @@ -24687,7 +24670,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -24890,7 +24872,6 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", diff --git a/src/tasks/models/tasksManager.ts b/src/tasks/models/tasksManager.ts index b758c0d..abb1ec3 100644 --- a/src/tasks/models/tasksManager.ts +++ b/src/tasks/models/tasksManager.ts @@ -36,6 +36,19 @@ export class TasksManager { } const job = await this.jobManager.getJob(task.jobId); + + + if (job.status === OperationStatus.ABORTED || job.status === OperationStatus.EXPIRED) { + this.logger.warn({ + msg: `Skipping notification handling for task id: ${taskId} - job is in terminal status '${job.status}'`, + jobId: job.id, + taskId, + taskStatus: task.status, + jobStatus: job.status, + }); + return; + } + const handler = getJobHandler(job.type, this.jobDefinitions, this.logger, this.queueClient, this.config, job, task); if (task.status === OperationStatus.FAILED) { diff --git a/tests/unit/tasks/models/tasksManager.spec.ts b/tests/unit/tasks/models/tasksManager.spec.ts index 4fb53ac..0b9b7db 100644 --- a/tests/unit/tasks/models/tasksManager.spec.ts +++ b/tests/unit/tasks/models/tasksManager.spec.ts @@ -149,6 +149,70 @@ describe('TasksManager', () => { }); }); + describe('Terminated Jobs', () => { + it('should not create a next task when a completed task notification arrives for an aborted job', async () => { + // mocks + const { tasksManager, mockFindTasks, mockGetJob, mockCreateTaskForJob, mockUpdateJob, jobDefinitionsConfigMock } = testContext; + const abortedJobMock = createTestJob(jobDefinitionsConfigMock.jobs.update, { + status: OperationStatus.ABORTED, + completedTasks: 10, + taskCount: 10, + }); + const mergeTaskMock = getTaskMock(abortedJobMock.id, { + type: jobDefinitionsConfigMock.tasks.merge, + status: OperationStatus.COMPLETED, + }); + + mockFindTasks.mockResolvedValueOnce([mergeTaskMock]); + mockGetJob.mockResolvedValue(abortedJobMock); + // action + await tasksManager.handleTaskNotification(mergeTaskMock.id); + // expectation + expect(mockCreateTaskForJob).not.toHaveBeenCalled(); + expect(mockUpdateJob).not.toHaveBeenCalled(); + }); + + it('should not fail an aborted job when a failed task notification arrives', async () => { + // mocks + const { tasksManager, mockFindTasks, mockGetJob, mockUpdateJob, jobDefinitionsConfigMock } = testContext; + const abortedJobMock = createTestJob(jobDefinitionsConfigMock.jobs.update, { status: OperationStatus.ABORTED }); + const mergeTaskMock = getTaskMock(abortedJobMock.id, { + type: jobDefinitionsConfigMock.tasks.merge, + status: OperationStatus.FAILED, + reason: 'reason', + }); + + mockFindTasks.mockResolvedValueOnce([mergeTaskMock]); + mockGetJob.mockResolvedValue(abortedJobMock); + // action + await tasksManager.handleTaskNotification(mergeTaskMock.id); + // expectation + expect(mockUpdateJob).not.toHaveBeenCalled(); + }); + + it('should not create a next task when a completed task notification arrives for an expired job', async () => { + // mocks + const { tasksManager, mockFindTasks, mockGetJob, mockCreateTaskForJob, mockUpdateJob, jobDefinitionsConfigMock } = testContext; + const expiredJobMock = createTestJob(jobDefinitionsConfigMock.jobs.update, { + status: OperationStatus.EXPIRED, + completedTasks: 10, + taskCount: 10, + }); + const mergeTaskMock = getTaskMock(expiredJobMock.id, { + type: jobDefinitionsConfigMock.tasks.merge, + status: OperationStatus.COMPLETED, + }); + + mockFindTasks.mockResolvedValueOnce([mergeTaskMock]); + mockGetJob.mockResolvedValue(expiredJobMock); + // action + await tasksManager.handleTaskNotification(mergeTaskMock.id); + // expectation + expect(mockCreateTaskForJob).not.toHaveBeenCalled(); + expect(mockUpdateJob).not.toHaveBeenCalled(); + }); + }); + describe('Error Handling', () => { it('should throw NotFoundError if the given task does not exist', async () => { // mocks From ce2141e163563965d99cc949c6e87d767d42faab Mon Sep 17 00:00:00 2001 From: almog8k Date: Wed, 15 Jul 2026 14:23:15 +0300 Subject: [PATCH 2/2] fix: remove unnecessary blank line before job status check in handleTaskNotification --- src/tasks/models/tasksManager.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tasks/models/tasksManager.ts b/src/tasks/models/tasksManager.ts index abb1ec3..e41d06a 100644 --- a/src/tasks/models/tasksManager.ts +++ b/src/tasks/models/tasksManager.ts @@ -37,7 +37,6 @@ export class TasksManager { const job = await this.jobManager.getJob(task.jobId); - if (job.status === OperationStatus.ABORTED || job.status === OperationStatus.EXPIRED) { this.logger.warn({ msg: `Skipping notification handling for task id: ${taskId} - job is in terminal status '${job.status}'`,