Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
73de81d
Core changes to support running Splinter with allocated shared memory.
gapisback Sep 26, 2022
b5f0a50
Support for multi-process, forked processes using shared memory
gapisback Oct 10, 2022
00fbdab
Stabilize large inserts stress tests. Skip some failing cases.
gapisback Mar 7, 2023
fedb8cf
Add more diags to track shm-free calls. Rework large inserts stress t…
gapisback Apr 6, 2023
903b5f0
Minor rework of alloc/free interfaces to use PROCESS_PRIVATE_HEAP_ID
gapisback Apr 6, 2023
4af370e
Change free methods to track 'size' of memory chunk being freed.
gapisback Apr 6, 2023
49cff1a
Implement small free fragment recycling. Add tracing. Use mutex v/s s…
gapisback Apr 6, 2023
5f5eb7c
Suppress assert on size > 0 in platform_shm_track_free()
gapisback Apr 6, 2023
6bbcd5b
Add platform_mem_frag{}, passed to platform_free() to free memory obj…
gapisback Apr 6, 2023
2c8b08a
Percolate errors from shared memory destroy all the way to splinterdb…
gapisback Apr 6, 2023
0661dbc
Update few unit-tests to check for rc from splinterdb_close().
gapisback Apr 6, 2023
514936b
Re-enable few cases in large_inserts_stress_test. Bump up cache to 4G…
gapisback Apr 6, 2023
6a820aa
Fix to get large_inserts_stress test to run in MSAN builds.
gapisback Apr 7, 2023
7a8f5cc
Implement fingerprint object mgmt apis. Test seems to work.
gapisback Apr 8, 2023
8c075e5
Fix call to realloc() that trips assertions. Tighten tests to check rc.
gapisback Apr 8, 2023
1f4d377
Fix failing unit-tests. Tighten platform_free() to require non-NULL p…
gapisback Apr 9, 2023
ce9cb1b
Make fp_array{} a derived object from platform_mem_frag{}
gapisback Apr 9, 2023
7d17d39
Rename to platform_memfrag(). Add more tracing. Fix bugs.
gapisback Apr 9, 2023
e99e962
Purge all refs to platform_free_mem(). Replace with mem_frag{}. Needs…
gapisback Apr 9, 2023
08722f1
Rework stats-gathering struct. Fix one debug tests bug.
gapisback Apr 10, 2023
10497d4
Add repro test_fp_num_tuples_out_of_bounds_bug_trunk_build_filters() …
gapisback Apr 10, 2023
7e9cd03
Fix frags_inuse and frags_inuse_HWM accounting bugs.
gapisback Apr 11, 2023
8fa7967
Fix assertion failure in trunk_build_filters() -> fingerprint_unalias().
gapisback Apr 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,12 @@ TESTSRC := $(COMMON_TESTSRC) $(FUNCTIONAL_TESTSRC) $(UNIT_TESTSRC)
# - Slow unit-tests will be skipped, as we want the resulting unit_test binary
# to run as fast as it can.
# - Skip tests that are to be invoked with specialized command-line arguments.
#
# These tests which are skipped will have to be run stand-alone.
# Construct a list of fast unit-tests that will be linked into unit_test binary,
# eliminating a sequence of slow-running unit-test programs.
ALL_UNIT_TESTSRC := $(call rwildcard, $(UNIT_TESTSDIR), *.c)
SLOW_UNIT_TESTSRC = splinter_test.c config_parse_test.c
SLOW_UNIT_TESTSRC = splinter_test.c config_parse_test.c large_inserts_stress_test.c splinterdb_forked_child_test.c
SLOW_UNIT_TESTSRC_FILTER := $(foreach slowf,$(SLOW_UNIT_TESTSRC), $(UNIT_TESTSDIR)/$(slowf))
FAST_UNIT_TESTSRC := $(sort $(filter-out $(SLOW_UNIT_TESTSRC_FILTER), $(ALL_UNIT_TESTSRC)))

Expand Down Expand Up @@ -386,7 +387,8 @@ $(foreach unit,$(UNIT_TESTBINS),$(eval $(call unit_test_self_dependency,$(unit))
#
# These will need to be fleshed out for filters, io subsystem, trunk,
# etc. as we create mini unit test executables for those subsystems.
PLATFORM_SYS = $(OBJDIR)/$(SRCDIR)/$(PLATFORM_DIR)/platform.o
PLATFORM_SYS = $(OBJDIR)/$(SRCDIR)/$(PLATFORM_DIR)/platform.o \
$(OBJDIR)/$(SRCDIR)/$(PLATFORM_DIR)/shmem.o

PLATFORM_IO_SYS = $(OBJDIR)/$(SRCDIR)/$(PLATFORM_DIR)/laio.o

Expand Down Expand Up @@ -466,6 +468,24 @@ $(BINDIR)/$(UNITDIR)/platform_apis_test: $(UTIL_SYS) \
$(COMMON_UNIT_TESTOBJ) \
$(PLATFORM_SYS)

$(BINDIR)/$(UNITDIR)/splinter_shmem_test: $(UTIL_SYS) \
$(COMMON_UNIT_TESTOBJ) \
$(LIBDIR)/libsplinterdb.so

$(BINDIR)/$(UNITDIR)/splinter_ipc_test: $(UTIL_SYS) \
$(COMMON_UNIT_TESTOBJ)

$(BINDIR)/$(UNITDIR)/splinterdb_forked_child_test: $(OBJDIR)/$(TESTS_DIR)/config.o \
$(COMMON_TESTOBJ) \
$(COMMON_UNIT_TESTOBJ) \
$(OBJDIR)/$(FUNCTIONAL_TESTSDIR)/test_async.o \
$(LIBDIR)/libsplinterdb.so

$(BINDIR)/$(UNITDIR)/large_inserts_stress_test: $(UTIL_SYS) \
$(OBJDIR)/$(TESTS_DIR)/config.o \
$(COMMON_UNIT_TESTOBJ) \
$(LIBDIR)/libsplinterdb.so

########################################
# Convenience mini unit-test targets
unit/util_test: $(BINDIR)/$(UNITDIR)/util_test
Expand All @@ -476,6 +496,11 @@ unit/splinter_test: $(BINDIR)/$(UNITDIR)/splinter_test
unit/splinterdb_quick_test: $(BINDIR)/$(UNITDIR)/splinterdb_quick_test
unit/splinterdb_stress_test: $(BINDIR)/$(UNITDIR)/splinterdb_stress_test
unit/writable_buffer_test: $(BINDIR)/$(UNITDIR)/writable_buffer_test
unit/config_parse_test: $(BINDIR)/$(UNITDIR)/config_parse_test
unit/limitations_test: $(BINDIR)/$(UNITDIR)/limitations_test
unit/task_system_test: $(BINDIR)/$(UNITDIR)/task_system_test
unit/splinter_shmem_test: $(BINDIR)/$(UNITDIR)/splinter_shmem_test
unit/splinter_ipc_test: $(BINDIR)/$(UNITDIR)/splinter_ipc_test
unit_test: $(BINDIR)/unit_test

# -----------------------------------------------------------------------------
Expand Down
42 changes: 34 additions & 8 deletions include/splinterdb/splinterdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,29 @@
const char *
splinterdb_get_version();

// Configuration options for SplinterDB
typedef struct {
/*
* ****************************************************************************
* Configuration options for SplinterDB:
*
* Physical configuration things such as file name, cache & disk-size,
* extent- and page-size are specified here. Application-specific data
* configuration is also provided through this struct. Additionally,
* user can select whether to use malloc()/free()-based memory allocation
* for all structures (default), or choose to setup a shared segment
* which will be used for shared structures.
*
* ******************* EXPERIMENTAL FEATURES ********************
*
* - use_shmem: Support for shared memory segments:
* This flag will configure a shared memory segment. All (most) run-time
* memory allocation will be done from this shared segment. Currently,
* we do not support free(), so you will likely run out of shared memory
* and run into shared-memory OOM errors. This functionality is
* solely meant for internal development uses.
*
* ******************* EXPERIMENTAL FEATURES ********************
*/
typedef struct splinterdb_config {
// required configuration
const char *filename;
uint64 cache_size;
Expand All @@ -32,12 +53,19 @@ typedef struct {
// For a simple reference implementation, see default_data_config.h
data_config *data_cfg;


// optional advanced config below
// if unset, defaults will be used
void *heap_handle;
void *heap_id;

// Shared memory support
uint64 shmem_size;
_Bool use_shmem; // Default is FALSE.
_Bool trace_shmem_allocs;
_Bool trace_shmem_frees;
_Bool trace_shmem; // Trace both allocs & frees from shared memory
_Bool fork_child; // Default is FALSE

uint64 page_size;
uint64 extent_size;

Expand Down Expand Up @@ -121,7 +149,6 @@ typedef struct {
// work to be performed on foreground threads, increasing tail
// latencies.
uint64 queue_scale_percent;

} splinterdb_config;

// Opaque handle to an opened instance of SplinterDB
Expand All @@ -136,7 +163,7 @@ typedef struct splinterdb splinterdb;
// But cfg->data_cfg will be referenced by the returned splinterdb object
// So it must live at least as long as the splinterdb
int
splinterdb_create(const splinterdb_config *cfg, splinterdb **kvs);
splinterdb_create(splinterdb_config *cfg, splinterdb **kvs);

// Open an existing splinterdb from a file/device on disk
//
Expand All @@ -147,12 +174,12 @@ splinterdb_create(const splinterdb_config *cfg, splinterdb **kvs);
// But cfg->data_cfg will be referenced by the returned splinterdb object
// So it must live at least as long as the splinterdb
int
splinterdb_open(const splinterdb_config *cfg, splinterdb **kvs);
splinterdb_open(splinterdb_config *cfg, splinterdb **kvs);

// Close a splinterdb
//
// This will flush all data to disk and release all resources
void
int
splinterdb_close(splinterdb **kvs);

// Register the current thread so that it can be used with splinterdb.
Expand Down Expand Up @@ -366,7 +393,6 @@ splinterdb_iterator_status(const splinterdb_iterator *iter);
*
* Reset statistics clears all statistics, including cache statistics.
*/

void
splinterdb_stats_print_insertion(const splinterdb *kvs);

Expand Down
6 changes: 3 additions & 3 deletions src/allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ allocator_page_valid(allocator *al, uint64 addr)
if ((addr % allocator_cfg->io_cfg->page_size) != 0) {
platform_error_log("%s():%d: Specified addr=%lu is not divisible by"
" configured page size=%lu\n",
__FUNCTION__,
__func__,
__LINE__,
addr,
allocator_cfg->io_cfg->page_size);
Expand All @@ -275,7 +275,7 @@ allocator_page_valid(allocator *al, uint64 addr)
platform_error_log(
"%s():%d: Trying to access an unreferenced extent."
" base_addr=%lu, addr=%lu, allocator_get_refcount()=%d\n",
__FUNCTION__,
__func__,
__LINE__,
base_addr,
addr,
Expand All @@ -286,7 +286,7 @@ allocator_page_valid(allocator *al, uint64 addr)
platform_error_log("%s():%d: Extent out of allocator capacity range."
" base_addr=%lu, addr=%lu"
", allocator_get_capacity()=%lu\n",
__FUNCTION__,
__func__,
__LINE__,
base_addr,
addr,
Expand Down
36 changes: 26 additions & 10 deletions src/btree.c
Original file line number Diff line number Diff line change
Expand Up @@ -1778,7 +1778,23 @@ btree_insert(cache *cc, // IN
btree_node parent_node = root_node;
btree_node child_node;
child_node.addr = index_entry_child_addr(parent_entry);
debug_assert(allocator_page_valid(cache_get_allocator(cc), child_node.addr));
#if SPLINTER_DEBUG
bool child_node_is_valid =
allocator_page_valid(cache_get_allocator(cc), child_node.addr);
if (!child_node_is_valid) {
btree_print_tree(Platform_default_log_handle,
cc,
(btree_config *)cfg,
parent_node.addr,
PAGE_TYPE_MEMTABLE);
}
#endif // SPLINTER_DEBUG

debug_assert(child_node_is_valid,
"parent_node.addr=%lu, child_node.addr=%lu\n",
parent_node.addr,
child_node.addr);

btree_node_get(cc, cfg, &child_node, PAGE_TYPE_MEMTABLE);

uint64 height = btree_height(parent_node.hdr);
Expand Down Expand Up @@ -2829,6 +2845,12 @@ btree_pack_link_extent(btree_pack_req *req,
req->num_edges[height] = 0;
}

static inline bool
btree_pack_can_fit_tuple(btree_pack_req *req)
{
return req->num_tuples < req->max_tuples;
}

static inline btree_node *
btree_pack_create_next_node(btree_pack_req *req, uint64 height, key pivot)
{
Expand Down Expand Up @@ -2892,8 +2914,8 @@ btree_pack_loop(btree_pack_req *req, // IN/OUT
log_trace_key(tuple_key, "btree_pack_loop (bottom)");

if (req->hash) {
platform_assert(req->num_tuples < req->max_tuples);
req->fingerprint_arr[req->num_tuples] =
platform_assert(btree_pack_can_fit_tuple(req));
fingerprint_start(&req->fingerprint)[req->num_tuples] =
req->hash(key_data(tuple_key), key_length(tuple_key), req->seed);
}

Expand Down Expand Up @@ -2941,12 +2963,6 @@ btree_pack_post_loop(btree_pack_req *req, key last_key)
mini_release(&req->mini, last_key);
}

static bool
btree_pack_can_fit_tuple(btree_pack_req *req, key tuple_key, message data)
{
return req->num_tuples < req->max_tuples;
}

static void
btree_pack_abort(btree_pack_req *req)
{
Expand Down Expand Up @@ -2985,7 +3001,7 @@ btree_pack(btree_pack_req *req)

while (SUCCESS(iterator_at_end(req->itor, &at_end)) && !at_end) {
iterator_get_curr(req->itor, &tuple_key, &data);
if (!btree_pack_can_fit_tuple(req, tuple_key, data)) {
if (!btree_pack_can_fit_tuple(req)) {
platform_error_log("%s(): req->num_tuples=%lu exceeded output size "
"limit, req->max_tuples=%lu\n",
__func__,
Expand Down
32 changes: 24 additions & 8 deletions src/btree.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ typedef struct btree_pack_req {
btree_config *cfg;
iterator *itor; // the itor which is being packed
uint64 max_tuples;
hash_fn hash; // hash function used for calculating filter_hash
unsigned int seed; // seed used for calculating filter_hash
uint32 *fingerprint_arr; // IN/OUT: hashes of the keys in the tree
hash_fn hash; // hash function used for calculating filter_hash
unsigned int seed; // seed used for calculating filter_hash
fp_hdr fingerprint; // IN/OUT: hashes of the keys in the tree

// internal data
uint16 height;
Expand All @@ -168,6 +168,7 @@ typedef struct btree_pack_req {
uint64 num_tuples; // no. of tuples in the output tree
uint64 key_bytes; // total size of keys in tuples of the output tree
uint64 message_bytes; // total size of msgs in tuples of the output tree
uint64 line; // Caller's line #
} btree_pack_req;

struct btree_async_ctxt;
Expand Down Expand Up @@ -323,7 +324,11 @@ btree_iterator_init(cache *cc,
void
btree_iterator_deinit(btree_iterator *itor);

static inline void
/*
* Initialize BTree Pack request structure. May allocate memory for fingerprint
* array.
*/
static inline platform_status
btree_pack_req_init(btree_pack_req *req,
cache *cc,
btree_config *cfg,
Expand All @@ -341,16 +346,27 @@ btree_pack_req_init(btree_pack_req *req,
req->hash = hash;
req->seed = seed;
if (hash != NULL && max_tuples > 0) {
req->fingerprint_arr =
TYPED_ARRAY_MALLOC(hid, req->fingerprint_arr, max_tuples);

fingerprint_init(&req->fingerprint, hid, max_tuples); // Allocates memory

// When we run with shared-memory configured, we expect that it is sized
// big-enough to not get OOMs from here. Hence, only a debug_assert().
debug_assert(!fingerprint_is_empty(&req->fingerprint),
"Unable to allocate memory for %lu tuples",
max_tuples);
if (fingerprint_is_empty(&req->fingerprint)) {
return STATUS_NO_MEMORY;
}
}
return STATUS_OK;
}

// Free memory if any was allocated for fingerprint array.
static inline void
btree_pack_req_deinit(btree_pack_req *req, platform_heap_id hid)
{
if (req->fingerprint_arr) {
platform_free(hid, req->fingerprint_arr);
if (!fingerprint_is_empty(&req->fingerprint)) {
fingerprint_deinit(hid, &req->fingerprint);
}
}

Expand Down
Loading