@toa.io/atomicity 1.0.0-alpha.301 → 1.0.0-alpha.303

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
@@ -3,6 +3,21 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.0.0-alpha.303](https://github.com/toa-io/toa/compare/v1.0.0-alpha.302...v1.0.0-alpha.303) (2026-09-12)
7
+
8
+ ### Features
9
+
10
+ * **metrics:** count what no span reaches ([efabf89](https://github.com/toa-io/toa/commit/efabf89e78f98a604bb939f476a937fc9ee106b3))
11
+
12
+
13
+ # [1.0.0-alpha.302](https://github.com/toa-io/toa/compare/v1.0.0-alpha.301...v1.0.0-alpha.302) (2026-09-11)
14
+
15
+ **Note:** Version bump only for package @toa.io/atomicity
16
+
17
+
18
+
19
+
20
+
6
21
  # [1.0.0-alpha.301](https://github.com/toa-io/toa/compare/v1.0.0-alpha.300...v1.0.0-alpha.301) (2026-09-11)
7
22
 
8
23
  **Note:** Version bump only for package @toa.io/atomicity
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/atomicity",
3
- "version": "1.0.0-alpha.301",
3
+ "version": "1.0.0-alpha.303",
4
4
  "type": "module",
5
5
  "description": "Toa Atomicity Connector",
6
6
  "author": "temich <tema.gurtovoy@gmail.com>",
@@ -18,14 +18,14 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@sesamecare-oss/redlock": "1.4.0",
21
- "@toa.io/core": "1.0.0-alpha.301",
21
+ "@toa.io/core": "1.0.0-alpha.303",
22
22
  "@toa.io/generic": "1.0.0-alpha.301",
23
23
  "ioredis": "6.0.0",
24
24
  "n-and-i": "0.3.4",
25
- "openspan": "1.0.0-alpha.301"
25
+ "openspan": "1.0.0-alpha.303"
26
26
  },
27
27
  "scripts": {
28
28
  "test": "echo \"Error: run tests from root\" && exit 1"
29
29
  },
30
- "gitHead": "cc49bc90efd35464b2ad7803246786c607a44685"
30
+ "gitHead": "b8e9dd89f6d49756944687a95dfc786f12947286"
31
31
  }
package/src/atom.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { console } from 'openspan'
2
2
  import { Connector } from '@toa.io/core'
3
3
  import { environment } from '@toa.io/generic'
4
+ import { holding, waited } from './measurements.js'
4
5
 
5
6
  /**
6
7
  * What one group of replicas decides together, in one place: which of them owns what, what they
@@ -119,7 +120,26 @@ export class Atom extends Connector {
119
120
  if (redlock === undefined)
120
121
  throw new Error('Locking requires atomicity. Set TOA_ATOMICITY_REDIS.')
121
122
 
122
- return redlock.using(this.#keys(LOCK, keys), LEASE, routine)
123
+ const started = performance.now()
124
+ let acquired = false
125
+
126
+ return redlock
127
+ .using(this.#keys(LOCK, keys), LEASE, async (signal) => {
128
+ // the wait is over once the routine is entered, and what follows it is the work
129
+ acquired = true
130
+ waited(this.#name, (performance.now() - started) / 1000)
131
+ holding(this.#name, 1)
132
+
133
+ try {
134
+ return await routine(signal)
135
+ } finally {
136
+ holding(this.#name, -1)
137
+ }
138
+ })
139
+ .finally(() => {
140
+ // a lock never acquired waited for however long it was refused
141
+ if (!acquired) waited(this.#name, (performance.now() - started) / 1000)
142
+ })
123
143
  }
124
144
 
125
145
  /**
@@ -0,0 +1,30 @@
1
+ import { registry } from 'openspan'
2
+
3
+ /**
4
+ * What locking measures.
5
+ *
6
+ * `lock()` waits for as long as it takes to acquire its keys, and that wait is inside the
7
+ * operation that asked for it — so a replica starving on a lock another replica will not release
8
+ * looks exactly like a busy component. The distribution is the signal, because a mean over waits
9
+ * that are usually zero says nothing.
10
+ */
11
+ const meters = registry()
12
+
13
+ /** From a lock taken at once to one waited out for a minute. */
14
+ const WAITS = [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 15, 60]
15
+
16
+ const wait = meters.histogram(
17
+ 'toa.atomicity.lock.wait',
18
+ { buckets: WAITS, unit: 's' },
19
+ { group: null }
20
+ )
21
+
22
+ const held = meters.gauge('toa.atomicity.locks', { group: null })
23
+
24
+ export function waited(group, seconds) {
25
+ wait.record(seconds, { group })
26
+ }
27
+
28
+ export function holding(group, delta) {
29
+ held.add(delta, { group })
30
+ }