@toa.io/extensions.convergence 1.0.0-alpha.298 → 1.0.0-alpha.299

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2020-present Artem Gurtovoi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.convergence",
3
- "version": "1.0.0-alpha.298",
3
+ "version": "1.0.0-alpha.299",
4
4
  "type": "module",
5
5
  "description": "Toa Convergence extension",
6
6
  "author": "temich <tema.gurtovoy@gmail.com>",
@@ -28,10 +28,11 @@
28
28
  "transpile": "tsc"
29
29
  },
30
30
  "dependencies": {
31
- "@toa.io/core": "1.0.0-alpha.293",
32
- "@toa.io/definitions": "1.0.0-alpha.298",
31
+ "@toa.io/core": "1.0.0-alpha.299",
32
+ "@toa.io/definitions": "1.0.0-alpha.299",
33
33
  "@toa.io/generic": "1.0.0-alpha.292",
34
- "@toa.io/pointer": "1.0.0-alpha.293",
34
+ "@toa.io/pointer": "1.0.0-alpha.299",
35
35
  "openspan": "1.0.0-alpha.293"
36
- }
36
+ },
37
+ "gitHead": "2ba95b16f2cf06f82d6ff8d436761e843e5e4b43"
37
38
  }
package/readme.md CHANGED
@@ -5,39 +5,51 @@ entity state.
5
5
 
6
6
  ## Declaration
7
7
 
8
- A region is a deployment of the context, not a variant of it: one context declares every region
9
- there is, at its root.
8
+ A region is a deployment of the context, and **the environment is which region it is** — the
9
+ same thing that already decides which database and which broker it uses. So a context declares
10
+ one of these per region, and they share nothing:
10
11
 
11
12
  ```yaml
12
13
  # context.toa.yaml
13
- convergence:
14
- - region: eu
15
- priority: 0 # a rank: 0 outranks 1
16
- binding:
17
- provider: amqp
18
- pointer: [amqp://cnv-eu-0.example.com, amqp://cnv-eu-1.example.com]
19
- - region: us
20
- priority: 1
21
- binding: { provider: amqp, pointer: amqp://cnv-us.example.com }
14
+ mongodb@eu: mongodb://mongo.eu.example.com/store
15
+ mongodb@us: mongodb://mongo.us.example.com/store
16
+
17
+ convergence@eu:
18
+ priority: 0 # a rank: 0 outranks 1
19
+ binding:
20
+ provider: amqp
21
+ pointer: [amqp://cnv-eu-0.example.com, amqp://cnv-eu-1.example.com]
22
+
23
+ convergence@us:
24
+ priority: 1
25
+ binding: { provider: amqp, pointer: amqp://cnv-us.example.com }
22
26
  ```
23
27
 
28
+ ```shell
29
+ $ toa deploy eu
30
+ ```
31
+
32
+ An environment that declares no `convergence` is not a region and converges nothing, so the
33
+ same context still deploys to `staging` as one place.
34
+
24
35
  Nothing is declared in a manifest. A context that declares convergence converges **every
25
- component that stores anything**, as the outbox publishes every event something consumes.
36
+ component that stores anything** — its own, and the ones its extensions ship: identity is what
37
+ holds users, their roles and the keys their tokens are read with, and a region without them is
38
+ one where nobody registered elsewhere exists.
39
+
40
+ **A storage that does not converge stands the component down, and says so at boot.** Nothing
41
+ else about the component changes and nothing else is held back; it is one line in the log,
42
+ because a component quietly not converging is two regions differing with nothing to notice it.
26
43
 
27
44
  `pointer` is a [pointer](/libraries/pointer), so a URL carries no credentials — they are
28
45
  deployed as secrets — and shards syntax works. It is flat: a URL or a list of them. A region has
29
46
  one set of brokers, and they are that region's own, shared with nothing else.
30
47
 
31
- ## Deploying
32
-
33
- Which region a deployment is, is chosen when it is deployed:
34
-
35
- ```shell
36
- $ TOA_CONVERGENCE_REGION=eu toa deploy production
37
- ```
38
-
39
- An operator can write it in `.env` instead of prefixing every command. Without it, a context
40
- that declares convergence is refused rather than deployed as a region it cannot name.
48
+ **`priority` is the same table in every region, and nothing checks that.** A deployment reads
49
+ only its own declaration, so two regions given one rank is a misconfiguration no deploy can see:
50
+ ties between those two would resolve for neither. What notices is the runtime — a record can
51
+ only carry the rank of the region that wrote it, so one arriving with this region's own rank is
52
+ reported as an error.
41
53
 
42
54
  **The region an existing application is first deployed as is rank `0`.** Its records were
43
55
  written by whatever region it is now becoming, and that is what they are recorded as.
@@ -56,6 +68,11 @@ Each region's broker holds two exchanges, the same two names everywhere:
56
68
  | `convergence.in` | **federated** from every other region's `convergence.out`. |
57
69
  | `convergence.<namespace>.<name>` | a durable queue per component, bound to `convergence.in` under that component's key. |
58
70
 
71
+ Nothing declares those queues for a region that is not running yet, which is what makes adding
72
+ one an order that matters, and adding a converging component to a running pair the same:
73
+ `toa export convergence <environment>` prints what a region's broker must carry, and
74
+ [operations](./operations.md) says when to declare it.
75
+
59
76
  **Toa does not configure the federation.** On each region's broker, an upstream per other region
60
77
  and one policy over them:
61
78
 
@@ -96,14 +113,19 @@ where a change that failed to publish would be lost rather than recovered, and t
96
113
  differ with nothing to say so. Events degrade there; convergence refuses.
97
114
 
98
115
  **[Atomicity](/connectors/atomicity).** Without it the outbox pump recovers nothing, so a
99
- publication that fails is never retried and that change is lost for good. Nothing detects this,
100
- which is why it is written here rather than raised.
116
+ publication that fails is never retried and that change is lost for good. Nothing can refuse at
117
+ boot over it — a replica owns no lane for a moment anyway — so what happens instead is that the
118
+ pump says so, every ten cycles it has owned none.
119
+
120
+ **Which delayed calls a region makes is `cadence.regions`,** not something held back from
121
+ convergence: every region holds every region's rows, and each makes the calls of the region it
122
+ is. See [cadence](/extensions/cadence#regions).
101
123
 
102
124
  **The same components in every region.** A component deployed in one and not another has no
103
125
  queue there, so its records cannot be routed; publishing is `mandatory`, so what had nowhere to
104
126
  go is logged rather than dropped in silence.
105
127
 
106
- **Unique indexes other than `_id` are not safe.** Two regions can independently take the same
128
+ **Uniqueness over anything but `id` is not safe.** Two regions can independently take the same
107
129
  value, and the record that arrives second cannot be stored at all. It is logged and dropped;
108
130
  redelivery would not help.
109
131
 
@@ -114,4 +136,8 @@ that region's rank. An incoming record wins where its version is greater, or whe
114
136
  is equal and the region that wrote it outranks the one that wrote what is stored. `priority` is
115
137
  read there and nowhere else.
116
138
 
139
+ **`context.region` is that rank**, which is what a component reads to tell where it is running.
140
+ It is zero where an application is deployed as one place, which is what such a deployment's
141
+ records carry.
142
+
117
143
  An unmanaged operation reads the driver's own handle, and will see the field.
@@ -12,14 +12,22 @@ export declare class Destination extends Connector implements outbox.Destination
12
12
  private readonly resolve;
13
13
  private readonly span;
14
14
  private outbound;
15
+ /** whether the storage under this component turned out to be one that does not converge */
16
+ private disabled;
15
17
  constructor(locator: Locator, resolve: () => Promise<bindings.Outbound>);
16
- emit(event: outbox.Row['event']): Promise<void>;
18
+ /**
19
+ * Stands down: the half that receives found a storage that does not converge, so neither
20
+ * does this component, and publishing what no region will consume would be returns and
21
+ * nothing else. The row settles at once rather than staying outstanding for good.
22
+ */
23
+ disable(): void;
24
+ emit(row: outbox.Row): Promise<void>;
17
25
  protected open(): Promise<void>;
18
26
  }
19
27
  /** What convergence puts on the wire. Owned by this extension at both ends. */
20
28
  export interface Message {
21
29
  /** the record as it stands — VERSION, timestamps, REGION and all */
22
30
  record: object;
23
- /** W3C traceparent, so the merge continues the trace of the write that caused it */
31
+ /** W3C traceparent, so the far side continues the trace of the write that caused it */
24
32
  trace?: string;
25
33
  }
@@ -12,6 +12,8 @@ export class Destination extends Connector {
12
12
  resolve;
13
13
  span;
14
14
  outbound;
15
+ /** whether the storage under this component turned out to be one that does not converge */
16
+ disabled = false;
15
17
  constructor(locator, resolve) {
16
18
  super();
17
19
  this.label = locator.id;
@@ -22,21 +24,37 @@ export class Destination extends Connector {
22
24
  attributes: { 'messaging.destination.name': CHANNEL }
23
25
  };
24
26
  }
25
- async emit(event) {
27
+ /**
28
+ * Stands down: the half that receives found a storage that does not converge, so neither
29
+ * does this component, and publishing what no region will consume would be returns and
30
+ * nothing else. The row settles at once rather than staying outstanding for good.
31
+ */
32
+ disable() {
33
+ this.disabled = true;
34
+ }
35
+ async emit(row) {
36
+ if (this.disabled)
37
+ return;
26
38
  await console.span(this.span, async () => {
27
39
  const context = current();
28
40
  /*
29
41
  * The record says which region wrote it, so the message does not say it again. `trace`
30
42
  * is convergence's own field rather than anything a binding puts there: it owns both
31
- * ends, and it wants the merge to continue the trace of the write that caused it.
43
+ * ends, and it wants the far side to continue the trace of the write that caused it.
44
+ *
45
+ * `row.trail` is deliberately not carried. The far side writes the record through the
46
+ * storage rather than through an operation, so it makes no call and publishes no event —
47
+ * there is no chain there to continue, and nothing to refuse.
32
48
  */
33
- const message = { record: event.state };
49
+ const message = { record: row.event.state };
34
50
  if (context !== undefined)
35
51
  message.trace = encode(context);
36
52
  await this.outbound.send(this.label, message);
37
53
  });
38
54
  }
39
55
  async open() {
56
+ if (this.disabled)
57
+ return;
40
58
  this.outbound = await this.resolve();
41
59
  await this.outbound.connect();
42
60
  this.depends(this.outbound);
@@ -1 +1 @@
1
- {"version":3,"file":"Destination.js","sourceRoot":"","sources":["../source/Destination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAoB,MAAM,UAAU,CAAA;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,4CAA4C,CAAA;AAIpE;;;;GAIG;AACH,MAAM,OAAO,WAAY,SAAQ,SAAS;IACxB,IAAI,GAAG,OAAO,CAAA;IAEb,KAAK,CAAQ;IACb,OAAO,CAAkC;IACzC,IAAI,CAAa;IAE1B,QAAQ,CAAoB;IAEpC,YAAmB,OAAgB,EAAE,OAAyC;QAC5E,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,EAAE,CAAA;QACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QAEtB,IAAI,CAAC,IAAI,GAAG;YACV,IAAI,EAAE,GAAG,OAAO,OAAO;YACvB,IAAI,EAAE,UAAU;YAChB,UAAU,EAAE,EAAE,4BAA4B,EAAE,OAAO,EAAE;SACtD,CAAA;IACH,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,KAA0B;QAC1C,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YACvC,MAAM,OAAO,GAAG,OAAO,EAAE,CAAA;YAEzB;;;;eAIG;YACH,MAAM,OAAO,GAAY,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,CAAA;YAEhD,IAAI,OAAO,KAAK,SAAS;gBAAE,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;YAE1D,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QAC/C,CAAC,CAAC,CAAA;IACJ,CAAC;IAEkB,KAAK,CAAC,IAAI;QAC3B,IAAI,CAAC,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;QAEpC,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAA;QAE7B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC7B,CAAC;CACF"}
1
+ {"version":3,"file":"Destination.js","sourceRoot":"","sources":["../source/Destination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAoB,MAAM,UAAU,CAAA;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,4CAA4C,CAAA;AAIpE;;;;GAIG;AACH,MAAM,OAAO,WAAY,SAAQ,SAAS;IACxB,IAAI,GAAG,OAAO,CAAA;IAEb,KAAK,CAAQ;IACb,OAAO,CAAkC;IACzC,IAAI,CAAa;IAE1B,QAAQ,CAAoB;IAEpC,2FAA2F;IACnF,QAAQ,GAAG,KAAK,CAAA;IAExB,YAAmB,OAAgB,EAAE,OAAyC;QAC5E,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,EAAE,CAAA;QACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QAEtB,IAAI,CAAC,IAAI,GAAG;YACV,IAAI,EAAE,GAAG,OAAO,OAAO;YACvB,IAAI,EAAE,UAAU;YAChB,UAAU,EAAE,EAAE,4BAA4B,EAAE,OAAO,EAAE;SACtD,CAAA;IACH,CAAC;IAED;;;;OAIG;IACI,OAAO;QACZ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAA;IACtB,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,GAAe;QAC/B,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAM;QAEzB,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YACvC,MAAM,OAAO,GAAG,OAAO,EAAE,CAAA;YAEzB;;;;;;;;eAQG;YACH,MAAM,OAAO,GAAY,EAAE,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;YAEpD,IAAI,OAAO,KAAK,SAAS;gBAAE,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;YAE1D,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QAC/C,CAAC,CAAC,CAAA;IACJ,CAAC;IAEkB,KAAK,CAAC,IAAI;QAC3B,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAM;QAEzB,IAAI,CAAC,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;QAEpC,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAA;QAE7B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC7B,CAAC;CACF"}
@@ -10,15 +10,17 @@ export declare class Factory implements extensions.Factory {
10
10
  private readonly host;
11
11
  /** one publisher for the process, however many components send through it */
12
12
  private outbound;
13
+ /**
14
+ * What this extension was given, by component, until the storage of that component is made.
15
+ * What decorates a storage is every extension loaded in the process rather than the ones a
16
+ * manifest named, so this is what tells a component this extension converges from one it was
17
+ * never asked about — one that stores nothing, or one of another composition entirely.
18
+ */
19
+ private readonly destinations;
13
20
  constructor(host: extensions.Host);
14
21
  /** out */
15
22
  destination(locator: Locator, _: null, manifest: Manifest): Destination | undefined;
16
- /**
17
- * In. Guarded, because what decorates a storage is every extension loaded in the process
18
- * rather than the ones a manifest named: once anything here converges, this is asked about
19
- * everything. Convergence is a property of a deployment, so what it is deployed with is what
20
- * says whether there is any.
21
- */
23
+ /** in, for the components this extension was given and no others */
22
24
  storage(storage: storages.Storage, locator: Locator): storages.Storage;
23
25
  private publisher;
24
26
  private binding;
@@ -1,3 +1,4 @@
1
+ import { console } from 'openspan';
1
2
  import { environment } from '@toa.io/generic';
2
3
  import { resolve } from '@toa.io/pointer';
3
4
  import { BINDING, BROKERS, CHANNEL, ID } from '@toa.io/definitions/extensions.convergence';
@@ -12,6 +13,13 @@ export class Factory {
12
13
  host;
13
14
  /** one publisher for the process, however many components send through it */
14
15
  outbound;
16
+ /**
17
+ * What this extension was given, by component, until the storage of that component is made.
18
+ * What decorates a storage is every extension loaded in the process rather than the ones a
19
+ * manifest named, so this is what tells a component this extension converges from one it was
20
+ * never asked about — one that stores nothing, or one of another composition entirely.
21
+ */
22
+ destinations = new Map();
15
23
  constructor(host) {
16
24
  this.host = host;
17
25
  }
@@ -19,17 +27,32 @@ export class Factory {
19
27
  destination(locator, _, manifest) {
20
28
  if (!converging() || manifest.entity === undefined)
21
29
  return undefined;
22
- return new Destination(locator, async () => this.publisher());
30
+ const destination = new Destination(locator, async () => this.publisher());
31
+ this.destinations.set(locator.id, destination);
32
+ return destination;
23
33
  }
24
- /**
25
- * In. Guarded, because what decorates a storage is every extension loaded in the process
26
- * rather than the ones a manifest named: once anything here converges, this is asked about
27
- * everything. Convergence is a property of a deployment, so what it is deployed with is what
28
- * says whether there is any.
29
- */
34
+ /** in, for the components this extension was given and no others */
30
35
  storage(storage, locator) {
31
- if (!converging())
36
+ const destination = this.destinations.get(locator.id);
37
+ // the pairing is for the component being built, and one is built once: an extension is
38
+ // loaded once for the life of a process, and holding a component's id past its build
39
+ // would decorate the next one to carry that id
40
+ this.destinations.delete(locator.id);
41
+ if (destination === undefined || !converging())
42
+ return storage;
43
+ /*
44
+ * A storage that does not converge has nowhere to put what another region wrote, so
45
+ * neither does this component — and the half that publishes stands down with it, because
46
+ * what it sent would reach a queue no region declares. Said out loud, because a component
47
+ * silently not converging is two regions differing with nothing to notice it.
48
+ */
49
+ if (storage.converges !== true) {
50
+ destination.disable();
51
+ console.warn('Component does not converge: its storage cannot', {
52
+ component: locator.id
53
+ });
32
54
  return storage;
55
+ }
33
56
  return new Converging(storage, locator, async (sink) => this.host.inbound(this.binding(), CHANNEL, this.uris(), locator.id, sink));
34
57
  }
35
58
  async publisher() {
@@ -1 +1 @@
1
- {"version":3,"file":"Factory.js","sourceRoot":"","sources":["../source/Factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACzC,OAAO,EACL,OAAO,EACP,OAAO,EACP,OAAO,EACP,EAAE,EACH,MAAM,4CAA4C,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAIzC;;;;GAIG;AACH,MAAM,OAAO,OAAO;IACD,IAAI,CAAiB;IAEtC,6EAA6E;IACrE,QAAQ,CAAwC;IAExD,YAAmB,IAAqB;QACtC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAED,UAAU;IACH,WAAW,CAChB,OAAgB,EAChB,CAAO,EACP,QAAkB;QAElB,IAAI,CAAC,UAAU,EAAE,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QAEpE,OAAO,IAAI,WAAW,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;IAC/D,CAAC;IAED;;;;;OAKG;IACI,OAAO,CAAC,OAAyB,EAAE,OAAgB;QACxD,IAAI,CAAC,UAAU,EAAE;YAAE,OAAO,OAAO,CAAA;QAEjC,OAAO,IAAI,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CACrD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAC1E,CAAA;IACH,CAAC;IAEO,KAAK,CAAC,SAAS;QACrB,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QAE1E,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;IAEO,OAAO;QACb,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QAE1C,IAAI,SAAS,KAAK,SAAS;YACzB,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,wDAAwD,CAAC,CAAA;QAErF,OAAO,SAAS,CAAA;IAClB,CAAC;IAEO,IAAI;QACV,OAAO,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IAC7B,CAAC;CACF;AAED,6FAA6F;AAC7F,SAAS,UAAU;IACjB,OAAO,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,SAAS,CAAA;AAC/C,CAAC"}
1
+ {"version":3,"file":"Factory.js","sourceRoot":"","sources":["../source/Factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACzC,OAAO,EACL,OAAO,EACP,OAAO,EACP,OAAO,EACP,EAAE,EACH,MAAM,4CAA4C,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAIzC;;;;GAIG;AACH,MAAM,OAAO,OAAO;IACD,IAAI,CAAiB;IAEtC,6EAA6E;IACrE,QAAQ,CAAwC;IAExD;;;;;OAKG;IACc,YAAY,GAAG,IAAI,GAAG,EAAuB,CAAA;IAE9D,YAAmB,IAAqB;QACtC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAED,UAAU;IACH,WAAW,CAChB,OAAgB,EAChB,CAAO,EACP,QAAkB;QAElB,IAAI,CAAC,UAAU,EAAE,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QAEpE,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;QAE1E,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,WAAW,CAAC,CAAA;QAE9C,OAAO,WAAW,CAAA;IACpB,CAAC;IAED,oEAAoE;IAC7D,OAAO,CAAC,OAAyB,EAAE,OAAgB;QACxD,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAErD,uFAAuF;QACvF,qFAAqF;QACrF,+CAA+C;QAC/C,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAEpC,IAAI,WAAW,KAAK,SAAS,IAAI,CAAC,UAAU,EAAE;YAAE,OAAO,OAAO,CAAA;QAE9D;;;;;WAKG;QACH,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC/B,WAAW,CAAC,OAAO,EAAE,CAAA;YAErB,OAAO,CAAC,IAAI,CAAC,iDAAiD,EAAE;gBAC9D,SAAS,EAAE,OAAO,CAAC,EAAE;aACtB,CAAC,CAAA;YAEF,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,OAAO,IAAI,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CACrD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAC1E,CAAA;IACH,CAAC;IAEO,KAAK,CAAC,SAAS;QACrB,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QAE1E,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;IAEO,OAAO;QACb,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QAE1C,IAAI,SAAS,KAAK,SAAS;YACzB,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,wDAAwD,CAAC,CAAA;QAErF,OAAO,SAAS,CAAA;IAClB,CAAC;IAEO,IAAI;QACV,OAAO,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IAC7B,CAAC;CACF;AAED,6FAA6F;AAC7F,SAAS,UAAU;IACjB,OAAO,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,SAAS,CAAA;AAC/C,CAAC"}
@@ -27,7 +27,7 @@ export declare class Converging extends Connector implements storages.Storage, b
27
27
  get raw(): unknown;
28
28
  get outbox(): storages.Storage['outbox'];
29
29
  get migrates(): boolean | undefined;
30
- get merges(): boolean | undefined;
30
+ get converges(): boolean | undefined;
31
31
  get(query: storages.Query): Promise<storages.Record | null>;
32
32
  find(query: storages.Query): Promise<storages.Record[]>;
33
33
  stream(query?: storages.Query): Promise<Readable>;
@@ -35,5 +35,5 @@ export declare class Converging extends Connector implements storages.Storage, b
35
35
  massStore(records: storages.Record[], rows?: outbox.Row[]): Promise<boolean>;
36
36
  upsert(query: storages.Query, changeset: object, row?: outbox.Row): Promise<storages.Record | null>;
37
37
  ensure(query: storages.Query | undefined, properties: object, record: storages.Record, row?: outbox.Row): Promise<storages.Record>;
38
- merge(record: storages.Record): Promise<boolean>;
38
+ converge(record: storages.Record): Promise<boolean>;
39
39
  }
@@ -1,4 +1,5 @@
1
1
  import { console, decode, run } from 'openspan';
2
+ import { environment } from '@toa.io/generic';
2
3
  import { Connector } from '@toa.io/core';
3
4
  import { CHANNEL } from '@toa.io/definitions/extensions.convergence';
4
5
  /**
@@ -31,7 +32,7 @@ export class Converging extends Connector {
31
32
  attributes: { 'messaging.destination.name': CHANNEL }
32
33
  };
33
34
  this.processing = {
34
- name: `${CHANNEL} merge`,
35
+ name: `${CHANNEL} process`,
35
36
  kind: 'consumer',
36
37
  service: locator.id,
37
38
  attributes: { 'messaging.destination.name': CHANNEL }
@@ -43,12 +44,24 @@ export class Converging extends Connector {
43
44
  */
44
45
  async accept(message) {
45
46
  const { record, trace } = message;
47
+ /*
48
+ * A record carrying this region's own rank cannot have come from anywhere: a region is
49
+ * not bound to what it publishes, and it republishes nothing it converges, so the rank on a
50
+ * record that arrives is the rank of the region that wrote it. Two of them sharing one is
51
+ * a misconfiguration nothing else can see — no deployment knows what the others declared —
52
+ * and it means ties between those two resolve for neither.
53
+ */
54
+ if (record.REGION === REGION)
55
+ console.error('Convergence received a record of this region\'s own rank', {
56
+ component: this.locator.id,
57
+ region: REGION
58
+ });
46
59
  const remote = trace === undefined ? null : decode(trace);
47
60
  const task = async () => console.span(this.delivery, async () => console.span(this.processing, async () => {
48
- const merged = await this.storage.merge(record);
49
- console.debug('Convergence merge', {
61
+ const applied = await this.storage.converge(record);
62
+ console.trace('Convergence processed', {
50
63
  component: this.locator.id,
51
- outcome: merged ? 'applied' : 'stale'
64
+ outcome: applied ? 'applied' : 'stale'
52
65
  });
53
66
  }));
54
67
  if (remote === null)
@@ -57,18 +70,13 @@ export class Converging extends Connector {
57
70
  await run(remote, task);
58
71
  }
59
72
  async open() {
60
- if (this.storage.merges !== true)
61
- throw new Error(`Component '${this.locator.id}' converges, which its storage does not do. ` +
62
- 'A record written in another region has nowhere to land.');
63
73
  /*
64
74
  * Without a durable outbox a publication is lost where the process dies before it lands,
65
75
  * and a lost one here is two regions differing for good, with nothing that notices and
66
76
  * nothing that repairs it. That is why this refuses where events only degrade.
67
77
  */
68
78
  if (this.storage.outbox === undefined)
69
- throw new Error(`Component '${this.locator.id}' converges, and its outbox is not durable. ` +
70
- 'MongoDB is not a replica set, so a change that fails to publish is lost rather ' +
71
- 'than recovered, and the regions would differ with nothing to say so.');
79
+ throw new Error(`Component '${this.locator.id}' converges, and its storage offers no outbox.`);
72
80
  const inbound = await this.subscribe(this);
73
81
  await inbound.connect();
74
82
  this.depends(inbound);
@@ -83,8 +91,8 @@ export class Converging extends Connector {
83
91
  get migrates() {
84
92
  return this.storage.migrates;
85
93
  }
86
- get merges() {
87
- return this.storage.merges;
94
+ get converges() {
95
+ return this.storage.converges;
88
96
  }
89
97
  async get(query) {
90
98
  return this.storage.get(query);
@@ -108,8 +116,10 @@ export class Converging extends Connector {
108
116
  async ensure(query, properties, record, row) {
109
117
  return this.storage.ensure(query, properties, record, row);
110
118
  }
111
- async merge(record) {
112
- return this.storage.merge(record);
119
+ async converge(record) {
120
+ return this.storage.converge(record);
113
121
  }
114
122
  }
123
+ /** The rank of this region, which nothing that arrives should be carrying. */
124
+ const REGION = Number(environment.get('TOA_REGION') ?? 0);
115
125
  //# sourceMappingURL=Storage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Storage.js","sourceRoot":"","sources":["../source/Storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAoB,MAAM,UAAU,CAAA;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,4CAA4C,CAAA;AAMpE;;;;;;;;GAQG;AACH,MAAM,OAAO,UAAW,SAAQ,SAAS;IACtB,OAAO,CAAkB;IACzB,OAAO,CAAS;IAChB,SAAS,CAA2C;IACpD,QAAQ,CAAa;IACrB,UAAU,CAAa;IAExC,YACE,OAAyB,EACzB,OAAgB,EAChB,OAAkD;QAElD,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,SAAS,GAAG,OAAO,CAAA;QAExB,yFAAyF;QACzF,6BAA6B;QAC7B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;QAErB,IAAI,CAAC,QAAQ,GAAG;YACd,IAAI,EAAE,GAAG,OAAO,UAAU;YAC1B,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,OAAO;YAChB,UAAU,EAAE,EAAE,4BAA4B,EAAE,OAAO,EAAE;SACtD,CAAA;QAED,IAAI,CAAC,UAAU,GAAG;YAChB,IAAI,EAAE,GAAG,OAAO,QAAQ;YACxB,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,OAAO,CAAC,EAAE;YACnB,UAAU,EAAE,EAAE,4BAA4B,EAAE,OAAO,EAAE;SACtD,CAAA;IACH,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,MAAM,CAAC,OAAe;QACjC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAkB,CAAA;QAE5C,MAAM,MAAM,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAEzD,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE,CACrC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CACrC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YACvC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAM,CAAC,MAAyB,CAAC,CAAA;YAEnE,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE;gBACjC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE;gBAC1B,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;aACtC,CAAC,CAAA;QACJ,CAAC,CAAC,CACH,CAAA;QAEH,IAAI,MAAM,KAAK,IAAI;YAAE,MAAM,IAAI,EAAE,CAAA;;YAC5B,MAAM,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAC9B,CAAC;IAEkB,KAAK,CAAC,IAAI;QAC3B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,IAAI;YAC9B,MAAM,IAAI,KAAK,CACb,cAAc,IAAI,CAAC,OAAO,CAAC,EAAE,8CAA8C;gBACzE,yDAAyD,CAC5D,CAAA;QAEH;;;;WAIG;QACH,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS;YACnC,MAAM,IAAI,KAAK,CACb,cAAc,IAAI,CAAC,OAAO,CAAC,EAAE,8CAA8C;gBACzE,iFAAiF;gBACjF,sEAAsE,CACzE,CAAA;QAEH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QAE1C,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;QAEvB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IACvB,CAAC;IAED,mBAAmB;IAEnB,IAAW,GAAG;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAA;IACzB,CAAC;IAED,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAA;IAC5B,CAAC;IAED,IAAW,QAAQ;QACjB,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAA;IAC9B,CAAC;IAED,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAA;IAC5B,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,KAAqB;QACpC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAChC,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,KAAqB;QACrC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACjC,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,KAAsB;QACxC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACnC,CAAC;IAEM,KAAK,CAAC,KAAK,CAAC,MAAuB,EAAE,GAAgB;QAC1D,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACxC,CAAC;IAEM,KAAK,CAAC,SAAS,CAAC,OAA0B,EAAE,IAAmB;QACpE,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;IAC9C,CAAC;IAEM,KAAK,CAAC,MAAM,CACjB,KAAqB,EACrB,SAAiB,EACjB,GAAgB;QAEhB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,CAAA;IACnD,CAAC;IAED,sCAAsC;IAC/B,KAAK,CAAC,MAAM,CACjB,KAAiC,EACjC,UAAkB,EAClB,MAAuB,EACvB,GAAgB;QAEhB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;IAC5D,CAAC;IAEM,KAAK,CAAC,KAAK,CAAC,MAAuB;QACxC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAM,CAAC,MAAM,CAAC,CAAA;IACpC,CAAC;CAGF"}
1
+ {"version":3,"file":"Storage.js","sourceRoot":"","sources":["../source/Storage.ts"],"names":[],"mappings":"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"}