tigerbeetle-node 0.10.0 → 0.11.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 +302 -101
- package/dist/index.d.ts +70 -72
- package/dist/index.js +70 -72
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/scripts/download_node_headers.sh +14 -7
- package/src/index.ts +6 -10
- package/src/node.zig +6 -3
- package/src/tigerbeetle/scripts/benchmark.sh +4 -4
- package/src/tigerbeetle/scripts/confirm_image.sh +44 -0
- package/src/tigerbeetle/scripts/fuzz_loop.sh +15 -0
- package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +7 -0
- package/src/tigerbeetle/scripts/install.sh +19 -4
- package/src/tigerbeetle/scripts/install_zig.bat +5 -1
- package/src/tigerbeetle/scripts/install_zig.sh +24 -14
- package/src/tigerbeetle/scripts/pre-commit.sh +9 -0
- package/src/tigerbeetle/scripts/shellcheck.sh +5 -0
- package/src/tigerbeetle/scripts/tests_on_alpine.sh +10 -0
- package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +14 -0
- package/src/tigerbeetle/src/benchmark.zig +4 -2
- package/src/tigerbeetle/src/benchmark_array_search.zig +3 -3
- package/src/tigerbeetle/src/c/tb_client/thread.zig +8 -9
- package/src/tigerbeetle/src/c/tb_client.h +100 -80
- package/src/tigerbeetle/src/c/tb_client.zig +4 -1
- package/src/tigerbeetle/src/cli.zig +1 -1
- package/src/tigerbeetle/src/config.zig +48 -16
- package/src/tigerbeetle/src/demo.zig +3 -1
- package/src/tigerbeetle/src/eytzinger_benchmark.zig +3 -3
- package/src/tigerbeetle/src/io/linux.zig +1 -1
- package/src/tigerbeetle/src/lsm/README.md +214 -0
- package/src/tigerbeetle/src/lsm/binary_search.zig +137 -10
- package/src/tigerbeetle/src/lsm/bloom_filter.zig +43 -0
- package/src/tigerbeetle/src/lsm/compaction.zig +352 -398
- package/src/tigerbeetle/src/lsm/composite_key.zig +2 -0
- package/src/tigerbeetle/src/lsm/eytzinger.zig +1 -1
- package/src/tigerbeetle/src/lsm/forest.zig +21 -447
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +412 -0
- package/src/tigerbeetle/src/lsm/grid.zig +145 -69
- package/src/tigerbeetle/src/lsm/groove.zig +196 -133
- package/src/tigerbeetle/src/lsm/k_way_merge.zig +40 -18
- package/src/tigerbeetle/src/lsm/level_iterator.zig +28 -9
- package/src/tigerbeetle/src/lsm/manifest.zig +81 -181
- package/src/tigerbeetle/src/lsm/manifest_level.zig +210 -454
- package/src/tigerbeetle/src/lsm/manifest_log.zig +77 -28
- package/src/tigerbeetle/src/lsm/posted_groove.zig +64 -76
- package/src/tigerbeetle/src/lsm/segmented_array.zig +561 -241
- package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +148 -0
- package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +9 -0
- package/src/tigerbeetle/src/lsm/set_associative_cache.zig +62 -12
- package/src/tigerbeetle/src/lsm/table.zig +83 -48
- package/src/tigerbeetle/src/lsm/table_immutable.zig +30 -23
- package/src/tigerbeetle/src/lsm/table_iterator.zig +25 -14
- package/src/tigerbeetle/src/lsm/table_mutable.zig +63 -12
- package/src/tigerbeetle/src/lsm/test.zig +49 -55
- package/src/tigerbeetle/src/lsm/tree.zig +407 -402
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +457 -0
- package/src/tigerbeetle/src/main.zig +28 -6
- package/src/tigerbeetle/src/message_bus.zig +2 -2
- package/src/tigerbeetle/src/message_pool.zig +14 -17
- package/src/tigerbeetle/src/simulator.zig +145 -112
- package/src/tigerbeetle/src/state_machine.zig +338 -228
- package/src/tigerbeetle/src/static_allocator.zig +65 -0
- package/src/tigerbeetle/src/storage.zig +3 -7
- package/src/tigerbeetle/src/test/accounting/auditor.zig +577 -0
- package/src/tigerbeetle/src/test/accounting/workload.zig +819 -0
- package/src/tigerbeetle/src/test/cluster.zig +18 -48
- package/src/tigerbeetle/src/test/conductor.zig +365 -0
- package/src/tigerbeetle/src/test/fuzz.zig +121 -0
- package/src/tigerbeetle/src/test/id.zig +89 -0
- package/src/tigerbeetle/src/test/priority_queue.zig +645 -0
- package/src/tigerbeetle/src/test/state_checker.zig +93 -69
- package/src/tigerbeetle/src/test/state_machine.zig +11 -35
- package/src/tigerbeetle/src/test/storage.zig +29 -8
- package/src/tigerbeetle/src/tigerbeetle.zig +14 -16
- package/src/tigerbeetle/src/unit_tests.zig +7 -0
- package/src/tigerbeetle/src/vopr.zig +494 -0
- package/src/tigerbeetle/src/vopr_hub/README.md +58 -0
- package/src/tigerbeetle/src/vopr_hub/SETUP.md +199 -0
- package/src/tigerbeetle/src/vopr_hub/go.mod +3 -0
- package/src/tigerbeetle/src/vopr_hub/main.go +1022 -0
- package/src/tigerbeetle/src/vopr_hub/scheduler/go.mod +3 -0
- package/src/tigerbeetle/src/vopr_hub/scheduler/main.go +403 -0
- package/src/tigerbeetle/src/vsr/client.zig +13 -0
- package/src/tigerbeetle/src/vsr/journal.zig +16 -13
- package/src/tigerbeetle/src/vsr/replica.zig +924 -491
- package/src/tigerbeetle/src/vsr/superblock.zig +55 -37
- package/src/tigerbeetle/src/vsr/superblock_client_table.zig +7 -10
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +2 -2
- package/src/tigerbeetle/src/vsr/superblock_manifest.zig +18 -3
- package/src/tigerbeetle/src/vsr.zig +75 -55
- package/src/tigerbeetle/scripts/vopr.bat +0 -48
- package/src/tigerbeetle/scripts/vopr.sh +0 -33
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The scheduler checks out the correct commit on each VOPR to test the latest code on main and
|
|
3
|
+
on pull requests with the `vopr` label.
|
|
4
|
+
Note: The scheduler must run in a directory which is separate from the VOPRs it manages to
|
|
5
|
+
prevent it from checking out a commit that could change the organizer itself.
|
|
6
|
+
To run the scheduler, four environmental variables are required:
|
|
7
|
+
1. TIGERBEETLE_DIRECTORY the location of the VOPR's tigerbeetle directory
|
|
8
|
+
2. REPOSITORY_URL to access TigerBeetle's GitHub repository
|
|
9
|
+
3. DEVELOPER_TOKEN required for making GitHub API calls
|
|
10
|
+
4. NUM_VOPRS specifies the number of VOPRs the system is running
|
|
11
|
+
5. CURRENT_VOPR specifies which VOPR is currently starting up and requiring updated Git commit info
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
package main
|
|
15
|
+
|
|
16
|
+
import (
|
|
17
|
+
"encoding/json"
|
|
18
|
+
"flag"
|
|
19
|
+
"fmt"
|
|
20
|
+
"io"
|
|
21
|
+
"net/http"
|
|
22
|
+
"os"
|
|
23
|
+
"os/exec"
|
|
24
|
+
"regexp"
|
|
25
|
+
"strconv"
|
|
26
|
+
"strings"
|
|
27
|
+
"time"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
const usageFmt = `usage: %s [flags]
|
|
31
|
+
|
|
32
|
+
environment:
|
|
33
|
+
TIGERBEETLE_DIRECTORY the location of the VOPR's tigerbeetle directory
|
|
34
|
+
REPOSITORY_URL TigerBeetle's GitHub repository URL
|
|
35
|
+
DEVELOPER_TOKEN required for making GitHub API calls
|
|
36
|
+
NUM_VOPRS the number of VOPRs the system is running
|
|
37
|
+
CURRENT_VOPR the number of the VOPR which is currently starting up (counting from 0)
|
|
38
|
+
|
|
39
|
+
flags:
|
|
40
|
+
-debug enabled debug logging
|
|
41
|
+
`
|
|
42
|
+
|
|
43
|
+
var (
|
|
44
|
+
debug_mode bool
|
|
45
|
+
developer_token string
|
|
46
|
+
tigerbeetle_directory string
|
|
47
|
+
repository_url string
|
|
48
|
+
num_voprs int
|
|
49
|
+
current_vopr int
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
type Label struct {
|
|
53
|
+
Name string `json:"name"`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type Head struct {
|
|
57
|
+
Label string `json:"label"`
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type Issue struct {
|
|
61
|
+
Labels []Label `json:"labels"`
|
|
62
|
+
Head Head `json:"head"`
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
type Commit struct {
|
|
66
|
+
Sha string `json:"sha"`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func usage() {
|
|
70
|
+
fmt.Fprintf(os.Stderr, usageFmt, os.Args[0])
|
|
71
|
+
os.Exit(1)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
func load_environment_variable(name string) string {
|
|
75
|
+
val, _ := os.LookupEnv(name)
|
|
76
|
+
if val == "" {
|
|
77
|
+
log_error(fmt.Sprintf("env %s not set", name))
|
|
78
|
+
usage()
|
|
79
|
+
os.Exit(1)
|
|
80
|
+
} else if name == "DEVELOPER_TOKEN" {
|
|
81
|
+
log_debug(fmt.Sprintf("env %s=*****", name))
|
|
82
|
+
} else {
|
|
83
|
+
log_debug(fmt.Sprintf("env %s=%q", name, val))
|
|
84
|
+
}
|
|
85
|
+
return val
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Checks out the appropriate commit for the specified tigerbeetle directory if it exists.
|
|
89
|
+
func checkout_commit(commit string, tigerbeetle_directory string) error {
|
|
90
|
+
// Git commands need to be run with the particular TigerBeetle directory as their
|
|
91
|
+
// working_directory.
|
|
92
|
+
fetch_command := exec.Command("git", "fetch", "--all")
|
|
93
|
+
fetch_command.Dir = tigerbeetle_directory
|
|
94
|
+
error := fetch_command.Run()
|
|
95
|
+
if error != nil {
|
|
96
|
+
error_message := fmt.Sprintf("Failed to run git fetch: %s", error.Error())
|
|
97
|
+
log_error(error_message)
|
|
98
|
+
return error
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Ensures commit is all hexadecimal.
|
|
102
|
+
commit_valid, error := regexp.MatchString(`^([0-9a-f]){40}$`, commit)
|
|
103
|
+
if error != nil {
|
|
104
|
+
panic(error.Error())
|
|
105
|
+
} else if !commit_valid {
|
|
106
|
+
error = fmt.Errorf("The GitHub commit contained unexpected characters")
|
|
107
|
+
log_error(error.Error())
|
|
108
|
+
return error
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Checkout the commit.
|
|
112
|
+
checkout_command := exec.Command("git", "checkout", commit)
|
|
113
|
+
checkout_command.Dir = tigerbeetle_directory
|
|
114
|
+
error = checkout_command.Run()
|
|
115
|
+
if error != nil {
|
|
116
|
+
error_message := fmt.Sprintf("Failed to run git checkout: %s", error.Error())
|
|
117
|
+
log_error(error_message)
|
|
118
|
+
return error
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Inspect the git logs.
|
|
122
|
+
log_command := exec.Command("git", "log", "-1")
|
|
123
|
+
log_command.Dir = tigerbeetle_directory
|
|
124
|
+
log_output := make([]byte, 47)
|
|
125
|
+
log_output, error = log_command.Output()
|
|
126
|
+
if error != nil {
|
|
127
|
+
error_message := fmt.Sprintf("Failed to run git log: %s", error.Error())
|
|
128
|
+
log_error(error_message)
|
|
129
|
+
return error
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Check the log to determine if the commit has been successfully checked out.
|
|
133
|
+
current_commit := string(log_output[0:47])
|
|
134
|
+
checkout_successful, error := regexp.MatchString("^commit "+commit, current_commit)
|
|
135
|
+
if error != nil {
|
|
136
|
+
error_message := fmt.Sprintf(
|
|
137
|
+
"Regular expression failure while checking the git logs: %s",
|
|
138
|
+
error.Error(),
|
|
139
|
+
)
|
|
140
|
+
log_error(error_message)
|
|
141
|
+
return error
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if !checkout_successful {
|
|
145
|
+
error = fmt.Errorf("Checkout failed")
|
|
146
|
+
return error
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return nil
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
func get_pull_requests(num_posts int, page_number int) []Issue {
|
|
153
|
+
pull_requests := []Issue{}
|
|
154
|
+
get_request, err := http.NewRequest(
|
|
155
|
+
"GET",
|
|
156
|
+
fmt.Sprintf("%s/pulls?per_page=%d&page=%d", repository_url, num_posts, page_number),
|
|
157
|
+
nil,
|
|
158
|
+
)
|
|
159
|
+
if err != nil {
|
|
160
|
+
log_error("unable to create get request")
|
|
161
|
+
panic(err.Error())
|
|
162
|
+
}
|
|
163
|
+
get_request.Header.Set("Authorization", "token "+developer_token)
|
|
164
|
+
res, err := http.DefaultClient.Do(get_request)
|
|
165
|
+
if err != nil {
|
|
166
|
+
log_error("Failed to send the HTTP request for the GitHub API")
|
|
167
|
+
panic(err.Error())
|
|
168
|
+
}
|
|
169
|
+
defer res.Body.Close()
|
|
170
|
+
|
|
171
|
+
body, err := io.ReadAll(res.Body)
|
|
172
|
+
|
|
173
|
+
if res.StatusCode > 299 {
|
|
174
|
+
log_error(
|
|
175
|
+
fmt.Sprintf(
|
|
176
|
+
"Response failed with status code: %d and\nbody: %s\n",
|
|
177
|
+
res.StatusCode,
|
|
178
|
+
body,
|
|
179
|
+
),
|
|
180
|
+
)
|
|
181
|
+
panic(err.Error())
|
|
182
|
+
}
|
|
183
|
+
if err != nil {
|
|
184
|
+
log_error("unable to receive a response from GitHub")
|
|
185
|
+
panic(err.Error())
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
err = json.Unmarshal(body, &pull_requests)
|
|
189
|
+
if err != nil {
|
|
190
|
+
log_error("unable to unmarshall json")
|
|
191
|
+
panic(err.Error())
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return pull_requests
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
func get_commits(branch_name string) string {
|
|
198
|
+
commits := []Commit{}
|
|
199
|
+
get_request, err := http.NewRequest(
|
|
200
|
+
"GET",
|
|
201
|
+
fmt.Sprintf("%s/commits?per_page=1&sha=%s", repository_url, branch_name),
|
|
202
|
+
nil,
|
|
203
|
+
)
|
|
204
|
+
if err != nil {
|
|
205
|
+
log_error("unable to create get request")
|
|
206
|
+
panic(err.Error())
|
|
207
|
+
}
|
|
208
|
+
get_request.Header.Set("Authorization", "token "+developer_token)
|
|
209
|
+
res, err := http.DefaultClient.Do(get_request)
|
|
210
|
+
if err != nil {
|
|
211
|
+
log_error("Failed to send the HTTP request for the GitHub API")
|
|
212
|
+
panic(err.Error())
|
|
213
|
+
}
|
|
214
|
+
defer res.Body.Close()
|
|
215
|
+
|
|
216
|
+
body, err := io.ReadAll(res.Body)
|
|
217
|
+
|
|
218
|
+
if res.StatusCode > 299 {
|
|
219
|
+
log_error(
|
|
220
|
+
fmt.Sprintf(
|
|
221
|
+
"Response failed with status code: %d and\nbody: %s\n",
|
|
222
|
+
res.StatusCode,
|
|
223
|
+
body,
|
|
224
|
+
),
|
|
225
|
+
)
|
|
226
|
+
panic(err.Error())
|
|
227
|
+
}
|
|
228
|
+
if err != nil {
|
|
229
|
+
log_error("unable to receive a response from GitHub")
|
|
230
|
+
panic(err.Error())
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
err = json.Unmarshal(body, &commits)
|
|
234
|
+
if err != nil {
|
|
235
|
+
log_error("unable to unmarshall json")
|
|
236
|
+
panic(err.Error())
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if len(commits) > 0 && len(commits[0].Sha) > 0 {
|
|
240
|
+
return commits[0].Sha
|
|
241
|
+
} else {
|
|
242
|
+
error := fmt.Errorf("unable to access commit associated with branch %s", branch_name)
|
|
243
|
+
panic(error.Error())
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
func get_commit_hashes() []string {
|
|
248
|
+
const num_posts int = 30 // This is the GitHub API default.
|
|
249
|
+
var pull_requests []Issue
|
|
250
|
+
var vopr_commits []string
|
|
251
|
+
|
|
252
|
+
page_number := 1
|
|
253
|
+
|
|
254
|
+
// Require at most (num_voprs-1) pull requests.
|
|
255
|
+
// The first VOPR always runs on main's latest commit.
|
|
256
|
+
for len(vopr_commits) < num_voprs-1 {
|
|
257
|
+
// Pull requests will be ordered newest to oldest by default.
|
|
258
|
+
pull_requests = get_pull_requests(num_posts, page_number)
|
|
259
|
+
|
|
260
|
+
for _, element := range pull_requests {
|
|
261
|
+
for _, label := range element.Labels {
|
|
262
|
+
if label.Name == "vopr" {
|
|
263
|
+
// Branches are returned in the format owner:branch_name.
|
|
264
|
+
_, branch_name, found := strings.Cut(element.Head.Label, ":")
|
|
265
|
+
if found && branch_name != "" {
|
|
266
|
+
commit := get_commits(branch_name)
|
|
267
|
+
vopr_commits = append(vopr_commits, commit)
|
|
268
|
+
}
|
|
269
|
+
break
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if len(vopr_commits) == num_voprs-1 {
|
|
274
|
+
break
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Exit the loop if there are no more pages of pull requests to be fetched from GitHub.
|
|
278
|
+
if len(pull_requests) < num_posts {
|
|
279
|
+
break
|
|
280
|
+
}
|
|
281
|
+
page_number++
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return vopr_commits
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
func get_vopr_assignments(vopr_commits []string) []string {
|
|
288
|
+
var num_pull_requests = len(vopr_commits)
|
|
289
|
+
var vopr_assignments []string
|
|
290
|
+
main_commit := get_commits("main")
|
|
291
|
+
|
|
292
|
+
if num_pull_requests > 0 {
|
|
293
|
+
// The first VOPR always runs main
|
|
294
|
+
vopr_assignments = append(vopr_assignments, main_commit)
|
|
295
|
+
|
|
296
|
+
// This calculates how many times each PR branch will be assigned to a VOPR.
|
|
297
|
+
var repeats = int((num_voprs - 1) / num_pull_requests)
|
|
298
|
+
// This calculates how many branches will have an additional assignment.
|
|
299
|
+
var remainders = (num_voprs - 1) % num_pull_requests
|
|
300
|
+
i := 1
|
|
301
|
+
commit_index := 0
|
|
302
|
+
for i < num_voprs {
|
|
303
|
+
for j := 0; j < repeats; j++ {
|
|
304
|
+
vopr_assignments = append(
|
|
305
|
+
vopr_assignments,
|
|
306
|
+
fmt.Sprintf("%s", vopr_commits[commit_index]),
|
|
307
|
+
)
|
|
308
|
+
i++
|
|
309
|
+
}
|
|
310
|
+
if remainders > 0 {
|
|
311
|
+
vopr_assignments = append(
|
|
312
|
+
vopr_assignments,
|
|
313
|
+
fmt.Sprintf("%s", vopr_commits[commit_index]),
|
|
314
|
+
)
|
|
315
|
+
remainders--
|
|
316
|
+
i++
|
|
317
|
+
}
|
|
318
|
+
commit_index++
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
i := 0
|
|
322
|
+
for i < num_voprs {
|
|
323
|
+
vopr_assignments = append(vopr_assignments, main_commit)
|
|
324
|
+
i++
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return vopr_assignments
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
func log_error(message string) {
|
|
331
|
+
log_message("error: ", message)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
func log_debug(message string) {
|
|
335
|
+
if debug_mode {
|
|
336
|
+
log_message("debug: ", message)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Formats all the log messages and adds a timestamp to them.
|
|
341
|
+
func log_message(log_level string, message string) {
|
|
342
|
+
// Gets the current time in UTC and rounds to the nearest second.
|
|
343
|
+
timestamp := time.Now().UTC().Format(time.RFC3339)
|
|
344
|
+
fmt.Printf("%s %s%s\n", timestamp, log_level, message)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
func main() {
|
|
348
|
+
// Determine the mode in which to run the VOPR Hub.
|
|
349
|
+
flags := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
|
350
|
+
flags.BoolVar(&debug_mode, "debug", false, "runs with debugging logs enabled")
|
|
351
|
+
if err := flags.Parse(os.Args[1:]); err != nil {
|
|
352
|
+
usage()
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
tigerbeetle_directory = load_environment_variable("TIGERBEETLE_DIRECTORY")
|
|
356
|
+
repository_url = load_environment_variable("REPOSITORY_URL")
|
|
357
|
+
developer_token = load_environment_variable("DEVELOPER_TOKEN")
|
|
358
|
+
num_voprs_str := load_environment_variable("NUM_VOPRS")
|
|
359
|
+
// string to int
|
|
360
|
+
var err error
|
|
361
|
+
num_voprs, err = strconv.Atoi(num_voprs_str)
|
|
362
|
+
if err != nil {
|
|
363
|
+
log_error("unable to convert num_voprs to a an integer value")
|
|
364
|
+
panic(err.Error())
|
|
365
|
+
} else if num_voprs <= 0 {
|
|
366
|
+
log_error("NUM_VOPRS must be an integer greater than 0")
|
|
367
|
+
os.Exit(1)
|
|
368
|
+
}
|
|
369
|
+
current_vopr_str := load_environment_variable("CURRENT_VOPR")
|
|
370
|
+
// string to int
|
|
371
|
+
current_vopr, err = strconv.Atoi(current_vopr_str)
|
|
372
|
+
if err != nil {
|
|
373
|
+
log_error("unable to convert current_vopr to a an integer value")
|
|
374
|
+
panic(err.Error())
|
|
375
|
+
} else if current_vopr < 0 {
|
|
376
|
+
log_error("CURRENT_VOPR must be a positive integer")
|
|
377
|
+
os.Exit(1)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Gets commit hashes for main and up to (NUM_VOPRS -1) PR branches that have the `vopr` label.
|
|
381
|
+
vopr_commits := get_commit_hashes()
|
|
382
|
+
|
|
383
|
+
// Assigns one commit for each VOPR to run on.
|
|
384
|
+
vopr_assignments := get_vopr_assignments(vopr_commits)
|
|
385
|
+
|
|
386
|
+
if current_vopr < len(vopr_assignments) && current_vopr >= 0 {
|
|
387
|
+
error := checkout_commit(
|
|
388
|
+
vopr_assignments[current_vopr],
|
|
389
|
+
fmt.Sprintf("%s%d", tigerbeetle_directory, current_vopr),
|
|
390
|
+
)
|
|
391
|
+
if error == nil {
|
|
392
|
+
log_debug("Successfully checked out commit " + vopr_assignments[current_vopr])
|
|
393
|
+
} else {
|
|
394
|
+
error_message := fmt.Sprintf(
|
|
395
|
+
"Failed to checkout commit %s: %s",
|
|
396
|
+
vopr_assignments[current_vopr],
|
|
397
|
+
error.Error(),
|
|
398
|
+
)
|
|
399
|
+
log_error(error_message)
|
|
400
|
+
panic(error.Error())
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
@@ -81,6 +81,15 @@ pub fn Client(comptime StateMachine: type, comptime MessageBus: type) type {
|
|
|
81
81
|
/// Seeded with the client's ID.
|
|
82
82
|
prng: std.rand.DefaultPrng,
|
|
83
83
|
|
|
84
|
+
on_reply_context: ?*anyopaque = null,
|
|
85
|
+
/// Used for testing. Called for replies to all operations (including `register`).
|
|
86
|
+
on_reply_callback: ?fn (
|
|
87
|
+
context: ?*anyopaque,
|
|
88
|
+
client: *Self,
|
|
89
|
+
request: *Message,
|
|
90
|
+
reply: *Message,
|
|
91
|
+
) void = null,
|
|
92
|
+
|
|
84
93
|
pub fn init(
|
|
85
94
|
allocator: mem.Allocator,
|
|
86
95
|
id: u128,
|
|
@@ -352,6 +361,10 @@ pub fn Client(comptime StateMachine: type, comptime MessageBus: type) type {
|
|
|
352
361
|
self.send_request_for_the_first_time(next_request.message);
|
|
353
362
|
}
|
|
354
363
|
|
|
364
|
+
if (self.on_reply_callback) |on_reply_callback| {
|
|
365
|
+
on_reply_callback(self.on_reply_context, self, inflight.message, reply);
|
|
366
|
+
}
|
|
367
|
+
|
|
355
368
|
if (inflight.message.header.operation != .register) {
|
|
356
369
|
inflight.callback(
|
|
357
370
|
inflight.user_data,
|
|
@@ -380,14 +380,15 @@ pub fn Journal(comptime Replica: type, comptime Storage: type) type {
|
|
|
380
380
|
return self.slot_with_op(header.op);
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
-
/// Returns any existing
|
|
384
|
-
///
|
|
385
|
-
pub fn
|
|
383
|
+
/// Returns any existing header at the location indicated by header.op.
|
|
384
|
+
/// The existing header may have an older or newer op number.
|
|
385
|
+
pub fn header_for_prepare(self: *const Self, header: *const Header) ?*const Header {
|
|
386
386
|
assert(header.command == .prepare);
|
|
387
387
|
return self.header_for_op(header.op);
|
|
388
388
|
}
|
|
389
389
|
|
|
390
390
|
/// We use `op` directly to index into the headers array and locate ops without a scan.
|
|
391
|
+
/// The existing header may have an older or newer op number.
|
|
391
392
|
pub fn header_for_op(self: *const Self, op: u64) ?*const Header {
|
|
392
393
|
// TODO Snapshots
|
|
393
394
|
const slot = self.slot_for_op(op);
|
|
@@ -510,7 +511,8 @@ pub fn Journal(comptime Replica: type, comptime Storage: type) type {
|
|
|
510
511
|
|
|
511
512
|
/// Copies latest headers between `op_min` and `op_max` (both inclusive) as fit in `dest`.
|
|
512
513
|
/// Reverses the order when copying so that latest headers are copied first, which protects
|
|
513
|
-
/// against the callsite slicing the buffer the wrong way and incorrectly
|
|
514
|
+
/// against the callsite slicing the buffer the wrong way and incorrectly, and which is
|
|
515
|
+
/// required by message handlers that use the hash chain for repairs.
|
|
514
516
|
/// Skips .reserved headers (gaps between headers).
|
|
515
517
|
/// Zeroes the `dest` buffer in case the copy would underflow and leave a buffer bleed.
|
|
516
518
|
/// Returns the number of headers actually copied.
|
|
@@ -725,6 +727,9 @@ pub fn Journal(comptime Replica: type, comptime Storage: type) type {
|
|
|
725
727
|
if (self.header_with_op_and_checksum(op, checksum)) |exact| {
|
|
726
728
|
if (exact.size == @sizeOf(Header)) {
|
|
727
729
|
message.header.* = exact.*;
|
|
730
|
+
// Normally the message's padding would have been zeroed by the MessageBus,
|
|
731
|
+
// but we are copying (only) a message header into a new buffer.
|
|
732
|
+
std.mem.set(u8, message.buffer[@sizeOf(Header)..config.sector_size], 0);
|
|
728
733
|
callback(replica, message, destination_replica);
|
|
729
734
|
return;
|
|
730
735
|
}
|
|
@@ -981,7 +986,7 @@ pub fn Journal(comptime Replica: type, comptime Storage: type) type {
|
|
|
981
986
|
const max = std.math.min(message.buffer.len, headers_size - offset);
|
|
982
987
|
assert(max % config.sector_size == 0);
|
|
983
988
|
assert(max % @sizeOf(Header) == 0);
|
|
984
|
-
return
|
|
989
|
+
return message.buffer[0..max];
|
|
985
990
|
}
|
|
986
991
|
|
|
987
992
|
fn recover_prepares(self: *Self, slot: Slot) void {
|
|
@@ -1441,6 +1446,7 @@ pub fn Journal(comptime Replica: type, comptime Storage: type) type {
|
|
|
1441
1446
|
header.op,
|
|
1442
1447
|
header.checksum,
|
|
1443
1448
|
});
|
|
1449
|
+
|
|
1444
1450
|
const slot = self.slot_for_header(header);
|
|
1445
1451
|
|
|
1446
1452
|
if (self.has(header)) {
|
|
@@ -1827,10 +1833,7 @@ pub fn Journal(comptime Replica: type, comptime Storage: type) type {
|
|
|
1827
1833
|
self.lock_sectors(@fieldParentPtr(Self.Write, "range", waiting));
|
|
1828
1834
|
}
|
|
1829
1835
|
|
|
1830
|
-
|
|
1831
|
-
const callback = range.callback;
|
|
1832
|
-
range.* = undefined;
|
|
1833
|
-
callback(write);
|
|
1836
|
+
range.callback(write);
|
|
1834
1837
|
}
|
|
1835
1838
|
|
|
1836
1839
|
pub fn writing(self: *Self, op: u64, checksum: u128) bool {
|
|
@@ -2236,14 +2239,14 @@ test "format_journal" {
|
|
|
2236
2239
|
};
|
|
2237
2240
|
|
|
2238
2241
|
for (write_sizes) |write_size_max| {
|
|
2239
|
-
|
|
2242
|
+
const wal_data = try std.testing.allocator.alignedAlloc(u8, @alignOf(Header), config.journal_size_max);
|
|
2240
2243
|
defer std.testing.allocator.free(wal_data);
|
|
2241
2244
|
|
|
2242
|
-
|
|
2245
|
+
const write_data = try std.testing.allocator.alloc(u8, write_size_max);
|
|
2243
2246
|
defer std.testing.allocator.free(write_data);
|
|
2244
2247
|
|
|
2245
|
-
|
|
2246
|
-
|
|
2248
|
+
const headers_ring = std.mem.bytesAsSlice(Header, wal_data[0..config.journal_size_headers]);
|
|
2249
|
+
const prepare_ring = std.mem.bytesAsSlice([config.message_size_max]u8, wal_data[config.journal_size_headers..]);
|
|
2247
2250
|
try std.testing.expectEqual(@as(usize, config.journal_slot_count), headers_ring.len);
|
|
2248
2251
|
try std.testing.expectEqual(@as(usize, config.journal_slot_count), prepare_ring.len);
|
|
2249
2252
|
|