Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9900202
Integrate BULKI serializer to save and restore server checkpoints
biqar Feb 25, 2026
0a53f02
Add code to test metadata checkpointing
biqar Feb 27, 2026
5430beb
Refactor kvtag add/get/verify test code
biqar Feb 27, 2026
602c5e6
Add bulki-based new transfer request metadata query for checkpointing
biqar Mar 17, 2026
050a723
Update call to bulki serialization and de-serialization directly to a…
biqar Mar 17, 2026
7efb945
Refactor bulki-based checkpointing integration in pdc-server
biqar Mar 17, 2026
684bf32
Refactor kvtag-get-verify test code
biqar Mar 17, 2026
389273a
Merge branch 'develop' into feature_metadata-serialization
jeanbez Mar 17, 2026
2c92c0b
Refactor code
biqar Mar 18, 2026
f1ce9f9
Merge remote-tracking branch 'origin/feature_metadata-serialization' …
biqar Mar 18, 2026
65c546f
Committing clang-format changes
github-actions[bot] Mar 26, 2026
ca6b636
Move the bulki logs under build preprocessor
biqar Mar 26, 2026
b995ac8
Add timer log for server restart
biqar Mar 26, 2026
0b76afc
Fix issue in printing server close time
biqar Mar 26, 2026
c9265b9
Merge remote-tracking branch 'origin/feature_metadata-serialization' …
biqar Mar 26, 2026
bfceb4a
Merge branch 'develop' into feature_metadata-serialization
biqar Mar 26, 2026
0fac8be
Merge branch 'develop' into feature_metadata-serialization
jeanbez May 13, 2026
08bff24
Fix log level to report the total checkpoint time
biqar May 15, 2026
fe12777
Merge remote-tracking branch 'origin/feature_metadata-serialization' …
biqar May 15, 2026
1b889a3
Fix close_server MPI init order and remove redundant barrier
biqar Jun 16, 2026
8cea3f5
Speed up BULKI checkpoint tree construction with incremental sizing APIs
biqar Jun 16, 2026
8d714d6
Committing clang-format changes
github-actions[bot] Jun 16, 2026
7612620
Add parameterized checkpoint/restart test runners and CTest coverage
biqar Jun 16, 2026
d572cba
Merge remote-tracking branch 'origin/feature_metadata-serialization' …
biqar Jun 16, 2026
d8171d9
Split kvtag scale tests into serial and MPI binaries from one source.
biqar Jun 26, 2026
3742765
Committing clang-format changes
github-actions[bot] Jun 26, 2026
a772b68
Merge pull request #302 from hpc-io/develop
jeanbez Feb 4, 2026
be5e57f
Revert "Merge pull request #302 from hpc-io/develop"
biqar Jun 27, 2026
f9a88ce
Merge remote-tracking branch 'origin/feature_metadata-serialization' …
biqar Jun 27, 2026
01d9f3a
Revert "Split kvtag scale tests into serial and MPI binaries from one…
biqar Jun 27, 2026
78c22c4
Merge branch 'develop' and resolve conflicts
biqar Jun 27, 2026
56973f4
Merge branch 'develop' into feature_metadata-serialization
jeanbez Jun 28, 2026
dc699f2
Harden BULKI checkpoint write and restart against malformed input.
biqar Jul 2, 2026
a43c8e3
Committing clang-format changes
github-actions[bot] Jul 2, 2026
10b83df
Validate BULKI_get results during checkpoint restart deserialization.
biqar Jul 3, 2026
be52c75
Merge remote-tracking branch 'origin/feature_metadata-serialization' …
biqar Jul 3, 2026
a4b14b7
Committing clang-format changes
github-actions[bot] Jul 3, 2026
82a043e
Remove unused variable names.
biqar Jul 3, 2026
4d0843f
Committing clang-format changes
github-actions[bot] Jul 3, 2026
87b2742
Fix out-of-bounds access in BULKI_delete_incremental().
biqar Jul 3, 2026
00b990f
Committing clang-format changes
github-actions[bot] Jul 3, 2026
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
2 changes: 1 addition & 1 deletion src/api/close_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ main(int argc, char *argv[])
#endif

FUNC_LEAVE(0);
}
}
274 changes: 257 additions & 17 deletions src/commons/serde/bulki/bulki.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@
#include "pdc_timing.h"
#include "pdc_malloc.h"

/** Wire size of a BULKI_Entity header: pdc_class, pdc_type, count, size fields. */
#define BULKI_ENTITY_WIRE_HEADER_SIZE ((size_t)(sizeof(int8_t) * 2 + sizeof(uint64_t) * 2))
/** Wire size of a BULKI map header: totalSize, numKeys, headerSize, dataSize, offsets. */
#define BULKI_WIRE_META_SIZE ((size_t)(sizeof(uint64_t) * 6))

static size_t bulki_entity_wire_size(BULKI_Entity *entity);
static size_t bulki_wire_size(BULKI *bulki);
static void bulki_refresh_total_size(BULKI *bulki);
static void bulki_entity_array_ensure_capacity(BULKI_Entity *dest, size_t element_size);
static BULKI_Entity *empty_array_entity(pdc_c_var_type_t pdc_type, int initial_capacity);

size_t
get_BULKI_Entity_size(BULKI_Entity *bulk_entity)
{
Expand Down Expand Up @@ -69,6 +80,102 @@ get_BULKI_size(BULKI *bulki)
FUNC_LEAVE(size);
}

/**
* Return the serialized byte length of a BULKI_Entity for incremental size updates.
*
* Uses the cached `entity->size` when set; otherwise falls back to a full
* `get_BULKI_Entity_size()` walk. Used by `_incremental` append helpers to avoid
* recomputing sibling subtrees on each append.
*/
static size_t
bulki_entity_wire_size(BULKI_Entity *entity)
{
if (entity != NULL && entity->size > 0)
return entity->size;
return get_BULKI_Entity_size(entity);
}

/**
* Return the serialized byte length of a BULKI map for incremental size updates.
*
* Uses the cached `bulki->totalSize` when set; otherwise falls back to
* `get_BULKI_size()`. Used when appending a BULKI child to an array entity.
*/
static size_t
bulki_wire_size(BULKI *bulki)
{
if (bulki != NULL && bulki->totalSize > 0)
return bulki->totalSize;
return get_BULKI_size(bulki);
}

/**
* Recompute `bulki->totalSize` from cached header and data region sizes in O(1).
*
* Assumes `headerSize` and `dataSize` are already accurate (maintained by
* `BULKI_put_incremental` / `BULKI_delete_incremental`). Does not walk keys.
*/
static void
bulki_refresh_total_size(BULKI *bulki)
{
bulki->totalSize = BULKI_WIRE_META_SIZE + bulki->header->headerSize + bulki->data->dataSize;
}

/**
* Grow a BULKI_Entity array backing store when `count` reaches `capacity`.
*
* Doubles capacity (minimum 1) and reallocates `dest->data`. No-op when free
* slots remain. Used by `_incremental` append paths to avoid per-element realloc.
*
* @param dest Array entity (PDC_CLS_ARRAY, PDC_BULKI or PDC_BULKI_ENT)
* @param element_size sizeof(BULKI) or sizeof(BULKI_Entity)
*/
static void
bulki_entity_array_ensure_capacity(BULKI_Entity *dest, size_t element_size)
{
if (dest->count < dest->capacity)
return;

if (dest->capacity == 0)
dest->capacity = 1;
else
dest->capacity *= 2;

dest->data = PDC_realloc(dest->data, dest->capacity * element_size);
}

/**
* Allocate an empty array BULKI_Entity, optionally with pre-sized backing storage.
*
* Shared by `empty_BULKI_Array_Entity_with_capacity` and
* `empty_Bent_Array_Entity_with_capacity`. Initializes `size` to the wire header
* only; `_incremental` appends add child wire sizes as elements are inserted.
*
* @param pdc_type PDC_BULKI or PDC_BULKI_ENT
* @param initial_capacity Slot count to pre-allocate; 0 leaves `data` NULL
*/
static BULKI_Entity *
empty_array_entity(pdc_c_var_type_t pdc_type, int initial_capacity)
{
BULKI_Entity *bulki_entity = (BULKI_Entity *)PDC_calloc(1, sizeof(BULKI_Entity));
bulki_entity->pdc_type = pdc_type;
bulki_entity->pdc_class = PDC_CLS_ARRAY;
bulki_entity->count = 0;
bulki_entity->capacity = 0;
bulki_entity->data = NULL;
bulki_entity->size = BULKI_ENTITY_WIRE_HEADER_SIZE;

if (initial_capacity > 0) {
bulki_entity->capacity = (uint64_t)initial_capacity;
if (pdc_type == PDC_BULKI)
bulki_entity->data = PDC_calloc((size_t)initial_capacity, sizeof(BULKI));
else if (pdc_type == PDC_BULKI_ENT)
bulki_entity->data = PDC_calloc((size_t)initial_capacity, sizeof(BULKI_Entity));
}

return bulki_entity;
}

void
BULKI_Entity_print(BULKI_Entity *bulk_entity)
{
Expand Down Expand Up @@ -147,30 +254,28 @@ BULKI_Entity *
empty_BULKI_Array_Entity()
{
FUNC_ENTER(NULL);
FUNC_LEAVE(empty_array_entity(PDC_BULKI, 0));
}

BULKI_Entity *bulki_entity = (BULKI_Entity *)PDC_calloc(1, sizeof(BULKI_Entity));
bulki_entity->pdc_type = PDC_BULKI;
bulki_entity->pdc_class = PDC_CLS_ARRAY;
bulki_entity->count = 0;
bulki_entity->data = NULL;
get_BULKI_Entity_size(bulki_entity);

FUNC_LEAVE(bulki_entity);
BULKI_Entity *
empty_BULKI_Array_Entity_with_capacity(int initial_capacity)
{
FUNC_ENTER(NULL);
FUNC_LEAVE(empty_array_entity(PDC_BULKI, initial_capacity));
}

BULKI_Entity *
empty_Bent_Array_Entity()
{
FUNC_ENTER(NULL);
FUNC_LEAVE(empty_array_entity(PDC_BULKI_ENT, 0));
}

BULKI_Entity *bulki_entity = (BULKI_Entity *)PDC_calloc(1, sizeof(BULKI_Entity));
bulki_entity->pdc_type = PDC_BULKI_ENT;
bulki_entity->pdc_class = PDC_CLS_ARRAY;
bulki_entity->count = 0;
bulki_entity->data = NULL;
get_BULKI_Entity_size(bulki_entity);

FUNC_LEAVE(bulki_entity);
BULKI_Entity *
empty_Bent_Array_Entity_with_capacity(int initial_capacity)
{
FUNC_ENTER(NULL);
FUNC_LEAVE(empty_array_entity(PDC_BULKI_ENT, initial_capacity));
}

BULKI_Entity *
Expand All @@ -194,6 +299,31 @@ BULKI_ENTITY_append_BULKI(BULKI_Entity *dest, BULKI *src)
FUNC_LEAVE(dest);
}

BULKI_Entity *
BULKI_ENTITY_append_BULKI_incremental(BULKI_Entity *dest, BULKI *src)
{
FUNC_ENTER(NULL);

size_t child_size;

if (src == NULL || dest == NULL) {
LOG_ERROR("Error: bulki is NULL\n");
FUNC_LEAVE(NULL);
}
if (dest->pdc_class != PDC_CLS_ARRAY || dest->pdc_type != PDC_BULKI) {
LOG_ERROR("Error: dest is not an array of BULKI structure\n");
FUNC_LEAVE(NULL);
}

child_size = bulki_wire_size(src);
bulki_entity_array_ensure_capacity(dest, sizeof(BULKI));
memcpy((BULKI *)dest->data + dest->count, src, sizeof(BULKI));
dest->size += child_size;
dest->count++;

FUNC_LEAVE(dest);
}

BULKI *
BULKI_ENTITY_get_BULKI(BULKI_Entity *bulki_entity, size_t idx)
{
Expand Down Expand Up @@ -237,6 +367,31 @@ BULKI_ENTITY_append_BULKI_Entity(BULKI_Entity *dest, BULKI_Entity *src)
FUNC_LEAVE(dest);
}

BULKI_Entity *
BULKI_ENTITY_append_BULKI_Entity_incremental(BULKI_Entity *dest, BULKI_Entity *src)
{
FUNC_ENTER(NULL);

size_t child_size;

if (src == NULL || dest == NULL) {
LOG_ERROR("Error: bulki is NULL\n");
FUNC_LEAVE(NULL);
}
if (dest->pdc_class != PDC_CLS_ARRAY || dest->pdc_type != PDC_BULKI_ENT) {
LOG_ERROR("Error: dest is not an array of BULKI_Entity structure\n");
FUNC_LEAVE(NULL);
}

child_size = bulki_entity_wire_size(src);
bulki_entity_array_ensure_capacity(dest, sizeof(BULKI_Entity));
memcpy((BULKI_Entity *)dest->data + dest->count, src, sizeof(BULKI_Entity));
dest->size += child_size;
dest->count++;

FUNC_LEAVE(dest);
}

BULKI_Entity *
BULKI_ENTITY_get_BULKI_Entity(BULKI_Entity *bulki_entity, size_t idx)
{
Expand Down Expand Up @@ -280,6 +435,10 @@ BULKI_ENTITY(void *data, uint64_t count, pdc_c_var_type_t pdc_type, pdc_c_var_cl
bulki_entity->data = data;
}
else {
// note: BULKI_ENTITY currently deep-copies payload into BULKI-owned memory.
// note: this copy can be avoided by introducing explicit pointer ownership semantics
// note: (e.g., borrowed/non-owned data mode) so BULKI can reference external buffers
// note: without freeing them in BULKI_Entity_free().
bulki_entity->data = PDC_calloc(1, size);
memcpy(bulki_entity->data, data, size);
}
Expand Down Expand Up @@ -317,7 +476,7 @@ BULKI_init(int initial_field_count)
buiki->data = PDC_calloc(1, sizeof(BULKI_Data));
buiki->data->values = PDC_calloc(buiki->capacity, sizeof(BULKI_Entity));
buiki->data->dataSize = 0;
get_BULKI_size(buiki);
bulki_refresh_total_size(buiki);

FUNC_LEAVE(buiki);
}
Expand Down Expand Up @@ -434,6 +593,45 @@ BULKI_put(BULKI *bulki, BULKI_Entity *key, BULKI_Entity *value)
FUNC_LEAVE_VOID();
}

void
BULKI_put_incremental(BULKI *bulki, BULKI_Entity *key, BULKI_Entity *value)
{
FUNC_ENTER(NULL);

if (bulki == NULL || key == NULL || value == NULL) {
LOG_ERROR("Error: bulki, key, or value is NULL\n");
FUNC_LEAVE_VOID();
}
// search for existing key
BULKI_Entity *existing_value = BULKI_get(bulki, key);
if (existing_value != NULL) {
bulki->header->headerSize -= key->size;
bulki->data->dataSize -= existing_value->size;
memcpy(existing_value, value, sizeof(BULKI_Entity));
bulki->header->headerSize += key->size;
bulki->data->dataSize += value->size;
bulki_refresh_total_size(bulki);
FUNC_LEAVE_VOID();
}
if (bulki->numKeys >= bulki->capacity) {
bulki->capacity *= 2;
bulki->header->keys = PDC_realloc(bulki->header->keys, bulki->capacity * sizeof(BULKI_Entity));
bulki->data->values = PDC_realloc(bulki->data->values, bulki->capacity * sizeof(BULKI_Entity));
}
memcpy(&bulki->header->keys[bulki->numKeys], key, sizeof(BULKI_Entity));
// append bytes for type, size, and key
bulki->header->headerSize += key->size;

memcpy(&bulki->data->values[bulki->numKeys], value, sizeof(BULKI_Entity));
// append bytes for class, type, size, and data
bulki->data->dataSize += value->size;

bulki->numKeys++;
bulki_refresh_total_size(bulki);

FUNC_LEAVE_VOID();
}

BULKI_Entity *
BULKI_delete(BULKI *bulki, BULKI_Entity *key)
{
Expand All @@ -456,6 +654,38 @@ BULKI_delete(BULKI *bulki, BULKI_Entity *key)
FUNC_LEAVE(value);
}

BULKI_Entity *
BULKI_delete_incremental(BULKI *bulki, BULKI_Entity *key)
{
FUNC_ENTER(NULL);

if (bulki == NULL || key == NULL) {
LOG_ERROR("Error: bulki or key is NULL in BULKI_delete_incremental()\n");
FUNC_LEAVE(NULL);
}

BULKI_Entity *value = NULL;
size_t last_index = bulki->numKeys - 1;
for (size_t i = 0; i < bulki->numKeys; i++) {
if (BULKI_Entity_equal(&bulki->header->keys[i], key)) {
value = &bulki->data->values[i];
bulki->header->headerSize -= key->size;
bulki->data->dataSize -= value->size;
bulki->numKeys--;
if (i != last_index) {
memcpy(&bulki->header->keys[i], &bulki->header->keys[bulki->numKeys - 1],
sizeof(BULKI_Entity));
memcpy(&bulki->data->values[i], &bulki->data->values[bulki->numKeys - 1],
sizeof(BULKI_Entity));
}
break;
}
}
bulki_refresh_total_size(bulki);

FUNC_LEAVE(value);
}

BULKI_Entity_Iterator *
Bent_iterator_init(BULKI_Entity *array, void *filter, pdc_c_var_type_t filter_type)
{
Expand Down Expand Up @@ -624,31 +854,41 @@ BULKI_Entity_free(BULKI_Entity *bulk_entity, int free_struct)
for (size_t i = 0; i < bulk_entity->count; i++) {
BULKI_free(&bulki_array[i], 0);
}
#ifdef BUKLI_DEBUG_LOG
LOG_INFO("Freeing bulki_array 1\n");
#endif
bulki_array = NULL;
}
else if (bulk_entity->pdc_type == PDC_BULKI_ENT && bulk_entity->data != NULL) {
BULKI_Entity *bulki_entity_array = (BULKI_Entity *)bulk_entity->data;
for (size_t i = 0; i < bulk_entity->count; i++) {
BULKI_Entity_free(&bulki_entity_array[i], 0);
}
#ifdef BUKLI_DEBUG_LOG
LOG_INFO("Freeing bulki_array 2\n");
#endif
bulki_entity_array = NULL;
}
}
else if (bulk_entity->pdc_class == PDC_CLS_ITEM) {
if (bulk_entity->pdc_type == PDC_BULKI && bulk_entity->data != NULL) {
BULKI_free((BULKI *)bulk_entity->data, 0);
bulk_entity->data = NULL;
#ifdef BUKLI_DEBUG_LOG
LOG_INFO("Freeing bulki_item 1\n");
#endif
}
}
#ifdef BUKLI_DEBUG_LOG
LOG_INFO("Freeing bulk_entity\n");
#endif
if (bulk_entity->data != NULL) {
#ifdef BUKLI_DEBUG_LOG
LOG_INFO(
"bulki_entity->class: %d, bulki_entity->class: %d, bulki_entity->data: %p, bulki_entity: "
"%p\n",
bulk_entity->pdc_class, bulk_entity->pdc_type, bulk_entity->data, bulk_entity);
#endif
bulk_entity->data = (void *)PDC_free(bulk_entity->data);
bulk_entity->data = NULL;
}
Expand Down
Loading