@atproto/bsync 0.0.29 → 0.0.30

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,5 +1,11 @@
1
1
  # @atproto/bsync
2
2
 
3
+ ## 0.0.30
4
+
5
+ ### Patch Changes
6
+
7
+ - [#5117](https://github.com/bluesky-social/atproto/pull/5117) [`8a4c88b`](https://github.com/bluesky-social/atproto/commit/8a4c88b0f63fb2d82b1391d64c54e0c760fac48b) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Use http-terminator to close http server
8
+
3
9
  ## 0.0.29
4
10
 
5
11
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -9,9 +9,9 @@ export { httpLogger } from './logger.js';
9
9
  export declare class BsyncService {
10
10
  ctx: AppContext;
11
11
  server: http.Server;
12
- private ac;
13
12
  private terminator;
14
- private dbStatsInterval?;
13
+ private ac;
14
+ private state;
15
15
  constructor(opts: {
16
16
  ctx: AppContext;
17
17
  server: http.Server;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAA;AAO5B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAO5D,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,qBAAa,YAAY;IAChB,GAAG,EAAE,UAAU,CAAA;IACf,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;IAC1B,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,eAAe,CAAC,CAAgB;IAExC,YAAY,IAAI,EAAE;QAChB,GAAG,EAAE,UAAU,CAAA;QACf,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;QACnB,EAAE,EAAE,eAAe,CAAA;KACpB,EAKA;IAED,OAAa,MAAM,CACjB,GAAG,EAAE,YAAY,EACjB,SAAS,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GACrC,OAAO,CAAC,YAAY,CAAC,CAiBvB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAmBlC;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAM7B;IAEK,cAAc,kBAoBnB;CACF;eAEc,YAAY"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAA;AAI5B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAO5D,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAIxC,qBAAa,YAAY;IAChB,GAAG,EAAE,UAAU,CAAA;IACf,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;IAC1B,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,KAAK,CAAmC;IAEhD,YAAY,IAAI,EAAE;QAChB,GAAG,EAAE,UAAU,CAAA;QACf,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;QACnB,EAAE,EAAE,eAAe,CAAA;KACpB,EAKA;IAED,OAAa,MAAM,CACjB,GAAG,EAAE,YAAY,EACjB,SAAS,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GACrC,OAAO,CAAC,YAAY,CAAC,CAiBvB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CA0BlC;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAS7B;IAEK,cAAc,kBAoBnB;CACF;eAEc,YAAY"}
package/dist/index.js CHANGED
@@ -1,10 +1,8 @@
1
1
  import events from 'node:events';
2
2
  import http from 'node:http';
3
3
  import { connectNodeAdapter } from '@connectrpc/connect-node';
4
- // eslint-disable-next-line import/default, import/no-named-as-default-member
4
+ // eslint-disable-next-line import/default
5
5
  import httpTerminator from 'http-terminator';
6
- // eslint-disable-next-line import/no-named-as-default-member
7
- const { createHttpTerminator } = httpTerminator;
8
6
  import { AppContext } from './context.js';
9
7
  import { createMuteOpChannel } from './db/schema/mute_op.js';
10
8
  import { createNotifOpChannel } from './db/schema/notif_op.js';
@@ -18,10 +16,11 @@ export { AppContext } from './context.js';
18
16
  export { httpLogger } from './logger.js';
19
17
  export class BsyncService {
20
18
  constructor(opts) {
19
+ this.state = 'initialized';
21
20
  this.ctx = opts.ctx;
22
21
  this.server = opts.server;
23
22
  this.ac = opts.ac;
24
- this.terminator = createHttpTerminator({ server: this.server });
23
+ this.terminator = httpTerminator.createHttpTerminator(opts);
25
24
  }
26
25
  static async create(cfg, overrides) {
27
26
  const ac = new AbortController();
@@ -42,16 +41,20 @@ export class BsyncService {
42
41
  return new BsyncService({ ctx, server, ac });
43
42
  }
44
43
  async start() {
45
- if (this.dbStatsInterval) {
44
+ if (this.state !== 'initialized') {
46
45
  throw new Error(`${this.constructor.name} already started`);
47
46
  }
48
- this.dbStatsInterval = setInterval(() => {
47
+ this.state = 'started';
48
+ const dbStatsInterval = setInterval(() => {
49
49
  dbLogger.info({
50
50
  idleCount: this.ctx.db.pool.idleCount,
51
51
  totalCount: this.ctx.db.pool.totalCount,
52
52
  waitingCount: this.ctx.db.pool.waitingCount,
53
53
  }, 'db pool stats');
54
54
  }, 10000);
55
+ this.ac.signal.addEventListener('abort', () => {
56
+ clearInterval(dbStatsInterval);
57
+ });
55
58
  await this.setupAppEvents();
56
59
  this.server.listen(this.ctx.cfg.service.port);
57
60
  this.server.keepAliveTimeout = 90000;
@@ -59,11 +62,16 @@ export class BsyncService {
59
62
  return this.server;
60
63
  }
61
64
  async destroy() {
65
+ if (this.state === 'destroyed')
66
+ return;
67
+ this.state = 'destroyed';
62
68
  this.ac.abort();
63
- await this.terminator.terminate();
64
- await this.ctx.db.close();
65
- clearInterval(this.dbStatsInterval);
66
- this.dbStatsInterval = undefined;
69
+ try {
70
+ await this.terminator.terminate();
71
+ }
72
+ finally {
73
+ await this.ctx.db.close();
74
+ }
67
75
  }
68
76
  async setupAppEvents() {
69
77
  const conn = await this.ctx.db.pool.connect();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAA;AAChC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAC7D,6EAA6E;AAC7E,OAAO,cAAc,MAAM,iBAAiB,CAAA;AAC5C,6DAA6D;AAC7D,MAAM,EAAE,oBAAoB,EAAE,GAAG,cAAc,CAAA;AAG/C,OAAO,EAAE,UAAU,EAAqB,MAAM,cAAc,CAAA;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,MAAM,MAAM,mBAAmB,CAAA;AAEtC,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,MAAM,OAAO,YAAY;IAOvB,YAAY,IAIX;QACC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAA;QACnB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;QACzB,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,CAAA;QACjB,IAAI,CAAC,UAAU,GAAG,oBAAoB,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;IACjE,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,MAAM,CACjB,GAAiB,EACjB,SAAsC;QAEtC,MAAM,EAAE,GAAG,IAAI,eAAe,EAAE,CAAA;QAChC,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;QAClE,MAAM,OAAO,GAAG,kBAAkB,CAAC;YACjC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC;YACnB,cAAc,EAAE,EAAE,CAAC,MAAM;SAC1B,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC5C,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;YAC1B,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,GAAG,CAAC,UAAU,GAAG,GAAG,CAAA;gBACpB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAA;gBACjD,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;YAClE,CAAC;YACD,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;QACnB,CAAC,CAAC,CAAA;QACF,OAAO,IAAI,YAAY,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;IAC9C,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,kBAAkB,CAAC,CAAA;QAC7D,CAAC;QACD,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE;YACtC,QAAQ,CAAC,IAAI,CACX;gBACE,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS;gBACrC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU;gBACvC,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY;aAC5C,EACD,eAAe,CAChB,CAAA;QACH,CAAC,EAAE,KAAK,CAAC,CAAA;QACT,MAAM,IAAI,CAAC,cAAc,EAAE,CAAA;QAC3B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QAC7C,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,KAAK,CAAA;QACpC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;QAC3C,OAAO,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAA;QACf,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAA;QACjC,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,CAAA;QACzB,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;QACnC,IAAI,CAAC,eAAe,GAAG,SAAS,CAAA;IAClC,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAA;QAC7C,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE;YAC7D,IAAI,EAAE,IAAI;SACX,CAAC,CAAA;QACF,mEAAmE;QACnE,IAAI,CAAC,KAAK,CAAC,UAAU,mBAAmB,EAAE,CAAC,CAAA;QAC3C,IAAI,CAAC,KAAK,CAAC,UAAU,oBAAoB,EAAE,CAAC,CAAA;QAC5C,IAAI,CAAC,KAAK,CAAC,UAAU,sBAAsB,EAAE,CAAC,CAAA;QAC9C,IAAI,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE;YAChC,IAAI,KAAK,CAAC,OAAO,KAAK,mBAAmB,EAAE,CAAC;gBAC1C,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAA;YAC3C,CAAC;YACD,IAAI,KAAK,CAAC,OAAO,KAAK,oBAAoB,EAAE,CAAC;gBAC3C,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAA;YAC5C,CAAC;YACD,IAAI,KAAK,CAAC,OAAO,KAAK,sBAAsB,EAAE,CAAC;gBAC7C,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAA;YAC9C,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;CACF;AAED,eAAe,YAAY,CAAA;AAE3B,MAAM,QAAQ,GAAG,CAAC,MAA0B,EAAE,EAAE;IAC9C,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IACzB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IAC1C,OAAO,GAAG,CAAC,QAAQ,KAAK,UAAU,CAAA;AACpC,CAAC,CAAA","sourcesContent":["import events from 'node:events'\nimport http from 'node:http'\nimport { connectNodeAdapter } from '@connectrpc/connect-node'\n// eslint-disable-next-line import/default, import/no-named-as-default-member\nimport httpTerminator from 'http-terminator'\n// eslint-disable-next-line import/no-named-as-default-member\nconst { createHttpTerminator } = httpTerminator\ntype HttpTerminator = ReturnType<typeof createHttpTerminator>\nimport { ServerConfig } from './config.js'\nimport { AppContext, AppContextOptions } from './context.js'\nimport { createMuteOpChannel } from './db/schema/mute_op.js'\nimport { createNotifOpChannel } from './db/schema/notif_op.js'\nimport { createOperationChannel } from './db/schema/operation.js'\nimport { dbLogger, loggerMiddleware } from './logger.js'\nimport routes from './routes/index.js'\n\nexport * from './config.js'\nexport * from './client.js'\nexport { Database } from './db/index.js'\nexport { AppContext } from './context.js'\nexport { httpLogger } from './logger.js'\n\nexport class BsyncService {\n public ctx: AppContext\n public server: http.Server\n private ac: AbortController\n private terminator: HttpTerminator\n private dbStatsInterval?: NodeJS.Timeout\n\n constructor(opts: {\n ctx: AppContext\n server: http.Server\n ac: AbortController\n }) {\n this.ctx = opts.ctx\n this.server = opts.server\n this.ac = opts.ac\n this.terminator = createHttpTerminator({ server: this.server })\n }\n\n static async create(\n cfg: ServerConfig,\n overrides?: Partial<AppContextOptions>,\n ): Promise<BsyncService> {\n const ac = new AbortController()\n const ctx = await AppContext.fromConfig(cfg, ac.signal, overrides)\n const handler = connectNodeAdapter({\n routes: routes(ctx),\n shutdownSignal: ac.signal,\n })\n const server = http.createServer((req, res) => {\n loggerMiddleware(req, res)\n if (isHealth(req.url)) {\n res.statusCode = 200\n res.setHeader('content-type', 'application/json')\n return res.end(JSON.stringify({ version: cfg.service.version }))\n }\n handler(req, res)\n })\n return new BsyncService({ ctx, server, ac })\n }\n\n async start(): Promise<http.Server> {\n if (this.dbStatsInterval) {\n throw new Error(`${this.constructor.name} already started`)\n }\n this.dbStatsInterval = setInterval(() => {\n dbLogger.info(\n {\n idleCount: this.ctx.db.pool.idleCount,\n totalCount: this.ctx.db.pool.totalCount,\n waitingCount: this.ctx.db.pool.waitingCount,\n },\n 'db pool stats',\n )\n }, 10000)\n await this.setupAppEvents()\n this.server.listen(this.ctx.cfg.service.port)\n this.server.keepAliveTimeout = 90000\n await events.once(this.server, 'listening')\n return this.server\n }\n\n async destroy(): Promise<void> {\n this.ac.abort()\n await this.terminator.terminate()\n await this.ctx.db.close()\n clearInterval(this.dbStatsInterval)\n this.dbStatsInterval = undefined\n }\n\n async setupAppEvents() {\n const conn = await this.ctx.db.pool.connect()\n this.ac.signal.addEventListener('abort', () => conn.release(), {\n once: true,\n })\n // if these error, unhandled rejection should cause process to exit\n conn.query(`listen ${createMuteOpChannel}`)\n conn.query(`listen ${createNotifOpChannel}`)\n conn.query(`listen ${createOperationChannel}`)\n conn.on('notification', (notif) => {\n if (notif.channel === createMuteOpChannel) {\n this.ctx.events.emit(createMuteOpChannel)\n }\n if (notif.channel === createNotifOpChannel) {\n this.ctx.events.emit(createNotifOpChannel)\n }\n if (notif.channel === createOperationChannel) {\n this.ctx.events.emit(createOperationChannel)\n }\n })\n }\n}\n\nexport default BsyncService\n\nconst isHealth = (urlStr: string | undefined) => {\n if (!urlStr) return false\n const url = new URL(urlStr, 'http://host')\n return url.pathname === '/_health'\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import events from 'node:events'\nimport http from 'node:http'\nimport { connectNodeAdapter } from '@connectrpc/connect-node'\n// eslint-disable-next-line import/default\nimport httpTerminator from 'http-terminator'\nimport { ServerConfig } from './config.js'\nimport { AppContext, AppContextOptions } from './context.js'\nimport { createMuteOpChannel } from './db/schema/mute_op.js'\nimport { createNotifOpChannel } from './db/schema/notif_op.js'\nimport { createOperationChannel } from './db/schema/operation.js'\nimport { dbLogger, loggerMiddleware } from './logger.js'\nimport routes from './routes/index.js'\n\nexport * from './config.js'\nexport * from './client.js'\nexport { Database } from './db/index.js'\nexport { AppContext } from './context.js'\nexport { httpLogger } from './logger.js'\n\ntype BsyncServiceState = 'initialized' | 'started' | 'destroyed'\n\nexport class BsyncService {\n public ctx: AppContext\n public server: http.Server\n private terminator: httpTerminator.HttpTerminator\n private ac: AbortController\n private state: BsyncServiceState = 'initialized'\n\n constructor(opts: {\n ctx: AppContext\n server: http.Server\n ac: AbortController\n }) {\n this.ctx = opts.ctx\n this.server = opts.server\n this.ac = opts.ac\n this.terminator = httpTerminator.createHttpTerminator(opts)\n }\n\n static async create(\n cfg: ServerConfig,\n overrides?: Partial<AppContextOptions>,\n ): Promise<BsyncService> {\n const ac = new AbortController()\n const ctx = await AppContext.fromConfig(cfg, ac.signal, overrides)\n const handler = connectNodeAdapter({\n routes: routes(ctx),\n shutdownSignal: ac.signal,\n })\n const server = http.createServer((req, res) => {\n loggerMiddleware(req, res)\n if (isHealth(req.url)) {\n res.statusCode = 200\n res.setHeader('content-type', 'application/json')\n return res.end(JSON.stringify({ version: cfg.service.version }))\n }\n handler(req, res)\n })\n return new BsyncService({ ctx, server, ac })\n }\n\n async start(): Promise<http.Server> {\n if (this.state !== 'initialized') {\n throw new Error(`${this.constructor.name} already started`)\n }\n this.state = 'started'\n\n const dbStatsInterval = setInterval(() => {\n dbLogger.info(\n {\n idleCount: this.ctx.db.pool.idleCount,\n totalCount: this.ctx.db.pool.totalCount,\n waitingCount: this.ctx.db.pool.waitingCount,\n },\n 'db pool stats',\n )\n }, 10000)\n\n this.ac.signal.addEventListener('abort', () => {\n clearInterval(dbStatsInterval)\n })\n\n await this.setupAppEvents()\n this.server.listen(this.ctx.cfg.service.port)\n this.server.keepAliveTimeout = 90000\n await events.once(this.server, 'listening')\n return this.server\n }\n\n async destroy(): Promise<void> {\n if (this.state === 'destroyed') return\n this.state = 'destroyed'\n this.ac.abort()\n try {\n await this.terminator.terminate()\n } finally {\n await this.ctx.db.close()\n }\n }\n\n async setupAppEvents() {\n const conn = await this.ctx.db.pool.connect()\n this.ac.signal.addEventListener('abort', () => conn.release(), {\n once: true,\n })\n // if these error, unhandled rejection should cause process to exit\n conn.query(`listen ${createMuteOpChannel}`)\n conn.query(`listen ${createNotifOpChannel}`)\n conn.query(`listen ${createOperationChannel}`)\n conn.on('notification', (notif) => {\n if (notif.channel === createMuteOpChannel) {\n this.ctx.events.emit(createMuteOpChannel)\n }\n if (notif.channel === createNotifOpChannel) {\n this.ctx.events.emit(createNotifOpChannel)\n }\n if (notif.channel === createOperationChannel) {\n this.ctx.events.emit(createOperationChannel)\n }\n })\n }\n}\n\nexport default BsyncService\n\nconst isHealth = (urlStr: string | undefined) => {\n if (!urlStr) return false\n const url = new URL(urlStr, 'http://host')\n return url.pathname === '/_health'\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/bsync",
3
- "version": "0.0.29",
3
+ "version": "0.0.30",
4
4
  "license": "MIT",
5
5
  "description": "Sychronizing service for app.bsky App View (Bluesky API)",
6
6
  "keywords": [
@@ -25,8 +25,8 @@
25
25
  "pg": "^8.10.0",
26
26
  "pino-http": "^8.2.1",
27
27
  "typed-emitter": "^2.1.0",
28
- "@atproto/syntax": "^0.6.2",
29
- "@atproto/common": "^0.6.3"
28
+ "@atproto/common": "^0.6.3",
29
+ "@atproto/syntax": "^0.6.2"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@bufbuild/buf": "^1.28.1",
package/src/index.ts CHANGED
@@ -1,11 +1,8 @@
1
1
  import events from 'node:events'
2
2
  import http from 'node:http'
3
3
  import { connectNodeAdapter } from '@connectrpc/connect-node'
4
- // eslint-disable-next-line import/default, import/no-named-as-default-member
4
+ // eslint-disable-next-line import/default
5
5
  import httpTerminator from 'http-terminator'
6
- // eslint-disable-next-line import/no-named-as-default-member
7
- const { createHttpTerminator } = httpTerminator
8
- type HttpTerminator = ReturnType<typeof createHttpTerminator>
9
6
  import { ServerConfig } from './config.js'
10
7
  import { AppContext, AppContextOptions } from './context.js'
11
8
  import { createMuteOpChannel } from './db/schema/mute_op.js'
@@ -20,12 +17,14 @@ export { Database } from './db/index.js'
20
17
  export { AppContext } from './context.js'
21
18
  export { httpLogger } from './logger.js'
22
19
 
20
+ type BsyncServiceState = 'initialized' | 'started' | 'destroyed'
21
+
23
22
  export class BsyncService {
24
23
  public ctx: AppContext
25
24
  public server: http.Server
25
+ private terminator: httpTerminator.HttpTerminator
26
26
  private ac: AbortController
27
- private terminator: HttpTerminator
28
- private dbStatsInterval?: NodeJS.Timeout
27
+ private state: BsyncServiceState = 'initialized'
29
28
 
30
29
  constructor(opts: {
31
30
  ctx: AppContext
@@ -35,7 +34,7 @@ export class BsyncService {
35
34
  this.ctx = opts.ctx
36
35
  this.server = opts.server
37
36
  this.ac = opts.ac
38
- this.terminator = createHttpTerminator({ server: this.server })
37
+ this.terminator = httpTerminator.createHttpTerminator(opts)
39
38
  }
40
39
 
41
40
  static async create(
@@ -61,10 +60,12 @@ export class BsyncService {
61
60
  }
62
61
 
63
62
  async start(): Promise<http.Server> {
64
- if (this.dbStatsInterval) {
63
+ if (this.state !== 'initialized') {
65
64
  throw new Error(`${this.constructor.name} already started`)
66
65
  }
67
- this.dbStatsInterval = setInterval(() => {
66
+ this.state = 'started'
67
+
68
+ const dbStatsInterval = setInterval(() => {
68
69
  dbLogger.info(
69
70
  {
70
71
  idleCount: this.ctx.db.pool.idleCount,
@@ -74,6 +75,11 @@ export class BsyncService {
74
75
  'db pool stats',
75
76
  )
76
77
  }, 10000)
78
+
79
+ this.ac.signal.addEventListener('abort', () => {
80
+ clearInterval(dbStatsInterval)
81
+ })
82
+
77
83
  await this.setupAppEvents()
78
84
  this.server.listen(this.ctx.cfg.service.port)
79
85
  this.server.keepAliveTimeout = 90000
@@ -82,11 +88,14 @@ export class BsyncService {
82
88
  }
83
89
 
84
90
  async destroy(): Promise<void> {
91
+ if (this.state === 'destroyed') return
92
+ this.state = 'destroyed'
85
93
  this.ac.abort()
86
- await this.terminator.terminate()
87
- await this.ctx.db.close()
88
- clearInterval(this.dbStatsInterval)
89
- this.dbStatsInterval = undefined
94
+ try {
95
+ await this.terminator.terminate()
96
+ } finally {
97
+ await this.ctx.db.close()
98
+ }
90
99
  }
91
100
 
92
101
  async setupAppEvents() {