tigerbeetle-node 0.6.0 → 0.9.0
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/README.md +102 -83
- package/dist/benchmark.js +102 -100
- package/dist/benchmark.js.map +1 -1
- package/dist/index.d.ts +82 -82
- package/dist/index.js +74 -93
- package/dist/index.js.map +1 -1
- package/dist/test.js +135 -112
- package/dist/test.js.map +1 -1
- package/package.json +13 -14
- package/scripts/download_node_headers.sh +3 -1
- package/src/benchmark.ts +114 -118
- package/src/index.ts +102 -111
- package/src/node.zig +53 -51
- package/src/test.ts +146 -125
- package/src/tigerbeetle/scripts/benchmark.bat +46 -46
- package/src/tigerbeetle/scripts/benchmark.sh +5 -0
- package/src/tigerbeetle/scripts/install_zig.bat +109 -109
- package/src/tigerbeetle/scripts/install_zig.sh +7 -3
- package/src/tigerbeetle/scripts/vopr.bat +47 -47
- package/src/tigerbeetle/src/benchmark.zig +63 -96
- package/src/tigerbeetle/src/config.zig +23 -19
- package/src/tigerbeetle/src/demo.zig +2 -15
- package/src/tigerbeetle/src/demo_01_create_accounts.zig +10 -10
- package/src/tigerbeetle/src/demo_03_create_transfers.zig +5 -3
- package/src/tigerbeetle/src/{demo_04_create_transfers_two_phase_commit.zig → demo_04_create_pending_transfers.zig} +18 -12
- package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +37 -0
- package/src/tigerbeetle/src/demo_06_void_pending_transfers.zig +24 -0
- package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +1 -1
- package/src/tigerbeetle/src/io/linux.zig +4 -4
- package/src/tigerbeetle/src/main.zig +19 -3
- package/src/tigerbeetle/src/message_pool.zig +5 -2
- package/src/tigerbeetle/src/ring_buffer.zig +48 -3
- package/src/tigerbeetle/src/simulator.zig +104 -8
- package/src/tigerbeetle/src/state_machine.zig +1813 -816
- package/src/tigerbeetle/src/test/cluster.zig +165 -32
- package/src/tigerbeetle/src/test/packet_simulator.zig +14 -1
- package/src/tigerbeetle/src/test/state_checker.zig +3 -1
- package/src/tigerbeetle/src/test/state_machine.zig +8 -7
- package/src/tigerbeetle/src/test/storage.zig +99 -40
- package/src/tigerbeetle/src/tigerbeetle.zig +103 -98
- package/src/tigerbeetle/src/vsr/journal.zig +1387 -459
- package/src/tigerbeetle/src/vsr/replica.zig +1204 -417
- package/src/tigerbeetle/src/vsr.zig +203 -49
- package/src/translate.zig +10 -0
- package/.yarn/releases/yarn-berry.cjs +0 -55
- package/.yarnrc.yml +0 -1
- package/scripts/postinstall.sh +0 -6
- package/src/tigerbeetle/src/demo_05_accept_transfers.zig +0 -23
- package/src/tigerbeetle/src/demo_06_reject_transfers.zig +0 -17
- package/src/tigerbeetle/src/format_test.zig +0 -69
- package/yarn.lock +0 -42
package/README.md
CHANGED
|
@@ -2,46 +2,32 @@
|
|
|
2
2
|
[TigerBeetle](https://github.com/coilhq/tigerbeetle) client for Node.js.
|
|
3
3
|
|
|
4
4
|
## Installation
|
|
5
|
-
The following steps will install the `tigerbeetle-node` module to your current working directory.
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
Install the `tigerbeetle-node` module to your current working directory:
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
> Your operating system should be Linux (kernel >= v5.6) or macOS. Windows support is not yet available but is in the works.
|
|
12
|
-
|
|
13
|
-
### YARN Package Manager
|
|
14
|
-
|
|
15
|
-
```sh
|
|
16
|
-
# Run the following from this directory
|
|
17
|
-
yarn add
|
|
18
|
-
```
|
|
19
|
-
or
|
|
20
|
-
|
|
21
|
-
### NPM Package Manager
|
|
22
|
-
```sh
|
|
8
|
+
```shell
|
|
23
9
|
npm install tigerbeetle-node
|
|
24
10
|
```
|
|
25
11
|
|
|
26
|
-
|
|
12
|
+
### Prerequisites
|
|
27
13
|
|
|
28
|
-
|
|
14
|
+
* NodeJS >= `14.0.0`. _(If the correct version is not installed, an installation error will occur)_
|
|
29
15
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
yarn
|
|
33
|
-
```
|
|
16
|
+
> Your operating system should be Linux (kernel >= v5.6) or macOS.
|
|
17
|
+
> Windows support is not yet available.
|
|
34
18
|
|
|
35
19
|
## Usage
|
|
20
|
+
|
|
36
21
|
A client needs to be configured with a `cluster_id` and `replica_addresses`.
|
|
37
22
|
This instantiates the client where memory is allocated to internally buffer events to be sent.
|
|
38
23
|
For the moment, only one client can be instantiated globally per process.
|
|
39
24
|
Future releases will allow multiple client instantiations.
|
|
25
|
+
|
|
40
26
|
```js
|
|
41
27
|
import { createClient } from 'tigerbeetle-node'
|
|
42
28
|
|
|
43
29
|
const client = createClient({
|
|
44
|
-
cluster_id:
|
|
30
|
+
cluster_id: 0,
|
|
45
31
|
replica_addresses: ['3001', '3002', '3003']
|
|
46
32
|
})
|
|
47
33
|
```
|
|
@@ -54,15 +40,15 @@ This is reflected in the below function interfaces where each one takes in an ar
|
|
|
54
40
|
```js
|
|
55
41
|
const account = {
|
|
56
42
|
id: 137n, // u128
|
|
57
|
-
user_data: 0n, // u128, opaque third-party identifier to link this account
|
|
43
|
+
user_data: 0n, // u128, opaque third-party identifier to link this account to an external entity:
|
|
58
44
|
reserved: Buffer.alloc(48, 0), // [48]u8
|
|
59
|
-
|
|
45
|
+
ledger: 1, // u32, ledger value
|
|
60
46
|
code: 718, // u16, a chart of accounts code describing the type of account (e.g. clearing, settlement)
|
|
61
|
-
flags: 0, //
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
47
|
+
flags: 0, // u16
|
|
48
|
+
debits_pending: 0n, // u64
|
|
49
|
+
debits_posted: 0n, // u64
|
|
50
|
+
credits_pending: 0n, // u64
|
|
51
|
+
credits_posted: 0n, // u64
|
|
66
52
|
timestamp: 0n, // u64, Reserved: This will be set by the server.
|
|
67
53
|
}
|
|
68
54
|
|
|
@@ -87,7 +73,7 @@ The `flags` on an account provide a way for you to enforce policies by toggling
|
|
|
87
73
|
|----------|----------------------------------|-----------------------------------------|
|
|
88
74
|
| `linked` | `debits_must_not_exceed_credits` | `credits_must_not_exceed_debits` |
|
|
89
75
|
|
|
90
|
-
The creation of an account can be linked to the successful creation of another by setting the `linked` flag (see [linked events](#linked-events)). By setting `debits_must_not_exceed_credits`, then any transfer such that `
|
|
76
|
+
The creation of an account can be linked to the successful creation of another by setting the `linked` flag (see [linked events](#linked-events)). By setting `debits_must_not_exceed_credits`, then any transfer such that `debits_posted + debits_pending + amount > credits_posted` will fail. Similarly for `credits_must_not_exceed_debits`.
|
|
91
77
|
```js
|
|
92
78
|
enum CreateAccountFlags {
|
|
93
79
|
linked = (1 << 0),
|
|
@@ -99,7 +85,7 @@ The creation of an account can be linked to the successful creation of another b
|
|
|
99
85
|
flags |= CreateAccountFlags.debits_must_not_exceed_credits
|
|
100
86
|
```
|
|
101
87
|
|
|
102
|
-
### Account
|
|
88
|
+
### Account Lookup
|
|
103
89
|
|
|
104
90
|
The `id` of the account is used for lookups. Only matched accounts are returned.
|
|
105
91
|
```js
|
|
@@ -111,80 +97,78 @@ The `id` of the account is used for lookups. Only matched accounts are returned.
|
|
|
111
97
|
* id: 137n,
|
|
112
98
|
* user_data: 0n,
|
|
113
99
|
* reserved: Buffer,
|
|
114
|
-
*
|
|
100
|
+
* ledger: 1,
|
|
115
101
|
* code: 718,
|
|
116
102
|
* flags: 0,
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
103
|
+
* debits_pending: 0n,
|
|
104
|
+
* debits_posted: 0n,
|
|
105
|
+
* credits_pending: 0n,
|
|
106
|
+
* credits_posted: 0n,
|
|
121
107
|
* timestamp: 1623062009212508993n,
|
|
122
108
|
* }]
|
|
123
109
|
*/
|
|
124
110
|
```
|
|
125
111
|
|
|
126
|
-
### Creating a
|
|
112
|
+
### Creating a Transfer
|
|
127
113
|
|
|
128
114
|
This creates a journal entry between two accounts.
|
|
129
115
|
```js
|
|
130
116
|
const transfer = {
|
|
131
117
|
id: 1n, // u128
|
|
118
|
+
// Double-entry accounting:
|
|
132
119
|
debit_account_id: 1n, // u128
|
|
133
120
|
credit_account_id: 2n, // u128
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
121
|
+
// Opaque third-party identifier to link this transfer to an external entity:
|
|
122
|
+
user_data: 0n, // u128
|
|
123
|
+
reserved: 0n, // u128
|
|
124
|
+
// Timeout applicable for a pending/2-phase transfer:
|
|
125
|
+
timeout: 0n, // u64, in nano-seconds.
|
|
126
|
+
// Collection of accounts usually grouped by the currency:
|
|
127
|
+
// You can't transfer money between accounts with different ledgers:
|
|
128
|
+
ledger: 720, // u32, ledger for transfer (e.g. currency).
|
|
129
|
+
// Chart of accounts code describing the reason for the transfer:
|
|
130
|
+
code: 1, // u16, (e.g. deposit, settlement)
|
|
131
|
+
flags: 0, // u16
|
|
139
132
|
amount: 10n, // u64
|
|
140
133
|
timestamp: 0n, //u64, Reserved: This will be set by the server.
|
|
141
134
|
}
|
|
142
|
-
|
|
143
135
|
const errors = await client.createTransfers([transfer])
|
|
144
136
|
```
|
|
145
|
-
Two-phase transfers are supported natively by toggling the appropriate flag. TigerBeetle will then adjust the `
|
|
146
|
-
| bit 0 | bit 1 | bit 2 |
|
|
147
|
-
|----------|--------------------|------------------|
|
|
148
|
-
| `linked` | `posting` | `condition` |
|
|
137
|
+
Two-phase transfers are supported natively by toggling the appropriate flag. TigerBeetle will then adjust the `credits_pending` and `debits_pending` fields of the appropriate accounts. A corresponding commit transfer then needs to be sent to accept or reject the transfer.
|
|
149
138
|
|
|
150
|
-
|
|
139
|
+
Transfers within a batch may also be linked (see [linked events](#linked-events)).
|
|
151
140
|
```js
|
|
152
|
-
enum
|
|
153
|
-
linked = (1 << 0
|
|
154
|
-
|
|
155
|
-
|
|
141
|
+
enum TransferFlags {
|
|
142
|
+
linked = (1 << 0),
|
|
143
|
+
pending = (1 << 1),
|
|
144
|
+
post_pending_transfer = (1 << 2),
|
|
145
|
+
void_pending_transfer = (1 << 3)
|
|
156
146
|
}
|
|
157
|
-
|
|
158
|
-
//
|
|
147
|
+
|
|
148
|
+
// Two-phase transfer (pending):
|
|
159
149
|
let flags = 0n
|
|
160
|
-
flags |= TransferFlags.
|
|
150
|
+
flags |= TransferFlags.pending
|
|
161
151
|
|
|
162
|
-
// two-phase transfer
|
|
152
|
+
// Linked two-phase transfer (pending):
|
|
163
153
|
let flags = 0n
|
|
164
|
-
flags |= TransferFlags.
|
|
165
|
-
flags |= TransferFlags.
|
|
154
|
+
flags |= TransferFlags.linked
|
|
155
|
+
flags |= TransferFlags.pending
|
|
166
156
|
```
|
|
167
157
|
|
|
168
|
-
###
|
|
169
|
-
|
|
170
|
-
This is used to commit a two-phase transfer.
|
|
171
|
-
| bit 0 | bit 1 | bit 2 |
|
|
172
|
-
|----------|----------|------------|
|
|
173
|
-
| `linked` | `reject` | `preimage` |
|
|
158
|
+
### Post a Pending Transfer (2-Phase)
|
|
174
159
|
|
|
175
|
-
|
|
160
|
+
With `flags = post_pending_transfer`, TigerBeetle will accept the transfer. TigerBeetle will atomically rollback the changes to `debits_pending` and `credits_pending` of the appropriate accounts and apply them to the `debits_posted` and `credits_posted` balances.
|
|
176
161
|
```js
|
|
177
|
-
const
|
|
178
|
-
id:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
flags: 0, // u32
|
|
162
|
+
const post = {
|
|
163
|
+
id: 2n, // u128, must correspond to the transfer id
|
|
164
|
+
pending_id: 1n, // u128, id of the pending transfer
|
|
165
|
+
flags: TransferFlags.post_pending_transfer, // to void, use [void_pending_transfer]
|
|
182
166
|
timestamp: 0n, // u64, Reserved: This will be set by the server.
|
|
183
167
|
}
|
|
184
|
-
const errors = await client.
|
|
168
|
+
const errors = await client.createTransfers([post])
|
|
185
169
|
```
|
|
186
170
|
|
|
187
|
-
### Linked
|
|
171
|
+
### Linked Events
|
|
188
172
|
|
|
189
173
|
When the `linked` flag is specified for the `createAccount`, `createTransfer`, `commitTransfer` event, it links an event with the next event in the batch, to create a chain of events, of arbitrary length, which all succeed or fail together. The tail of a chain is denoted by the first event without this flag. The last event in a batch may therefore never have the `linked` flag set as this would leave a chain open-ended. Multiple chains or individual events may coexist within a batch to succeed or fail independently. Events within a chain are executed within order, or are rolled back on error, so that the effect of each event in the chain is visible to the next, and so that the chain is either visible or invisible as a unit to subsequent events after the chain. The event that was the first to break the chain will have a unique error result. Other events in the chain will have their error result set to `linked_event_failed`.
|
|
190
174
|
|
|
@@ -193,10 +177,10 @@ let batch = []
|
|
|
193
177
|
let linkedFlag = 0
|
|
194
178
|
linkedFlag |= CreateTransferFlags.linked
|
|
195
179
|
|
|
196
|
-
// An individual transfer (successful)
|
|
180
|
+
// An individual transfer (successful):
|
|
197
181
|
batch.push({ id: 1n, ... })
|
|
198
182
|
|
|
199
|
-
// A chain of 4 transfers (the last transfer in the chain closes the chain with linked=false)
|
|
183
|
+
// A chain of 4 transfers (the last transfer in the chain closes the chain with linked=false):
|
|
200
184
|
batch.push({ id: 2n, ..., flags: linkedFlag }) // Commit/rollback.
|
|
201
185
|
batch.push({ id: 3n, ..., flags: linkedFlag }) // Commit/rollback.
|
|
202
186
|
batch.push({ id: 2n, ..., flags: linkedFlag }) // Fail with exists
|
|
@@ -206,11 +190,11 @@ batch.push({ id: 4n, ..., flags: 0 }) // Fail without committing.
|
|
|
206
190
|
// This should not see any effect from the failed chain above.
|
|
207
191
|
batch.push({ id: 2n, ..., flags: 0 })
|
|
208
192
|
|
|
209
|
-
// A chain of 2 transfers (the first transfer fails the chain)
|
|
193
|
+
// A chain of 2 transfers (the first transfer fails the chain):
|
|
210
194
|
batch.push({ id: 2n, ..., flags: linkedFlag })
|
|
211
195
|
batch.push({ id: 3n, ..., flags: 0 })
|
|
212
196
|
|
|
213
|
-
// A chain of 2 transfers (successful)
|
|
197
|
+
// A chain of 2 transfers (successful):
|
|
214
198
|
batch.push({ id: 3n, ..., flags: linkedFlag })
|
|
215
199
|
batch.push({ id: 4n, ..., flags: 0 })
|
|
216
200
|
|
|
@@ -218,13 +202,48 @@ const errors = await client.createTransfers(batch)
|
|
|
218
202
|
|
|
219
203
|
/**
|
|
220
204
|
* [
|
|
221
|
-
* { index: 1, error: 1 },
|
|
222
|
-
* { index: 2, error: 1 },
|
|
223
|
-
* { index: 3, error:
|
|
224
|
-
* { index: 4, error: 1 },
|
|
205
|
+
* { index: 1, error: 1 }, // linked_event_failed
|
|
206
|
+
* { index: 2, error: 1 }, // linked_event_failed
|
|
207
|
+
* { index: 3, error: 25 }, // exists
|
|
208
|
+
* { index: 4, error: 1 }, // linked_event_failed
|
|
225
209
|
*
|
|
226
|
-
* { index: 6, error:
|
|
227
|
-
* { index: 7, error: 1 },
|
|
210
|
+
* { index: 6, error: 17 }, // exists_with_different_flags
|
|
211
|
+
* { index: 7, error: 1 }, // linked_event_failed
|
|
228
212
|
* ]
|
|
229
213
|
*/
|
|
230
214
|
```
|
|
215
|
+
|
|
216
|
+
### Development
|
|
217
|
+
|
|
218
|
+
To get up and running when cloning the repo:
|
|
219
|
+
|
|
220
|
+
```shell
|
|
221
|
+
git clone --recurse-submodules https://github.com/coilhq/tigerbeetle-node.git
|
|
222
|
+
cd tigerbeetle-node/
|
|
223
|
+
npm install --include dev # This will automatically install and build everything you need.
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### Rebuild
|
|
227
|
+
|
|
228
|
+
To rebuild the TypeScript distribution, and to rebuild the native Node library, again after changes:
|
|
229
|
+
|
|
230
|
+
```shell
|
|
231
|
+
npm run build
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
*If you ever run `npm run clean` then you will need to `npm install --include dev` to reinstall
|
|
235
|
+
TypeScript within `node_modules`, as TypeScript is required by `npm run prepack` when publishing.*
|
|
236
|
+
|
|
237
|
+
#### Benchmark
|
|
238
|
+
|
|
239
|
+
```shell
|
|
240
|
+
npm run benchmark
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
#### Test
|
|
244
|
+
|
|
245
|
+
```shell
|
|
246
|
+
./tigerbeetle init --cluster=1 --replica=0 --directory=.
|
|
247
|
+
./tigerbeetle start --cluster=1 --replica=0 --directory=. --addresses=3001
|
|
248
|
+
npm run test
|
|
249
|
+
```
|
package/dist/benchmark.js
CHANGED
|
@@ -5,46 +5,44 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const assert_1 = __importDefault(require("assert"));
|
|
7
7
|
const _1 = require(".");
|
|
8
|
-
const MAX_TRANSFERS =
|
|
9
|
-
const MAX_REQUEST_BATCH_SIZE =
|
|
10
|
-
const
|
|
8
|
+
const MAX_TRANSFERS = 51200;
|
|
9
|
+
const MAX_REQUEST_BATCH_SIZE = 5120;
|
|
10
|
+
const IS_TWO_PHASE_TRANSFER = false;
|
|
11
11
|
const IS_RAW_REQUEST = false;
|
|
12
|
-
const PREVIOUS_RAW_REQUEST_RESULT =
|
|
13
|
-
const PREVIOUS_RESULT =
|
|
12
|
+
const PREVIOUS_RAW_REQUEST_RESULT = IS_TWO_PHASE_TRANSFER ? 300000 : 620000;
|
|
13
|
+
const PREVIOUS_RESULT = IS_TWO_PHASE_TRANSFER ? 150000 : 310000;
|
|
14
14
|
const PREVIOUS_BENCHMARK = IS_RAW_REQUEST ? PREVIOUS_RAW_REQUEST_RESULT : PREVIOUS_RESULT;
|
|
15
15
|
const TOLERANCE = 10;
|
|
16
|
-
const client = _1.createClient({
|
|
17
|
-
cluster_id:
|
|
16
|
+
const client = (0, _1.createClient)({
|
|
17
|
+
cluster_id: 0,
|
|
18
18
|
replica_addresses: ['3001']
|
|
19
19
|
});
|
|
20
20
|
const TRANSFER_SIZE = 128;
|
|
21
|
-
const COMMIT_SIZE = 64;
|
|
22
21
|
const Zeroed48Bytes = Buffer.alloc(48, 0);
|
|
23
|
-
const Zeroed32Bytes = Buffer.alloc(32, 0);
|
|
24
22
|
const accountA = {
|
|
25
23
|
id: 137n,
|
|
26
24
|
user_data: 0n,
|
|
27
25
|
reserved: Zeroed48Bytes,
|
|
28
|
-
|
|
26
|
+
ledger: 1,
|
|
27
|
+
code: 1,
|
|
29
28
|
flags: 0,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
credits_reserved: 0n,
|
|
29
|
+
debits_pending: 0n,
|
|
30
|
+
debits_posted: 0n,
|
|
31
|
+
credits_pending: 0n,
|
|
32
|
+
credits_posted: 0n,
|
|
35
33
|
timestamp: 0n,
|
|
36
34
|
};
|
|
37
35
|
const accountB = {
|
|
38
36
|
id: 138n,
|
|
39
37
|
user_data: 0n,
|
|
40
38
|
reserved: Zeroed48Bytes,
|
|
41
|
-
|
|
39
|
+
ledger: 1,
|
|
40
|
+
code: 1,
|
|
42
41
|
flags: 0,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
credits_reserved: 0n,
|
|
42
|
+
debits_pending: 0n,
|
|
43
|
+
debits_posted: 0n,
|
|
44
|
+
credits_pending: 0n,
|
|
45
|
+
credits_posted: 0n,
|
|
48
46
|
timestamp: 0n,
|
|
49
47
|
};
|
|
50
48
|
const rawCreateTransfers = async (batch) => {
|
|
@@ -63,46 +61,30 @@ const rawCreateTransfers = async (batch) => {
|
|
|
63
61
|
}
|
|
64
62
|
});
|
|
65
63
|
};
|
|
66
|
-
const rawCommitTransfers = async (batch) => {
|
|
67
|
-
return new Promise((resolve, reject) => {
|
|
68
|
-
const callback = (error, results) => {
|
|
69
|
-
if (error) {
|
|
70
|
-
reject(error);
|
|
71
|
-
}
|
|
72
|
-
resolve(results);
|
|
73
|
-
};
|
|
74
|
-
try {
|
|
75
|
-
client.rawRequest(_1.Operation.COMMIT_TRANSFER, batch, callback);
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
reject(error);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
};
|
|
82
64
|
const encodeTransfer = (transfer, offset, output) => {
|
|
83
|
-
assert_1.default(offset + TRANSFER_SIZE <= output.length);
|
|
65
|
+
(0, assert_1.default)(BigInt((offset + TRANSFER_SIZE)) <= BigInt(output.length), `Transfer ${transfer} exceeds buffer of ${output}!`);
|
|
84
66
|
output.writeBigUInt64LE(transfer.id, offset);
|
|
85
67
|
output.writeBigUInt64LE(transfer.debit_account_id, offset + 16);
|
|
86
68
|
output.writeBigUInt64LE(transfer.credit_account_id, offset + 32);
|
|
69
|
+
output.writeBigUInt64LE(transfer.user_data, offset + 48);
|
|
70
|
+
output.writeBigUInt64LE(transfer.reserved, offset + 64);
|
|
71
|
+
output.writeBigUInt64LE(transfer.pending_id, offset + 80);
|
|
87
72
|
output.writeBigUInt64LE(transfer.timeout, offset + 96);
|
|
88
|
-
output.writeUInt32LE(transfer.
|
|
89
|
-
output.writeUInt32LE(transfer.
|
|
73
|
+
output.writeUInt32LE(transfer.ledger, offset + 104);
|
|
74
|
+
output.writeUInt32LE(transfer.code, offset + 108);
|
|
75
|
+
output.writeUInt32LE(transfer.flags, offset + 110);
|
|
90
76
|
output.writeBigUInt64LE(transfer.amount, offset + 112);
|
|
91
77
|
output.writeBigUInt64LE(transfer.timestamp, offset + 120);
|
|
92
78
|
};
|
|
93
|
-
const
|
|
94
|
-
assert_1.default(
|
|
95
|
-
|
|
96
|
-
};
|
|
97
|
-
const runBenchmarkRawReqeust = async () => {
|
|
98
|
-
assert_1.default(MAX_TRANSFERS % MAX_REQUEST_BATCH_SIZE === 0, "The raw request benchmark requires MAX_TRANSFERS to be a multiple of MAX_REQUEST_BATCH_SIZE");
|
|
99
|
-
console.log(`pre-allocating ${MAX_TRANSFERS} transfers and commits...`);
|
|
79
|
+
const runBenchmarkRawRequest = async () => {
|
|
80
|
+
(0, assert_1.default)(MAX_TRANSFERS % MAX_REQUEST_BATCH_SIZE === 0, "The raw request benchmark requires MAX_TRANSFERS to be a multiple of MAX_REQUEST_BATCH_SIZE");
|
|
81
|
+
console.log(`pre-allocating ${MAX_TRANSFERS} transfers and posts...`);
|
|
100
82
|
const transfers = [];
|
|
101
|
-
const
|
|
83
|
+
const posts = [];
|
|
102
84
|
let count = 0;
|
|
103
85
|
while (count < MAX_TRANSFERS) {
|
|
104
86
|
const transferBatch = Buffer.alloc(MAX_REQUEST_BATCH_SIZE * TRANSFER_SIZE, 0);
|
|
105
|
-
const
|
|
87
|
+
const postTransferBatch = Buffer.alloc(MAX_REQUEST_BATCH_SIZE * TRANSFER_SIZE, 0);
|
|
106
88
|
for (let i = 0; i < MAX_REQUEST_BATCH_SIZE; i++) {
|
|
107
89
|
if (count === MAX_TRANSFERS)
|
|
108
90
|
break;
|
|
@@ -111,45 +93,56 @@ const runBenchmarkRawReqeust = async () => {
|
|
|
111
93
|
id: BigInt(count),
|
|
112
94
|
debit_account_id: accountA.id,
|
|
113
95
|
credit_account_id: accountB.id,
|
|
114
|
-
code: 0,
|
|
115
|
-
reserved: Zeroed32Bytes,
|
|
116
96
|
user_data: 0n,
|
|
117
|
-
|
|
97
|
+
reserved: 0n,
|
|
98
|
+
pending_id: 0n,
|
|
99
|
+
timeout: IS_TWO_PHASE_TRANSFER ? BigInt(2e9) : 0n,
|
|
100
|
+
ledger: 1,
|
|
101
|
+
code: 1,
|
|
102
|
+
flags: IS_TWO_PHASE_TRANSFER ? _1.TransferFlags.pending : 0,
|
|
118
103
|
amount: 1n,
|
|
119
|
-
timeout: IS_TWO_PHASE_COMMIT ? BigInt(2e9) : 0n,
|
|
120
104
|
timestamp: 0n,
|
|
121
105
|
}, i * TRANSFER_SIZE, transferBatch);
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
id: BigInt(count),
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
106
|
+
if (IS_TWO_PHASE_TRANSFER) {
|
|
107
|
+
encodeTransfer({
|
|
108
|
+
id: BigInt((MAX_TRANSFERS + count)),
|
|
109
|
+
debit_account_id: accountA.id,
|
|
110
|
+
credit_account_id: accountB.id,
|
|
111
|
+
user_data: 0n,
|
|
112
|
+
reserved: 0n,
|
|
113
|
+
pending_id: BigInt(count),
|
|
114
|
+
timeout: 0n,
|
|
115
|
+
ledger: 1,
|
|
116
|
+
code: 1,
|
|
117
|
+
flags: _1.TransferFlags.post_pending_transfer,
|
|
118
|
+
amount: 1n,
|
|
128
119
|
timestamp: 0n,
|
|
129
|
-
}, i *
|
|
120
|
+
}, i * TRANSFER_SIZE, postTransferBatch);
|
|
130
121
|
}
|
|
131
122
|
}
|
|
132
123
|
transfers.push(transferBatch);
|
|
133
|
-
if (
|
|
134
|
-
|
|
124
|
+
if (IS_TWO_PHASE_TRANSFER)
|
|
125
|
+
posts.push(postTransferBatch);
|
|
126
|
+
if (count % 100)
|
|
127
|
+
console.log(`${Number((count / MAX_TRANSFERS) * 100).toFixed(1)}%`);
|
|
135
128
|
}
|
|
136
|
-
assert_1.default(count === MAX_TRANSFERS);
|
|
129
|
+
(0, assert_1.default)(count === MAX_TRANSFERS);
|
|
137
130
|
console.log(`starting benchmark. MAX_TRANSFERS=${MAX_TRANSFERS} REQUEST_BATCH_SIZE=${MAX_REQUEST_BATCH_SIZE} NUMBER_OF_BATCHES=${transfers.length}`);
|
|
138
131
|
let maxCreateTransfersLatency = 0;
|
|
139
132
|
let maxCommitTransfersLatency = 0;
|
|
140
133
|
const start = Date.now();
|
|
141
134
|
for (let i = 0; i < transfers.length; i++) {
|
|
142
135
|
const ms1 = Date.now();
|
|
143
|
-
const
|
|
144
|
-
assert_1.default(
|
|
136
|
+
const transferErrors = await rawCreateTransfers(transfers[i]);
|
|
137
|
+
(0, assert_1.default)(transferErrors.length === 0);
|
|
145
138
|
const ms2 = Date.now();
|
|
146
139
|
const createTransferLatency = ms2 - ms1;
|
|
147
140
|
if (createTransferLatency > maxCreateTransfersLatency) {
|
|
148
141
|
maxCreateTransfersLatency = createTransferLatency;
|
|
149
142
|
}
|
|
150
|
-
if (
|
|
151
|
-
const
|
|
152
|
-
assert_1.default(
|
|
143
|
+
if (IS_TWO_PHASE_TRANSFER) {
|
|
144
|
+
const commitErrors = await rawCreateTransfers(posts[i]);
|
|
145
|
+
(0, assert_1.default)(commitErrors.length === 0);
|
|
153
146
|
const ms3 = Date.now();
|
|
154
147
|
const commitTransferLatency = ms3 - ms2;
|
|
155
148
|
if (commitTransferLatency > maxCommitTransfersLatency) {
|
|
@@ -165,60 +158,69 @@ const runBenchmarkRawReqeust = async () => {
|
|
|
165
158
|
};
|
|
166
159
|
};
|
|
167
160
|
const runBenchmark = async () => {
|
|
168
|
-
console.log(`pre-allocating ${MAX_TRANSFERS} transfers and
|
|
161
|
+
console.log(`pre-allocating ${MAX_TRANSFERS} transfers and posts...`);
|
|
169
162
|
const transfers = [];
|
|
170
|
-
const
|
|
163
|
+
const posts = [];
|
|
171
164
|
let count = 0;
|
|
172
165
|
while (count < MAX_TRANSFERS) {
|
|
173
|
-
const
|
|
174
|
-
const
|
|
166
|
+
const pendingBatch = [];
|
|
167
|
+
const postBatch = [];
|
|
175
168
|
for (let i = 0; i < MAX_REQUEST_BATCH_SIZE; i++) {
|
|
176
169
|
if (count === MAX_TRANSFERS)
|
|
177
170
|
break;
|
|
178
171
|
count += 1;
|
|
179
|
-
|
|
172
|
+
pendingBatch.push({
|
|
180
173
|
id: BigInt(count),
|
|
181
174
|
debit_account_id: accountA.id,
|
|
182
175
|
credit_account_id: accountB.id,
|
|
183
|
-
|
|
184
|
-
|
|
176
|
+
pending_id: 0n,
|
|
177
|
+
code: 1,
|
|
178
|
+
ledger: 1,
|
|
179
|
+
reserved: 0n,
|
|
185
180
|
user_data: 0n,
|
|
186
|
-
flags:
|
|
181
|
+
flags: IS_TWO_PHASE_TRANSFER ? _1.TransferFlags.pending : 0,
|
|
187
182
|
amount: 1n,
|
|
188
|
-
timeout:
|
|
183
|
+
timeout: IS_TWO_PHASE_TRANSFER ? BigInt(2e9) : 0n,
|
|
189
184
|
timestamp: 0n,
|
|
190
185
|
});
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
id: BigInt(count),
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
186
|
+
if (IS_TWO_PHASE_TRANSFER) {
|
|
187
|
+
postBatch.push({
|
|
188
|
+
id: BigInt(MAX_TRANSFERS + count),
|
|
189
|
+
debit_account_id: accountA.id,
|
|
190
|
+
credit_account_id: accountB.id,
|
|
191
|
+
pending_id: BigInt(count),
|
|
192
|
+
code: 1,
|
|
193
|
+
ledger: 1,
|
|
194
|
+
reserved: 0n,
|
|
195
|
+
user_data: 0n,
|
|
196
|
+
flags: IS_TWO_PHASE_TRANSFER ? _1.TransferFlags.post_pending_transfer : 0,
|
|
197
|
+
amount: 1n,
|
|
198
|
+
timeout: 0n,
|
|
197
199
|
timestamp: 0n,
|
|
198
200
|
});
|
|
199
201
|
}
|
|
200
202
|
}
|
|
201
|
-
transfers.push(
|
|
202
|
-
if (
|
|
203
|
-
|
|
203
|
+
transfers.push(pendingBatch);
|
|
204
|
+
if (IS_TWO_PHASE_TRANSFER)
|
|
205
|
+
posts.push(postBatch);
|
|
204
206
|
}
|
|
205
|
-
assert_1.default(count === MAX_TRANSFERS);
|
|
207
|
+
(0, assert_1.default)(count === MAX_TRANSFERS);
|
|
206
208
|
console.log(`starting benchmark. MAX_TRANSFERS=${MAX_TRANSFERS} REQUEST_BATCH_SIZE=${MAX_REQUEST_BATCH_SIZE} NUMBER_OF_BATCHES=${transfers.length}`);
|
|
207
209
|
let maxCreateTransfersLatency = 0;
|
|
208
210
|
let maxCommitTransfersLatency = 0;
|
|
209
211
|
const start = Date.now();
|
|
210
212
|
for (let i = 0; i < transfers.length; i++) {
|
|
211
213
|
const ms1 = Date.now();
|
|
212
|
-
const
|
|
213
|
-
assert_1.default(
|
|
214
|
+
const transferErrors = await client.createTransfers(transfers[i]);
|
|
215
|
+
(0, assert_1.default)(transferErrors.length === 0);
|
|
214
216
|
const ms2 = Date.now();
|
|
215
217
|
const createTransferLatency = ms2 - ms1;
|
|
216
218
|
if (createTransferLatency > maxCreateTransfersLatency) {
|
|
217
219
|
maxCreateTransfersLatency = createTransferLatency;
|
|
218
220
|
}
|
|
219
|
-
if (
|
|
220
|
-
const
|
|
221
|
-
assert_1.default(
|
|
221
|
+
if (IS_TWO_PHASE_TRANSFER) {
|
|
222
|
+
const commitErrors = await client.createTransfers(posts[i]);
|
|
223
|
+
(0, assert_1.default)(commitErrors.length === 0);
|
|
222
224
|
const ms3 = Date.now();
|
|
223
225
|
const commitTransferLatency = ms3 - ms2;
|
|
224
226
|
if (commitTransferLatency > maxCommitTransfersLatency) {
|
|
@@ -237,19 +239,19 @@ const main = async () => {
|
|
|
237
239
|
console.log("creating the accounts...");
|
|
238
240
|
await client.createAccounts([accountA, accountB]);
|
|
239
241
|
const accountResults = await client.lookupAccounts([accountA.id, accountB.id]);
|
|
240
|
-
assert_1.default(accountResults.length === 2);
|
|
241
|
-
assert_1.default(accountResults[0].
|
|
242
|
-
assert_1.default(accountResults[1].
|
|
243
|
-
const benchmark = IS_RAW_REQUEST ? await
|
|
242
|
+
(0, assert_1.default)(accountResults.length === 2);
|
|
243
|
+
(0, assert_1.default)(accountResults[0].debits_posted === 0n);
|
|
244
|
+
(0, assert_1.default)(accountResults[1].debits_posted === 0n);
|
|
245
|
+
const benchmark = IS_RAW_REQUEST ? await runBenchmarkRawRequest() : await runBenchmark();
|
|
244
246
|
const accounts = await client.lookupAccounts([accountA.id, accountB.id]);
|
|
245
247
|
const result = Math.floor((1000 * MAX_TRANSFERS) / benchmark.ms);
|
|
246
248
|
console.log("=============================");
|
|
247
|
-
console.log(`${
|
|
249
|
+
console.log(`${IS_TWO_PHASE_TRANSFER ? 'two-phase ' : ''}transfers per second: ${result}`);
|
|
248
250
|
console.log(`create transfers max p100 latency per 10 000 transfers = ${benchmark.maxCreateTransfersLatency}ms`);
|
|
249
251
|
console.log(`commit transfers max p100 latency per 10 000 transfers = ${benchmark.maxCommitTransfersLatency}ms`);
|
|
250
|
-
assert_1.default(accounts.length === 2);
|
|
251
|
-
assert_1.default(accounts[0].
|
|
252
|
-
assert_1.default(accounts[1].
|
|
252
|
+
(0, assert_1.default)(accounts.length === 2);
|
|
253
|
+
(0, assert_1.default)(accounts[0].debits_posted === BigInt(MAX_TRANSFERS));
|
|
254
|
+
(0, assert_1.default)(accounts[1].credits_posted === BigInt(MAX_TRANSFERS));
|
|
253
255
|
if (result < PREVIOUS_BENCHMARK * (100 - TOLERANCE) / 100) {
|
|
254
256
|
console.warn(`There has been a performance regression. Previous benchmark=${PREVIOUS_BENCHMARK}`);
|
|
255
257
|
}
|