Skip to content

Commit ac1b50d

Browse files
committed
wip: not-yet-working c reproducer for issue #201
1 parent 7f9dac2 commit ac1b50d

File tree

1 file changed

+240
-0
lines changed

1 file changed

+240
-0
lines changed

tests/unit/bug_repro_201_test.c

+240
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
// Copyright 2021 VMware, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// WIP attempt to reproduce https://github.com/vmware/splinterdb/issues/201
5+
// but not yet working
6+
7+
#include <string.h>
8+
#include <unistd.h>
9+
#include <stdlib.h>
10+
#include <fcntl.h>
11+
#include <sys/stat.h>
12+
#include <pthread.h>
13+
14+
#include "splinterdb/platform_public.h"
15+
#include "splinterdb/kvstore_basic.h"
16+
#include "unit_tests.h"
17+
#include "../functional/random.h"
18+
#include "ctest.h"
19+
20+
#define KEY_SIZE 47
21+
#define VALUE_SIZE 8
22+
23+
CTEST_DATA(bug_repro_201)
24+
{
25+
kvstore_basic * kvsb;
26+
kvstore_basic_cfg cfg;
27+
};
28+
29+
CTEST_SETUP(bug_repro_201)
30+
{
31+
data->cfg = (kvstore_basic_cfg){
32+
.filename = "/fdb-data/nvme0n1/testing/c_repro.db",
33+
// .filename = TEST_DB_NAME,
34+
.cache_size = 3 * Giga,
35+
.disk_size = 128 * Giga,
36+
.max_key_size = KEY_SIZE,
37+
.max_value_size = VALUE_SIZE,
38+
};
39+
40+
int rc = kvstore_basic_create(&data->cfg, &data->kvsb);
41+
ASSERT_EQUAL(0, rc);
42+
}
43+
44+
CTEST_TEARDOWN(bug_repro_201)
45+
{
46+
kvstore_basic_close(data->kvsb);
47+
}
48+
49+
static void
50+
rand_fill_buffer(random_state *rand_state, char *to_fill, size_t len_to_fill)
51+
{
52+
random_bytes(rand_state, to_fill, len_to_fill);
53+
}
54+
55+
56+
static void
57+
do_random_lookups(const kvstore_basic *kvsb,
58+
uint32 count,
59+
random_state * rand_state,
60+
char * key_buffer,
61+
size_t key_buffer_len)
62+
{
63+
64+
char val_buffer[VALUE_SIZE];
65+
_Bool val_truncated;
66+
_Bool val_found;
67+
size_t val_size;
68+
69+
for (uint32 i = 0; i < count; i++) {
70+
rand_fill_buffer(rand_state, key_buffer, key_buffer_len);
71+
int rc = kvstore_basic_lookup(kvsb,
72+
key_buffer,
73+
key_buffer_len,
74+
val_buffer,
75+
VALUE_SIZE,
76+
&val_size,
77+
&val_truncated,
78+
&val_found);
79+
ASSERT_EQUAL(0, rc);
80+
}
81+
}
82+
83+
static uint32
84+
do_range_delete(const kvstore_basic *kvsb,
85+
char * start_key,
86+
size_t start_key_len,
87+
uint32 count)
88+
{
89+
90+
// naive range delete
91+
92+
// 1. collect all the keys we need to delete
93+
char *keys_to_delete = calloc(count, KEY_SIZE);
94+
95+
kvstore_basic_iterator *it;
96+
int rc = kvstore_basic_iter_init(kvsb, &it, start_key, start_key_len);
97+
ASSERT_EQUAL(0, rc);
98+
99+
const char *key;
100+
const char *val;
101+
size_t key_len, val_len;
102+
uint32 num_found = 0;
103+
for (; kvstore_basic_iter_valid(it); kvstore_basic_iter_next(it)) {
104+
kvstore_basic_iter_get_current(it, &key, &key_len, &val, &val_len);
105+
ASSERT_EQUAL(KEY_SIZE, key_len);
106+
memcpy(keys_to_delete + num_found * KEY_SIZE, key, KEY_SIZE);
107+
num_found++;
108+
if (num_found >= count) {
109+
break;
110+
}
111+
}
112+
rc = kvstore_basic_iter_status(it);
113+
ASSERT_EQUAL(0, rc);
114+
kvstore_basic_iter_deinit(&it);
115+
116+
// 2. delete every key we collected
117+
for (uint32 i = 0; i < num_found; i++) {
118+
char *key_to_delete = keys_to_delete + i * KEY_SIZE;
119+
kvstore_basic_delete(kvsb, key_to_delete, KEY_SIZE);
120+
}
121+
122+
free(keys_to_delete);
123+
124+
return num_found;
125+
}
126+
127+
128+
static void
129+
do_random_inserts(const kvstore_basic *kvsb,
130+
uint32 count,
131+
random_state * rand_state,
132+
char * key_buffer,
133+
size_t key_buffer_len,
134+
char * val_buffer,
135+
size_t val_buffer_len)
136+
{
137+
138+
for (uint32 i = 0; i < count; i++) {
139+
rand_fill_buffer(rand_state, key_buffer, key_buffer_len);
140+
rand_fill_buffer(rand_state, val_buffer, val_buffer_len);
141+
int rc = kvstore_basic_insert(
142+
kvsb, key_buffer, key_buffer_len, val_buffer, val_buffer_len);
143+
ASSERT_EQUAL(0, rc);
144+
}
145+
}
146+
147+
typedef struct {
148+
kvstore_basic *kvsb;
149+
uint32_t ops_per_round;
150+
uint64_t seed;
151+
} worker_config;
152+
153+
static uint32_t
154+
rand_in_range(random_state *rs, uint32_t min, uint32_t max)
155+
{
156+
return random_next_uint32(rs) % (max + 1 - min) + min;
157+
}
158+
159+
static void *
160+
thread_worker(void *w)
161+
{
162+
char key_buf[KEY_SIZE] = {0};
163+
char value_buf[VALUE_SIZE] = {0};
164+
165+
worker_config *cfg = (worker_config *)w;
166+
167+
kvstore_basic_register_thread(cfg->kvsb);
168+
169+
random_state rand_state;
170+
random_init(&rand_state, cfg->seed, 0);
171+
172+
for (uint32_t round = 0; round < 1000; round++) {
173+
uint32_t min_to_write = 3 * cfg->ops_per_round / 4;
174+
uint32_t max_to_write = cfg->ops_per_round;
175+
uint32_t num_inserts =
176+
rand_in_range(&rand_state, min_to_write, max_to_write);
177+
do_random_inserts(cfg->kvsb,
178+
num_inserts,
179+
&rand_state,
180+
key_buf,
181+
KEY_SIZE,
182+
value_buf,
183+
VALUE_SIZE);
184+
185+
186+
do_random_lookups(
187+
cfg->kvsb, cfg->ops_per_round / 10, &rand_state, key_buf, KEY_SIZE);
188+
189+
if (round >= 100) {
190+
uint32_t num_to_delete = cfg->ops_per_round - num_inserts;
191+
192+
char start_key[4];
193+
rand_fill_buffer(&rand_state, start_key, sizeof(start_key));
194+
do_range_delete(
195+
cfg->kvsb, start_key, sizeof(start_key), num_to_delete);
196+
}
197+
fprintf(stderr, ".");
198+
}
199+
200+
kvstore_basic_deregister_thread(cfg->kvsb);
201+
return 0;
202+
}
203+
204+
205+
CTEST2(bug_repro_201, bug_repro_201)
206+
{
207+
Platform_stdout_fh = stdout;
208+
Platform_stderr_fh = stderr;
209+
// Platform_stdout_fh = fopen("/tmp/unit_test.stdout", "a+");
210+
// Platform_stderr_fh = fopen("/tmp/unit_test.stderr", "a+");
211+
212+
213+
worker_config cfg = {
214+
.kvsb = data->kvsb,
215+
.ops_per_round = 50000,
216+
.seed = 42,
217+
};
218+
219+
const uint8_t num_threads = 4;
220+
pthread_t thread_ids[num_threads];
221+
222+
for (int i = 0; i < num_threads; i++) {
223+
int rc = pthread_create(&thread_ids[i], NULL, &thread_worker, &cfg);
224+
ASSERT_EQUAL(0, rc);
225+
}
226+
227+
for (int i = 0; i < num_threads; i++) {
228+
void *thread_rc;
229+
int rc = pthread_join(thread_ids[i], &thread_rc);
230+
ASSERT_EQUAL(0, rc);
231+
if (thread_rc != 0) {
232+
fprintf(stderr,
233+
"Thread %d [ID=%lu] had error: %p\n",
234+
i,
235+
thread_ids[i],
236+
thread_rc);
237+
ASSERT_TRUE(FALSE);
238+
}
239+
}
240+
}

0 commit comments

Comments
 (0)