@pikku/queue-pg-boss 0.11.0 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,11 +1,38 @@
1
1
  ## 0.11.0
2
2
 
3
+ ## 0.11.2
4
+
5
+ ### Patch Changes
6
+
7
+ - db9c7bf: Simplify CreateWireServices type signature for custom Config compatibility
8
+ - Updated dependencies [db9c7bf]
9
+ - @pikku/core@0.11.2
10
+
11
+ ## 0.11.2
12
+
13
+ ### Fixes
14
+
15
+ - ddd87eaf: Simplify CreateWireServices type signature for custom Config compatibility
16
+
17
+ ## 0.11.1
18
+
19
+ ### Patch Changes
20
+
21
+ - 4b811db: chore: updating all dependencies
22
+ - 06e1a31: breaking: change session services to wire services
23
+ - Updated dependencies [4b811db]
24
+ - Updated dependencies [e12a00c]
25
+ - Updated dependencies [4579434]
26
+ - Updated dependencies [28aeb7f]
27
+ - Updated dependencies [ce902b1]
28
+ - Updated dependencies [06e1a31]
29
+ - @pikku/core@0.11.1
30
+
3
31
  ### Minor Changes
4
32
 
5
33
  - Add scheduler service implementation
6
34
  - Add service factory for queue and scheduler creation
7
35
 
8
-
9
36
  # @pikku/queue-pg-boss
10
37
 
11
38
  ## 0.10.0
@@ -1,6 +1,6 @@
1
- import PgBoss from 'pg-boss';
1
+ import { PgBoss, Request as PgBossRequest } from 'pg-boss';
2
2
  import type { QueueService, QueueJob, JobOptions } from '@pikku/core/queue';
3
- export declare const mapPikkuJobToPgBoss: (options?: JobOptions) => PgBoss.JobOptions;
3
+ export declare const mapPikkuJobToPgBoss: (options?: JobOptions) => PgBossRequest["options"];
4
4
  /**
5
5
  * pg-boss/PostgreSQL queue service implementation
6
6
  * Supports job results, retries, and PostgreSQL-based queuing
@@ -1,14 +1,14 @@
1
- import PgBoss from 'pg-boss';
1
+ import { PgBoss, type WorkOptions } from 'pg-boss';
2
2
  import type { QueueWorkers, PikkuWorkerConfig, QueueConfigMapping, ConfigValidationResult } from '@pikku/core/queue';
3
- import { CoreServices, CoreSingletonServices, CreateSessionServices } from '@pikku/core';
4
- export declare const mapPikkuWorkerToPgBoss: (workerConfig?: PikkuWorkerConfig) => PgBoss.WorkOptions;
3
+ import { CoreSingletonServices, CreateWireServices } from '@pikku/core';
4
+ export declare const mapPikkuWorkerToPgBoss: (workerConfig?: PikkuWorkerConfig) => WorkOptions;
5
5
  /**
6
6
  * pg-boss/PostgreSQL queue worker implementation
7
7
  * Supports job results, retries, and PostgreSQL-based queuing
8
8
  */
9
9
  export declare class PgBossQueueWorkers implements QueueWorkers {
10
10
  private singletonServices;
11
- private createSessionServices?;
11
+ private createWireServices?;
12
12
  readonly name = "pg-boss";
13
13
  readonly supportsResults = true;
14
14
  /**
@@ -18,7 +18,7 @@ export declare class PgBossQueueWorkers implements QueueWorkers {
18
18
  readonly configMappings: QueueConfigMapping;
19
19
  private pgBoss;
20
20
  private activeWorkers;
21
- constructor(pgBoss: PgBoss, singletonServices: CoreSingletonServices, createSessionServices?: CreateSessionServices<CoreSingletonServices, CoreServices, any> | undefined);
21
+ constructor(pgBoss: PgBoss, singletonServices: CoreSingletonServices, createWireServices?: CreateWireServices | undefined);
22
22
  /**
23
23
  * Scan state and register all compatible processors
24
24
  */
@@ -1,7 +1,9 @@
1
1
  import { runQueueJob, registerQueueWorkers, QueueJobFailedError, QueueJobDiscardedError, } from '@pikku/core/queue';
2
2
  import { mapPgBossJobToQueueJob } from './utils.js';
3
3
  export const mapPikkuWorkerToPgBoss = (workerConfig) => {
4
- const workerOptions = {};
4
+ const workerOptions = {
5
+ batchSize: 10,
6
+ };
5
7
  if (workerConfig?.batchSize !== undefined) {
6
8
  workerOptions.batchSize = workerConfig.batchSize;
7
9
  }
@@ -16,7 +18,7 @@ export const mapPikkuWorkerToPgBoss = (workerConfig) => {
16
18
  */
17
19
  export class PgBossQueueWorkers {
18
20
  singletonServices;
19
- createSessionServices;
21
+ createWireServices;
20
22
  name = 'pg-boss';
21
23
  supportsResults = true;
22
24
  /**
@@ -28,7 +30,7 @@ export class PgBossQueueWorkers {
28
30
  supported: {
29
31
  batchSize: {
30
32
  queueProperty: 'batchSize',
31
- description: 'Number of jobs to process in a single batch',
33
+ description: 'Number of jobs to fetch and process concurrently in parallel',
32
34
  },
33
35
  pollInterval: {
34
36
  queueProperty: 'pollingIntervalSeconds',
@@ -83,9 +85,9 @@ export class PgBossQueueWorkers {
83
85
  };
84
86
  pgBoss;
85
87
  activeWorkers = new Map();
86
- constructor(pgBoss, singletonServices, createSessionServices) {
88
+ constructor(pgBoss, singletonServices, createWireServices) {
87
89
  this.singletonServices = singletonServices;
88
- this.createSessionServices = createSessionServices;
90
+ this.createWireServices = createWireServices;
89
91
  this.pgBoss = pgBoss;
90
92
  }
91
93
  /**
@@ -97,33 +99,45 @@ export class PgBossQueueWorkers {
97
99
  const workerId = await this.pgBoss.work(processor.queueName, {
98
100
  ...mapPikkuWorkerToPgBoss(processor.config),
99
101
  includeMetadata: true,
100
- }, async ([job]) => {
102
+ }, async (jobs) => {
101
103
  if (!this.singletonServices) {
102
104
  throw new Error('Singleton services not available');
103
105
  }
104
- if (!job) {
105
- this.singletonServices.logger.warn(`No job received for queue ${queueName}`);
106
+ if (!jobs || jobs.length === 0) {
107
+ this.singletonServices.logger.warn(`No jobs received for queue ${queueName}`);
106
108
  return;
107
109
  }
108
- try {
109
- await runQueueJob({
110
- singletonServices: this.singletonServices,
111
- createSessionServices: this.createSessionServices,
112
- job: mapPgBossJobToQueueJob(job, this.pgBoss),
113
- });
114
- }
115
- catch (error) {
116
- if (error instanceof QueueJobFailedError) {
117
- // Let pg-boss handle this as a failed job
118
- throw new Error(error.message);
110
+ // Process all jobs in parallel
111
+ await Promise.all(jobs.map(async (job) => {
112
+ try {
113
+ await runQueueJob({
114
+ singletonServices: this.singletonServices,
115
+ createWireServices: this.createWireServices,
116
+ job: mapPgBossJobToQueueJob(job, this.pgBoss),
117
+ });
119
118
  }
120
- else if (error instanceof QueueJobDiscardedError) {
121
- // For pg-boss, complete the job successfully to discard it
122
- this.singletonServices.logger.info(`PgBoss job ${job.id} discarded: ${error.message}`);
123
- return; // Successfully "completed" by discarding
119
+ catch (error) {
120
+ if (error instanceof QueueJobFailedError) {
121
+ // Mark this specific job as failed
122
+ await this.pgBoss.fail(queueName, job.id, {
123
+ message: error.message,
124
+ });
125
+ }
126
+ else if (error instanceof QueueJobDiscardedError) {
127
+ // For pg-boss, complete the job successfully to discard it
128
+ this.singletonServices.logger.info(`PgBoss job ${job.id} discarded: ${error.message}`);
129
+ await this.pgBoss.complete(queueName, job.id);
130
+ }
131
+ else {
132
+ // For other errors, fail the job
133
+ await this.pgBoss.fail(queueName, job.id, {
134
+ message: error instanceof Error
135
+ ? error.message
136
+ : 'Unknown error',
137
+ });
138
+ }
124
139
  }
125
- throw error;
126
- }
140
+ }));
127
141
  });
128
142
  this.activeWorkers.set(queueName, workerId);
129
143
  });
@@ -1,4 +1,4 @@
1
- import PgBoss from 'pg-boss';
1
+ import { PgBoss } from 'pg-boss';
2
2
  import { SchedulerService, ScheduledTaskInfo, ScheduledTaskSummary, CoreUserSession } from '@pikku/core';
3
3
  /**
4
4
  * pg-boss scheduler service implementation
@@ -1,8 +1,8 @@
1
- import PgBoss from 'pg-boss';
1
+ import { PgBoss, type ConstructorOptions } from 'pg-boss';
2
2
  import { PgBossQueueService } from './pg-boss-queue-service.js';
3
3
  import { PgBossQueueWorkers } from './pg-boss-queue-worker.js';
4
4
  import { PgBossSchedulerService } from './pg-boss-scheduler-service.js';
5
- import type { CoreServices, CoreSingletonServices, CreateSessionServices } from '@pikku/core';
5
+ import type { CoreSingletonServices, CreateWireServices } from '@pikku/core';
6
6
  /**
7
7
  * Factory class for pg-boss services
8
8
  * Manages a single PgBoss instance shared across queue, worker, and scheduler services
@@ -13,7 +13,7 @@ export declare class PgBossServiceFactory {
13
13
  private queueWorkers?;
14
14
  private schedulerService?;
15
15
  private initialized;
16
- constructor(optionsOrInstance: PgBoss.ConstructorOptions | string | PgBoss);
16
+ constructor(optionsOrInstance: ConstructorOptions | string | PgBoss);
17
17
  /**
18
18
  * Initialize the pg-boss instance
19
19
  */
@@ -25,7 +25,7 @@ export declare class PgBossServiceFactory {
25
25
  /**
26
26
  * Get the queue workers for processing jobs
27
27
  */
28
- getQueueWorkers(singletonServices: CoreSingletonServices, createSessionServices?: CreateSessionServices<CoreSingletonServices, CoreServices, any>): PgBossQueueWorkers;
28
+ getQueueWorkers(singletonServices: CoreSingletonServices, createWireServices?: CreateWireServices): PgBossQueueWorkers;
29
29
  /**
30
30
  * Get the scheduler service for managing recurring tasks
31
31
  */
@@ -34,4 +34,8 @@ export declare class PgBossServiceFactory {
34
34
  * Close all services and the pg-boss connection
35
35
  */
36
36
  close(): Promise<void>;
37
+ /**
38
+ * Alias for close() - used by stopSingletonServices
39
+ */
40
+ stop(): Promise<void>;
37
41
  }
@@ -1,4 +1,4 @@
1
- import PgBoss from 'pg-boss';
1
+ import { PgBoss } from 'pg-boss';
2
2
  import { PgBossQueueService } from './pg-boss-queue-service.js';
3
3
  import { PgBossQueueWorkers } from './pg-boss-queue-worker.js';
4
4
  import { PgBossSchedulerService } from './pg-boss-scheduler-service.js';
@@ -44,9 +44,9 @@ export class PgBossServiceFactory {
44
44
  /**
45
45
  * Get the queue workers for processing jobs
46
46
  */
47
- getQueueWorkers(singletonServices, createSessionServices) {
47
+ getQueueWorkers(singletonServices, createWireServices) {
48
48
  if (!this.queueWorkers) {
49
- this.queueWorkers = new PgBossQueueWorkers(this.pgBoss, singletonServices, createSessionServices);
49
+ this.queueWorkers = new PgBossQueueWorkers(this.pgBoss, singletonServices, createWireServices);
50
50
  }
51
51
  return this.queueWorkers;
52
52
  }
@@ -68,4 +68,10 @@ export class PgBossServiceFactory {
68
68
  this.initialized = false;
69
69
  }
70
70
  }
71
+ /**
72
+ * Alias for close() - used by stopSingletonServices
73
+ */
74
+ async stop() {
75
+ await this.close();
76
+ }
71
77
  }
package/dist/utils.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { QueueJob } from '@pikku/core/queue';
2
- import PgBoss from 'pg-boss';
2
+ import { PgBoss, type JobWithMetadata } from 'pg-boss';
3
3
  /**
4
4
  * Map pg-boss job to QueueJob interface
5
5
  */
6
- export declare const mapPgBossJobToQueueJob: <In, Out>(pgBossJob: PgBoss.JobWithMetadata<In>, pgBossInstance: PgBoss) => QueueJob<In, Out>;
6
+ export declare const mapPgBossJobToQueueJob: <In, Out>(pgBossJob: JobWithMetadata<In>, pgBossInstance: PgBoss) => QueueJob<In, Out>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/queue-pg-boss",
3
- "version": "0.11.0",
3
+ "version": "0.11.2",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "MIT",
6
6
  "module": "dist/index.js",
@@ -13,10 +13,10 @@
13
13
  "release": "npm run build && npm test"
14
14
  },
15
15
  "peerDependencies": {
16
- "@pikku/core": "^0.11.0"
16
+ "@pikku/core": "^0.11.2"
17
17
  },
18
18
  "dependencies": {
19
- "pg-boss": "^11.1.2"
19
+ "pg-boss": "^12.2.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "typescript": "^5.9"
@@ -1,10 +1,10 @@
1
- import PgBoss from 'pg-boss'
1
+ import { PgBoss, Request as PgBossRequest } from 'pg-boss'
2
2
  import type { QueueService, QueueJob, JobOptions } from '@pikku/core/queue'
3
3
  import { mapPgBossJobToQueueJob } from './utils.js'
4
4
 
5
5
  export const mapPikkuJobToPgBoss = (
6
6
  options?: JobOptions
7
- ): PgBoss.JobOptions => {
7
+ ): PgBossRequest['options'] => {
8
8
  const pgBossOptions: any = {}
9
9
 
10
10
  if (options?.priority !== undefined) {
@@ -46,7 +46,7 @@ export const mapPikkuJobToPgBoss = (
46
46
  // Note: pg-boss keeps failed jobs by default
47
47
  }
48
48
 
49
- return pgBossOptions as PgBoss.JobOptions
49
+ return pgBossOptions as PgBossRequest['options']
50
50
  }
51
51
 
52
52
  /**
@@ -1,4 +1,4 @@
1
- import PgBoss from 'pg-boss'
1
+ import { PgBoss, type WorkOptions } from 'pg-boss'
2
2
  import type {
3
3
  QueueWorkers,
4
4
  PikkuWorkerConfig,
@@ -11,17 +11,15 @@ import {
11
11
  QueueJobFailedError,
12
12
  QueueJobDiscardedError,
13
13
  } from '@pikku/core/queue'
14
- import {
15
- CoreServices,
16
- CoreSingletonServices,
17
- CreateSessionServices,
18
- } from '@pikku/core'
14
+ import { CoreSingletonServices, CreateWireServices } from '@pikku/core'
19
15
  import { mapPgBossJobToQueueJob } from './utils.js'
20
16
 
21
17
  export const mapPikkuWorkerToPgBoss = (
22
18
  workerConfig?: PikkuWorkerConfig
23
- ): PgBoss.WorkOptions => {
24
- const workerOptions: PgBoss.WorkOptions = {}
19
+ ): WorkOptions => {
20
+ const workerOptions: WorkOptions = {
21
+ batchSize: 10,
22
+ }
25
23
 
26
24
  if (workerConfig?.batchSize !== undefined) {
27
25
  workerOptions.batchSize = workerConfig.batchSize
@@ -53,7 +51,8 @@ export class PgBossQueueWorkers implements QueueWorkers {
53
51
  supported: {
54
52
  batchSize: {
55
53
  queueProperty: 'batchSize',
56
- description: 'Number of jobs to process in a single batch',
54
+ description:
55
+ 'Number of jobs to fetch and process concurrently in parallel',
57
56
  },
58
57
  pollInterval: {
59
58
  queueProperty: 'pollingIntervalSeconds',
@@ -125,11 +124,7 @@ export class PgBossQueueWorkers implements QueueWorkers {
125
124
  constructor(
126
125
  pgBoss: PgBoss,
127
126
  private singletonServices: CoreSingletonServices,
128
- private createSessionServices?: CreateSessionServices<
129
- CoreSingletonServices,
130
- CoreServices,
131
- any
132
- >
127
+ private createWireServices?: CreateWireServices
133
128
  ) {
134
129
  this.pgBoss = pgBoss
135
130
  }
@@ -149,35 +144,49 @@ export class PgBossQueueWorkers implements QueueWorkers {
149
144
  ...mapPikkuWorkerToPgBoss(processor.config),
150
145
  includeMetadata: true,
151
146
  },
152
- async ([job]) => {
147
+ async (jobs) => {
153
148
  if (!this.singletonServices) {
154
149
  throw new Error('Singleton services not available')
155
150
  }
156
- if (!job) {
151
+ if (!jobs || jobs.length === 0) {
157
152
  this.singletonServices.logger.warn(
158
- `No job received for queue ${queueName}`
153
+ `No jobs received for queue ${queueName}`
159
154
  )
160
155
  return
161
156
  }
162
- try {
163
- await runQueueJob({
164
- singletonServices: this.singletonServices,
165
- createSessionServices: this.createSessionServices,
166
- job: mapPgBossJobToQueueJob(job, this.pgBoss),
157
+ // Process all jobs in parallel
158
+ await Promise.all(
159
+ jobs.map(async (job) => {
160
+ try {
161
+ await runQueueJob({
162
+ singletonServices: this.singletonServices,
163
+ createWireServices: this.createWireServices,
164
+ job: mapPgBossJobToQueueJob(job, this.pgBoss),
165
+ })
166
+ } catch (error: unknown) {
167
+ if (error instanceof QueueJobFailedError) {
168
+ // Mark this specific job as failed
169
+ await this.pgBoss.fail(queueName, job.id, {
170
+ message: error.message,
171
+ })
172
+ } else if (error instanceof QueueJobDiscardedError) {
173
+ // For pg-boss, complete the job successfully to discard it
174
+ this.singletonServices.logger.info(
175
+ `PgBoss job ${job.id} discarded: ${error.message}`
176
+ )
177
+ await this.pgBoss.complete(queueName, job.id)
178
+ } else {
179
+ // For other errors, fail the job
180
+ await this.pgBoss.fail(queueName, job.id, {
181
+ message:
182
+ error instanceof Error
183
+ ? error.message
184
+ : 'Unknown error',
185
+ })
186
+ }
187
+ }
167
188
  })
168
- } catch (error: unknown) {
169
- if (error instanceof QueueJobFailedError) {
170
- // Let pg-boss handle this as a failed job
171
- throw new Error(error.message)
172
- } else if (error instanceof QueueJobDiscardedError) {
173
- // For pg-boss, complete the job successfully to discard it
174
- this.singletonServices.logger.info(
175
- `PgBoss job ${job.id} discarded: ${error.message}`
176
- )
177
- return // Successfully "completed" by discarding
178
- }
179
- throw error
180
- }
189
+ )
181
190
  }
182
191
  )
183
192
  this.activeWorkers.set(queueName, workerId)
@@ -1,4 +1,4 @@
1
- import PgBoss from 'pg-boss'
1
+ import { PgBoss } from 'pg-boss'
2
2
  import {
3
3
  SchedulerService,
4
4
  ScheduledTaskInfo,
@@ -1,12 +1,8 @@
1
- import PgBoss from 'pg-boss'
1
+ import { PgBoss, type ConstructorOptions } from 'pg-boss'
2
2
  import { PgBossQueueService } from './pg-boss-queue-service.js'
3
3
  import { PgBossQueueWorkers } from './pg-boss-queue-worker.js'
4
4
  import { PgBossSchedulerService } from './pg-boss-scheduler-service.js'
5
- import type {
6
- CoreServices,
7
- CoreSingletonServices,
8
- CreateSessionServices,
9
- } from '@pikku/core'
5
+ import type { CoreSingletonServices, CreateWireServices } from '@pikku/core'
10
6
 
11
7
  /**
12
8
  * Factory class for pg-boss services
@@ -19,7 +15,7 @@ export class PgBossServiceFactory {
19
15
  private schedulerService?: PgBossSchedulerService
20
16
  private initialized = false
21
17
 
22
- constructor(optionsOrInstance: PgBoss.ConstructorOptions | string | PgBoss) {
18
+ constructor(optionsOrInstance: ConstructorOptions | string | PgBoss) {
23
19
  if (optionsOrInstance instanceof PgBoss) {
24
20
  this.pgBoss = optionsOrInstance
25
21
  } else {
@@ -56,17 +52,13 @@ export class PgBossServiceFactory {
56
52
  */
57
53
  getQueueWorkers(
58
54
  singletonServices: CoreSingletonServices,
59
- createSessionServices?: CreateSessionServices<
60
- CoreSingletonServices,
61
- CoreServices,
62
- any
63
- >
55
+ createWireServices?: CreateWireServices
64
56
  ): PgBossQueueWorkers {
65
57
  if (!this.queueWorkers) {
66
58
  this.queueWorkers = new PgBossQueueWorkers(
67
59
  this.pgBoss,
68
60
  singletonServices,
69
- createSessionServices
61
+ createWireServices
70
62
  )
71
63
  }
72
64
  return this.queueWorkers
@@ -91,4 +83,11 @@ export class PgBossServiceFactory {
91
83
  this.initialized = false
92
84
  }
93
85
  }
86
+
87
+ /**
88
+ * Alias for close() - used by stopSingletonServices
89
+ */
90
+ async stop(): Promise<void> {
91
+ await this.close()
92
+ }
94
93
  }
package/src/utils.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { QueueJob, QueueJobStatus } from '@pikku/core/queue'
2
- import PgBoss from 'pg-boss'
2
+ import { PgBoss, type JobWithMetadata } from 'pg-boss'
3
3
 
4
4
  /**
5
5
  * Map pg-boss job state to our queue job status
@@ -26,7 +26,7 @@ const mapPgBossStateToStatus = (state: string): QueueJobStatus => {
26
26
  * Map pg-boss job to QueueJob interface
27
27
  */
28
28
  export const mapPgBossJobToQueueJob = <In, Out>(
29
- pgBossJob: PgBoss.JobWithMetadata<In>,
29
+ pgBossJob: JobWithMetadata<In>,
30
30
  pgBossInstance: PgBoss
31
31
  ): QueueJob<In, Out> => {
32
32
  return {