From 3a2f099cc7947a22cae48761832c85e2afa3ab58 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Mon, 22 Sep 2025 06:50:24 +0000 Subject: [PATCH 01/20] DAOS-17322 ddb: DTX aggregation with ddb Integrate reviewers comments: - Simplify linked list management Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/dtx/tests/dts_count.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/dtx/tests/dts_count.c b/src/dtx/tests/dts_count.c index 3e6b823caf2..2c54641b1e3 100644 --- a/src/dtx/tests/dts_count.c +++ b/src/dtx/tests/dts_count.c @@ -48,19 +48,14 @@ test_setup(void **unused) for (i = 0; i < DBD_COUNT; i++) { D_ALLOC(mock_dbds[i], CELL_SIZE); assert_non_null(mock_dbds[i]); + mock_dbds_off[i] = umem_ptr2off(&mock_pool.vp_umm, mock_dbds[i]); } for (i = 0; i < DBD_COUNT; i++) { mock_dbds[i]->dbd_magic = DTX_CMT_BLOB_MAGIC; mock_dbds[i]->dbd_cap = DBD_BLOB_DF_CAP; - if (i == DBD_COUNT - 1) - mock_dbds[i]->dbd_next = UMOFF_NULL; - else - mock_dbds[i]->dbd_next = umem_ptr2off(&mock_pool.vp_umm, mock_dbds[i + 1]); - if (i == 0) - mock_dbds[i]->dbd_prev = UMOFF_NULL; - else - mock_dbds[i]->dbd_prev = umem_ptr2off(&mock_pool.vp_umm, mock_dbds[i - 1]); + mock_dbds[i]->dbd_next = (i == DBD_COUNT - 1) ? UMOFF_NULL : mock_dbds_off[i + 1]; + mock_dbds[i]->dbd_prev = (i == 0) ? UMOFF_NULL : mock_dbds_off[i - 1]; mock_dbds[i]->dbd_count = (i + 1) * 2; assert_true(mock_dbds[i]->dbd_count <= DBD_BLOB_DF_CAP); From 22ab8bf75d9ee2c2fa8c8218b2ed266d867e3425 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Wed, 30 Apr 2025 13:17:44 +0000 Subject: [PATCH 02/20] DAOS-17322 ddb: DTX aggregation with ddb Add the following new command to ddb: - dtx_aggr: Aggregate DTX entries Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/control/cmd/ddb/commands_wrapper.go | 24 + src/control/cmd/ddb/ddb_commands.go | 21 + src/dtx/dtx_common.c | 2 +- src/dtx/tests/SConscript | 7 +- src/dtx/tests/dts_aggregate.c | 654 ++++++++++++++++++++ src/dtx/tests/dtx_ut.c | 3 + src/include/daos_srv/vos.h | 7 +- src/utils/ddb/ddb.c | 102 ++- src/utils/ddb/ddb.h | 13 + src/utils/ddb/ddb_commands.c | 62 ++ src/utils/ddb/tests/ddb_cmd_options_tests.c | 34 + src/utils/ddb/tests/ddb_commands_tests.c | 201 ++++++ src/vos/tests/vts_dtx.c | 2 +- src/vos/vos_dtx.c | 156 +++-- 14 files changed, 1219 insertions(+), 69 deletions(-) create mode 100644 src/dtx/tests/dts_aggregate.c diff --git a/src/control/cmd/ddb/commands_wrapper.go b/src/control/cmd/ddb/commands_wrapper.go index 95d7b8aff86..690fcc70c45 100644 --- a/src/control/cmd/ddb/commands_wrapper.go +++ b/src/control/cmd/ddb/commands_wrapper.go @@ -8,6 +8,7 @@ package main import ( + "math" "runtime" "unsafe" @@ -299,3 +300,26 @@ func ddbDtxStat(ctx *DdbContext, path string) error { /* Run the c code command */ return daosError(C.ddb_run_dtx_stat(&ctx.ctx, &options)) } + +func ddbDtxAggr(ctx *DdbContext, path string, epoch uint64, date string) error { + if epoch != math.MaxUint64 && date != "" { + return daosError(C.DER_INVAL) + } + + /* Set up the options */ + options := C.struct_dtx_aggr_options{} + options.path = C.CString(path) + defer freeString(options.path) + options.format = C.DDB_DTX_AGGR_NOW + if epoch != math.MaxUint64 { + options.format = C.DDB_DTX_AGGR_EPOCH + options.epoch = C.uint64_t(epoch) + } + if date != "" { + options.format = C.DDB_DTX_AGGR_DATE + options.date = C.CString(date) + defer freeString(options.date) + } + /* Run the c code command */ + return daosError(C.ddb_run_dtx_aggr(&ctx.ctx, &options)) +} diff --git a/src/control/cmd/ddb/ddb_commands.go b/src/control/cmd/ddb/ddb_commands.go index 27968cf5fec..3b78ea01be8 100644 --- a/src/control/cmd/ddb/ddb_commands.go +++ b/src/control/cmd/ddb/ddb_commands.go @@ -8,6 +8,8 @@ package main import ( + "math" + "github.com/desertbit/grumble" ) @@ -396,4 +398,23 @@ the path must include the extent, otherwise, it must not.`, }, Completer: nil, }) + // Command dtx_aggr + app.AddCommand(&grumble.Command{ + Name: "dtx_aggr", + Aliases: nil, + Help: "Aggregate DTX entries", + LongHelp: "Aggregate DTX entries until a given epoch or date", + HelpGroup: "vos", + Args: func(a *grumble.Args) { + a.String("path", "VOS tree path to aggregate.") + }, + Flags: func(f *grumble.Flags) { + f.Uint64("e", "epoch", math.MaxUint64, "Max aggregation epoch") + f.String("d", "date", "", "Max aggregation date (format '1970-01-01 00:00:00')") + }, + Run: func(c *grumble.Context) error { + return ddbDtxAggr(ctx, c.Args.String("path"), c.Flags.Uint64("epoch"), c.Flags.String("date")) + }, + Completer: nil, + }) } diff --git a/src/dtx/dtx_common.c b/src/dtx/dtx_common.c index 0c94d3cc713..b4c5ec3369c 100644 --- a/src/dtx/dtx_common.c +++ b/src/dtx/dtx_common.c @@ -430,7 +430,7 @@ dtx_aggregate(void *arg) struct dtx_stat stat = { 0 }; int rc; - rc = vos_dtx_aggregate(cont->sc_hdl); + rc = vos_dtx_aggregate(cont->sc_hdl, NULL); if (rc != 0) break; diff --git a/src/dtx/tests/SConscript b/src/dtx/tests/SConscript index 1a97b9bdff0..295319c4b01 100644 --- a/src/dtx/tests/SConscript +++ b/src/dtx/tests/SConscript @@ -51,6 +51,10 @@ def scons(): tenv.AppendUnique(LINKFLAGS=['-Wl,--wrap=vos_irec_is_valid']) tenv.AppendUnique(LINKFLAGS=['-Wl,--wrap=evt_desc_is_valid']) tenv.AppendUnique(LINKFLAGS=['-Wl,--wrap=dbtree_lookup']) + tenv.AppendUnique(LINKFLAGS=['-Wl,--wrap=vos_tls_get']) + tenv.AppendUnique(LINKFLAGS=['-Wl,--wrap=lrua_array_aggregate']) + tenv.AppendUnique(LINKFLAGS=['-Wl,--wrap=dbtree_delete']) + tenv.AppendUnique(LINKFLAGS=['-Wl,--wrap=d_tm_dec_gauge']) vos_src = Glob('../../vos/*.c') @@ -61,7 +65,8 @@ def scons(): 'sched_mock.c', 'srv_mock.c', 'srv_module_mock.c', - 'dts_count.c' + 'dts_count.c', + 'dts_aggregate.c' ] dtx_ut = tenv.d_program('dtx_ut', test_src + vos_src, LIBS=libraries) diff --git a/src/dtx/tests/dts_aggregate.c b/src/dtx/tests/dts_aggregate.c new file mode 100644 index 00000000000..13e93f109f2 --- /dev/null +++ b/src/dtx/tests/dts_aggregate.c @@ -0,0 +1,654 @@ +/** + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ +#define D_LOGFAC DD_FAC(tests) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ilog.h" +#include "vos_layout.h" +#include "vos_internal.h" + +/* mocks */ + +#define DTX_CMT_BLOB_MAGIC 0x2502191c +#define DBD_BLOB_DF_CAP 0x8 +#define EPOCH_START 0x1000 +#define EPOCH_STEP 0x10 +#define MOCK_UMM_TX_MAX 0x100 + +struct mock_umm_tx { + bool in_tx; + size_t count; + struct { + void *target; + void *snapshot; + size_t size; + } tx[MOCK_UMM_TX_MAX]; +}; + +static struct vos_pool Pool; +static struct vos_container Cont; +static struct vos_cont_df ContDf; +static struct vos_tls Tls; +static struct vos_dtx_blob_df *Dbd = NULL; +static umem_off_t DbdOff; +static struct btr_instance Btr; +static daos_handle_t Coh; +static struct mock_umm_tx MockUmmTx; + +struct vos_tls * +__wrap_vos_tls_get(bool standalone) +{ + return mock_ptr_type(struct vos_tls *); +} + +void +__wrap_lrua_array_aggregate(struct lru_array *array) +{ + /* nop */ + return; +} + +int +__wrap_dbtree_delete(daos_handle_t toh, dbtree_probe_opc_t opc, d_iov_t *key, void *args) +{ + assert_int_equal(toh.cookie, Cont.vc_dtx_committed_hdl.cookie); + assert_int_equal(opc, BTR_PROBE_EQ); + assert_non_null(key); + assert_null(args); + return mock(); +} + +void +__wrap_d_tm_dec_gauge(struct d_tm_node_t *metric, uint64_t value) +{ + check_expected_ptr(metric); + check_expected(value); +} + +static int +tx_begin(struct umem_instance *umm, struct umem_tx_stage_data *txd) +{ + int rc; + + assert_ptr_equal(umm, &Pool.vp_umm); + assert_null(txd); + assert_int_equal(MockUmmTx.count, 0); + rc = mock(); + MockUmmTx.in_tx = (rc == 0); + return rc; +} + +static int +tx_commit(struct umem_instance *umm, void *data) +{ + int i; + int rc; + + assert_ptr_equal(umm, &Pool.vp_umm); + assert_null(data); + assert_true(MockUmmTx.in_tx); + rc = mock(); + for (i = 0; i < MockUmmTx.count; i++) { + if (rc != 0) + memcpy(MockUmmTx.tx[i].target, MockUmmTx.tx[i].snapshot, + MockUmmTx.tx[i].size); + D_FREE(MockUmmTx.tx[i].snapshot); + } + MockUmmTx.count = 0; + MockUmmTx.in_tx = false; + return rc; +} + +static int +tx_abort(struct umem_instance *umm, int error) +{ + int i; + + assert_ptr_equal(umm, &Pool.vp_umm); + check_expected(error); + assert_true(MockUmmTx.in_tx); + for (i = 0; i < MockUmmTx.count; i++) { + memcpy(MockUmmTx.tx[i].target, MockUmmTx.tx[i].snapshot, MockUmmTx.tx[i].size); + D_FREE(MockUmmTx.tx[i].snapshot); + } + MockUmmTx.count = 0; + MockUmmTx.in_tx = false; + if (error) { + return error; + } + return mock(); +} + +static int +tx_add_ptr(struct umem_instance *umm, void *ptr, size_t ptr_size) +{ + assert_ptr_equal(umm, &Pool.vp_umm); + check_expected_ptr(ptr); + check_expected(ptr_size); + D_ALLOC(MockUmmTx.tx[MockUmmTx.count].snapshot, ptr_size); + assert_non_null(MockUmmTx.tx[MockUmmTx.count].snapshot); + memcpy(MockUmmTx.tx[MockUmmTx.count].snapshot, ptr, ptr_size); + MockUmmTx.tx[MockUmmTx.count].target = ptr; + MockUmmTx.tx[MockUmmTx.count].size = ptr_size; + MockUmmTx.count++; + return mock(); +} + +static int +tx_free(struct umem_instance *umm, umem_off_t umoff) +{ + assert_ptr_equal(umm, &Pool.vp_umm); + check_expected(umoff); + assert_true(MockUmmTx.in_tx); + return mock(); +} + +/* Helpers */ + +static void +prep_dtx_entries(size_t count) +{ + int i; + daos_epoch_t epoch = EPOCH_START; + + for (i = 0; i < count; i++) { + struct vos_dtx_cmt_ent_df *dce_df; + + dce_df = &Dbd->dbd_committed_data[i]; + dce_df->dce_epoch = epoch; + + epoch += EPOCH_STEP; + } + Dbd->dbd_count = count; + Cont.vc_dtx_committed_count = count; + Pool.vp_dtx_committed_count = count; +} + +static void +check_rollback(size_t count) +{ + int i; + + assert_int_equal(Dbd->dbd_count, count); + for (i = 0; i < count; i++) + assert_int_equal(Dbd->dbd_committed_data[i].dce_epoch, + EPOCH_START + i * EPOCH_STEP); + assert_int_equal(ContDf.cd_newest_aggregated, 0); + assert_int_equal(ContDf.cd_dtx_committed_head, umem_ptr2off(&Pool.vp_umm, Dbd)); + assert_int_equal(ContDf.cd_dtx_committed_tail, umem_ptr2off(&Pool.vp_umm, Dbd)); + assert_int_equal(Cont.vc_dtx_committed_count, count); + assert_int_equal(Pool.vp_dtx_committed_count, count); + assert_int_equal(Cont.vc_cmt_dtx_reindex_pos, umem_ptr2off(&Pool.vp_umm, Dbd)); +} + +/* setup & teardown */ + +static umem_ops_t umm_ops = {.mo_tx_begin = tx_begin, + .mo_tx_commit = tx_commit, + .mo_tx_abort = tx_abort, + .mo_tx_add_ptr = tx_add_ptr, + .mo_tx_free = tx_free}; + +static int +test_setup(void **unused) +{ + memset(&Pool, 0, sizeof(Pool)); + memset(&Cont, 0, sizeof(Cont)); + memset(&ContDf, 0, sizeof(ContDf)); + memset(&Tls, 0, sizeof(Tls)); + memset(&MockUmmTx, 0, sizeof(MockUmmTx)); + + D_ALLOC(Dbd, sizeof(struct vos_dtx_blob_df) + + DBD_BLOB_DF_CAP * sizeof(struct vos_dtx_cmt_ent_df)); + assert_non_null(Dbd); + Dbd->dbd_magic = DTX_CMT_BLOB_MAGIC; + Dbd->dbd_cap = DBD_BLOB_DF_CAP; + Dbd->dbd_next = UMOFF_NULL; + DbdOff = umem_ptr2off(&Pool.vp_umm, Dbd); + + Pool.vp_umm.umm_ops = &umm_ops; + Cont.vc_pool = &Pool; + Cont.vc_cont_df = &ContDf; + Cont.vc_cmt_dtx_reindex_pos = DbdOff; + Cont.vc_dtx_committed_hdl.cookie = (uint64_t)&Btr; + ContDf.cd_dtx_committed_head = DbdOff; + ContDf.cd_dtx_committed_tail = DbdOff; + Coh.cookie = (uint64_t)&Cont; + + return 0; +} + +static int +test_teardown(void **unused) +{ + assert_int_equal(MockUmmTx.count, 0); + D_FREE(Dbd); + + return 0; +} + +/* tests */ + +static void +test_asserts(void **unused) +{ + daos_handle_t hdl_null = {0}; + + /* Missing argument. */ + expect_assert_failure(vos_dtx_aggregate(hdl_null, NULL)); + + /* Invalid Telemetry global variable. */ + will_return(__wrap_vos_tls_get, NULL); + expect_assert_failure(vos_dtx_aggregate(Coh, NULL)); + + /* Invalid pool type. */ + Pool.vp_sysdb = true; + Dbd->dbd_count = 1; + will_return(__wrap_vos_tls_get, &Tls); + expect_assert_failure(vos_dtx_aggregate(Coh, NULL)); +} + +/* Can not start a PMEM transtation */ +static void +test_tx_begin_error(void **unused) +{ + const size_t dtx_count = 5; + int rc; + + will_return(__wrap_vos_tls_get, &Tls); + will_return(tx_begin, -DER_UNKNOWN); + prep_dtx_entries(dtx_count); + + rc = vos_dtx_aggregate(Coh, NULL); + assert_rc_equal(rc, -DER_UNKNOWN); + check_rollback(dtx_count); +} + +/* DAOS B-tree delete failure */ +static void +test_dbtree_delete_error(void **unused) +{ + const size_t dtx_count = 5; + int i; + int rc; + + will_return(__wrap_vos_tls_get, &Tls); + will_return(tx_begin, 0); + prep_dtx_entries(dtx_count); + for (i = 0; i < 3; i++) { + will_return(__wrap_dbtree_delete, 0); + } + will_return(__wrap_dbtree_delete, -DER_UNKNOWN); + expect_value(tx_abort, error, -DER_UNKNOWN); + + rc = vos_dtx_aggregate(Coh, NULL); + assert_rc_equal(rc, -DER_UNKNOWN); + check_rollback(dtx_count); +} + +/* Update of newest aggregated epoch failure */ +static void +test_newest_aggregated_error(void **unused) +{ + const size_t dtx_count = 5; + int i; + int rc; + + will_return(__wrap_vos_tls_get, &Tls); + will_return(tx_begin, 0); + prep_dtx_entries(dtx_count); + for (i = 0; i < dtx_count; i++) { + will_return(__wrap_dbtree_delete, 0); + } + expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + will_return(tx_add_ptr, -DER_UNKNOWN); + expect_value(tx_abort, error, -DER_UNKNOWN); + + rc = vos_dtx_aggregate(Coh, NULL); + assert_rc_equal(rc, -DER_UNKNOWN); + check_rollback(dtx_count); +} + +/* Update of DTX blob list failure */ +static void +test_committed_tail_error(void **unused) +{ + const size_t dtx_count = 5; + int i; + int rc; + + will_return(__wrap_vos_tls_get, &Tls); + will_return(tx_begin, 0); + prep_dtx_entries(dtx_count); + for (i = 0; i < dtx_count; i++) { + will_return(__wrap_dbtree_delete, 0); + } + expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_tail); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_tail)); + will_return(tx_add_ptr, -DER_UNKNOWN); + expect_value(tx_abort, error, -DER_UNKNOWN); + + rc = vos_dtx_aggregate(Coh, NULL); + assert_rc_equal(rc, -DER_UNKNOWN); + check_rollback(dtx_count); +} + +/* Update of DTX blob list failure */ +static void +test_committed_head_error(void **unused) +{ + const size_t dtx_count = 5; + int i; + int rc; + + will_return(__wrap_vos_tls_get, &Tls); + will_return(tx_begin, 0); + prep_dtx_entries(dtx_count); + for (i = 0; i < dtx_count; i++) { + will_return(__wrap_dbtree_delete, 0); + } + expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_tail); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_tail)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_head); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_head)); + will_return(tx_add_ptr, -DER_UNKNOWN); + expect_value(tx_abort, error, -DER_UNKNOWN); + + rc = vos_dtx_aggregate(Coh, NULL); + assert_rc_equal(rc, -DER_UNKNOWN); + check_rollback(dtx_count); +} + +/* Pmem free failure */ +static void +test_umm_free_error(void **unused) +{ + const size_t dtx_count = 5; + int i; + int rc; + + will_return(__wrap_vos_tls_get, &Tls); + will_return(tx_begin, 0); + prep_dtx_entries(dtx_count); + for (i = 0; i < dtx_count; i++) { + will_return(__wrap_dbtree_delete, 0); + } + expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_tail); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_tail)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_head); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_head)); + will_return(tx_add_ptr, 0); + expect_value(tx_free, umoff, ContDf.cd_dtx_committed_head); + will_return(tx_free, -DER_UNKNOWN); + expect_value(tx_abort, error, -DER_UNKNOWN); + + rc = vos_dtx_aggregate(Coh, NULL); + assert_rc_equal(rc, -DER_UNKNOWN); + check_rollback(dtx_count); +} + +/* Update of committed DTX entries failure */ +static void +test_committed_data_error(void **unused) +{ + const daos_epoch_t ep_max = EPOCH_START + 2 * EPOCH_STEP; + const size_t dtx_count = 5; + int i; + int rc; + + will_return(__wrap_vos_tls_get, &Tls); + will_return(tx_begin, 0); + prep_dtx_entries(dtx_count); + for (i = 0; i < 3; i++) { + will_return(__wrap_dbtree_delete, -DER_NONEXIST); + } + expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &Dbd->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_committed_data[0]) * dtx_count); + will_return(tx_add_ptr, -DER_UNKNOWN); + expect_value(tx_abort, error, -DER_UNKNOWN); + + rc = vos_dtx_aggregate(Coh, &ep_max); + assert_rc_equal(rc, -DER_UNKNOWN); + check_rollback(dtx_count); +} + +/* Update of committed DTX entries count failure */ +static void +test_dbd_count_error(void **unused) +{ + const daos_epoch_t ep_max = EPOCH_START + 2 * EPOCH_STEP; + const size_t dtx_count = 5; + int i; + int rc; + + will_return(__wrap_vos_tls_get, &Tls); + will_return(tx_begin, 0); + prep_dtx_entries(dtx_count); + for (i = 0; i < 3; i++) { + will_return(__wrap_dbtree_delete, -DER_NONEXIST); + } + expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &Dbd->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_committed_data[0]) * dtx_count); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &Dbd->dbd_count); + expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_count)); + will_return(tx_add_ptr, -DER_UNKNOWN); + expect_value(tx_abort, error, -DER_UNKNOWN); + + rc = vos_dtx_aggregate(Coh, &ep_max); + assert_rc_equal(rc, -DER_UNKNOWN); + check_rollback(dtx_count); +} + +/* Pmem commit transaction failure */ +static void +test_umm_commit_error(void **unused) +{ + const daos_epoch_t ep_max = EPOCH_START + 2 * EPOCH_STEP; + const size_t dtx_count = 5; + int i; + int rc; + + will_return(__wrap_vos_tls_get, &Tls); + will_return(tx_begin, 0); + prep_dtx_entries(dtx_count); + for (i = 0; i < 3; i++) { + will_return(__wrap_dbtree_delete, -DER_NONEXIST); + } + expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &Dbd->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_committed_data[0]) * dtx_count); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &Dbd->dbd_count); + expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_count)); + will_return(tx_add_ptr, 0); + will_return(tx_commit, -DER_UNKNOWN); + + rc = vos_dtx_aggregate(Coh, &ep_max); + assert_rc_equal(rc, -DER_UNKNOWN); + check_rollback(dtx_count); +} + +/* Pool without DTX committed transaction */ +static void +test_empty(void **unused) +{ + int rc; + + will_return(__wrap_vos_tls_get, &Tls); + + /* No committed DTX entries to aggregate. */ + rc = vos_dtx_aggregate(Coh, NULL); + assert_rc_equal(rc, 0); +} + +/* Aggregation of all the 5 committed DTX transactions */ +static void +test_5_entries(void **unused) +{ + const size_t dtx_count = 5; + int i; + int rc; + + will_return(__wrap_vos_tls_get, &Tls); + will_return(tx_begin, 0); + prep_dtx_entries(dtx_count); + for (i = 0; i < dtx_count; i++) { + will_return(__wrap_dbtree_delete, 0); + } + expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_tail); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_tail)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_head); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_head)); + will_return(tx_add_ptr, 0); + expect_value(tx_free, umoff, ContDf.cd_dtx_committed_head); + will_return(tx_free, 0); + will_return(tx_commit, 0); + expect_value(__wrap_d_tm_dec_gauge, metric, Tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, dtx_count); + + rc = vos_dtx_aggregate(Coh, NULL); + assert_rc_equal(rc, 0); + assert_int_equal(ContDf.cd_newest_aggregated, EPOCH_START + (dtx_count - 1) * EPOCH_STEP); + assert_int_equal(ContDf.cd_dtx_committed_head, UMOFF_NULL); + assert_int_equal(ContDf.cd_dtx_committed_tail, UMOFF_NULL); + assert_int_equal(Cont.vc_dtx_committed_count, 0); + assert_int_equal(Pool.vp_dtx_committed_count, 0); + assert_int_equal(Cont.vc_cmt_dtx_reindex_pos, UMOFF_NULL); +} + +/* Aggregation of 3 committed DTX transactions */ +static void +test_3_entries(void **unused) +{ + const daos_epoch_t ep_max = EPOCH_START + 2 * EPOCH_STEP; + const size_t dtx_count = 5; + int i; + int rc; + + will_return(__wrap_vos_tls_get, &Tls); + will_return(tx_begin, 0); + prep_dtx_entries(dtx_count); + for (i = 0; i < 3; i++) { + will_return(__wrap_dbtree_delete, -DER_NONEXIST); + } + expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &Dbd->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_committed_data[0]) * dtx_count); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &Dbd->dbd_count); + expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_count)); + will_return(tx_add_ptr, 0); + will_return(tx_commit, 0); + expect_value(__wrap_d_tm_dec_gauge, metric, Tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, 3); + + rc = vos_dtx_aggregate(Coh, &ep_max); + assert_rc_equal(rc, 0); + assert_int_equal(Dbd->dbd_count, 2); + assert_int_equal(Dbd->dbd_committed_data[0].dce_epoch, EPOCH_START + 3 * EPOCH_STEP); + assert_int_equal(Dbd->dbd_committed_data[1].dce_epoch, EPOCH_START + 4 * EPOCH_STEP); + assert_int_equal(ContDf.cd_newest_aggregated, EPOCH_START + 2 * EPOCH_STEP); + assert_int_equal(ContDf.cd_dtx_committed_head, DbdOff); + assert_int_equal(ContDf.cd_dtx_committed_tail, DbdOff); + assert_int_equal(Cont.vc_dtx_committed_count, 2); + assert_int_equal(Pool.vp_dtx_committed_count, 2); + assert_int_equal(Cont.vc_cmt_dtx_reindex_pos, DbdOff); +} + +/* Aggregation of 0 committed DTX transactions */ +static void +test_0_entry(void **unused) +{ + const daos_epoch_t ep_max = EPOCH_START - 1; + const size_t dtx_count = 5; + int rc; + + will_return(__wrap_vos_tls_get, &Tls); + will_return(tx_begin, 0); + prep_dtx_entries(dtx_count); + expect_value(tx_add_ptr, ptr, &Dbd->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_committed_data[0]) * dtx_count); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &Dbd->dbd_count); + expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_count)); + will_return(tx_add_ptr, 0); + will_return(tx_commit, 0); + expect_value(__wrap_d_tm_dec_gauge, metric, Tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, 0); + + rc = vos_dtx_aggregate(Coh, &ep_max); + assert_rc_equal(rc, 0); + check_rollback(dtx_count); +} + +/* clang-format off */ +/* compilation unit's entry point */ +#define TEST(name, func) {name ": vos_dtx_aggregate - " #func, func, test_setup, test_teardown} +/* clang-format on */ + +static const struct CMUnitTest vos_dtx_aggregate_tests_all[] = { + TEST("DTX500", test_asserts), + TEST("DTX501", test_tx_begin_error), + TEST("DTX502", test_dbtree_delete_error), + TEST("DTX503", test_newest_aggregated_error), + TEST("DTX504", test_committed_tail_error), + TEST("DTX505", test_committed_head_error), + TEST("DTX506", test_umm_free_error), + TEST("DTX507", test_committed_data_error), + TEST("DTX508", test_dbd_count_error), + TEST("DTX509", test_umm_commit_error), + TEST("DTX550", test_empty), + TEST("DTX551", test_5_entries), + TEST("DTX552", test_3_entries), + TEST("DTX554", test_0_entry), +}; + +int +run_dtx_aggregate_tests(void) +{ + const char *test_name = "vos_dtx_aggregate"; + + return cmocka_run_group_tests_name(test_name, vos_dtx_aggregate_tests_all, NULL, NULL); +} diff --git a/src/dtx/tests/dtx_ut.c b/src/dtx/tests/dtx_ut.c index dcccb713161..630ba53de42 100644 --- a/src/dtx/tests/dtx_ut.c +++ b/src/dtx/tests/dtx_ut.c @@ -23,6 +23,8 @@ int run_discard_invalid_tests(void); int run_dtx_count_tests(void); +int +run_dtx_aggregate_tests(void); static void print_usage() @@ -43,6 +45,7 @@ run_all_tests(int keys) failed += run_structs_tests(); failed += run_discard_invalid_tests(); failed += run_dtx_count_tests(); + failed += run_dtx_aggregate_tests(); return failed; } diff --git a/src/include/daos_srv/vos.h b/src/include/daos_srv/vos.h index 1b798b5c4c9..91fe6f52630 100644 --- a/src/include/daos_srv/vos.h +++ b/src/include/daos_srv/vos.h @@ -235,12 +235,13 @@ vos_dtx_set_flags(daos_handle_t coh, struct dtx_id dtis[], int count, uint32_t f /** * Aggregate the committed DTXs. * - * \param coh [IN] Container open handle. + * \param coh [IN] Container open handle. + * \param ep_max [IN] The upper epoch to aggregate. * - * \return Zero on success, negative value if error. + * \return Zero on success, negative value if error. */ int -vos_dtx_aggregate(daos_handle_t coh); +vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max); /** * Query the container's DTXs statistics information. diff --git a/src/utils/ddb/ddb.c b/src/utils/ddb/ddb.c index fb5f5021004..e0f79123155 100644 --- a/src/utils/ddb/ddb.c +++ b/src/utils/ddb/ddb.c @@ -46,6 +46,7 @@ #define COMMAND_NAME_DEV_LIST "dev_list" #define COMMAND_NAME_DEV_REPLACE "dev_replace" #define COMMAND_NAME_DTX_STAT "dtx_stat" +#define COMMAND_NAME_DTX_AGGR "dtx_aggr" /* Parse command line options for the 'ls' command */ static int @@ -853,6 +854,82 @@ dtx_stat_option_parse(struct ddb_ctx *ctx, struct dtx_stat_options *cmd_args, ui return 0; } +/* Parse command line options for the 'dtx_aggr' command */ +static int +dtx_aggr_option_parse(struct ddb_ctx *ctx, struct dtx_aggr_options *cmd_args, uint32_t argc, + char **argv) +{ + int opt; + char *options_short = "e:d:"; + int index = 0; + char *endptr; + struct option options_long[] = {{"epoch", required_argument, NULL, 'e'}, + {"date", required_argument, NULL, 'd'}, + {NULL}}; + + memset(cmd_args, 0, sizeof(*cmd_args)); + /* Restart getopt */ + optind = 1; + opterr = 0; + cmd_args->format = DDB_DTX_AGGR_NOW; + while ((opt = getopt_long(argc, argv, options_short, options_long, &index)) != -1) { + switch (opt) { + case 'e': + if (cmd_args->format == DDB_DTX_AGGR_DATE) { + ddb_error( + ctx, "'--epoch' and '--date' options are mutually exclusive\n"); + return -DER_INVAL; + } + if (cmd_args->format == DDB_DTX_AGGR_EPOCH) { + ddb_error(ctx, "'--epoch' option can not be used multiple time\n"); + return -DER_INVAL; + } + errno = 0; + cmd_args->epoch = strtoull(optarg, &endptr, 10); + if (errno != 0 || endptr == optarg || *endptr != '\0') { + ddb_error(ctx, "'--epoch' option arg epoch format is invalid\n"); + return -DER_INVAL; + } + cmd_args->format = DDB_DTX_AGGR_EPOCH; + break; + case 'd': + if (cmd_args->format == DDB_DTX_AGGR_EPOCH) { + ddb_error( + ctx, "'--epoch' and '--date' options are mutually exclusive\n"); + return -DER_INVAL; + } + if (cmd_args->format == DDB_DTX_AGGR_DATE) { + ddb_error(ctx, "'--date' option can not be used multiple time\n"); + return -DER_INVAL; + } + cmd_args->date = optarg; + cmd_args->format = DDB_DTX_AGGR_DATE; + break; + case '?': + ddb_printf(ctx, "Unknown option: '%c'\n", optopt); + break; + default: + return -DER_INVAL; + } + } + + index = optind; + if (argc - index > 0) { + cmd_args->path = argv[index]; + index++; + } else { + ddb_error(ctx, "Expected argument 'path'\n"); + return -DER_INVAL; + } + + if (argc - index > 0) { + ddb_errorf(ctx, "Unexpected argument: %s\n", argv[index]); + return -DER_INVAL; + } + + return 0; +} + int ddb_parse_cmd_args(struct ddb_ctx *ctx, uint32_t argc, char **argv, struct ddb_cmd_info *info) { @@ -979,6 +1056,11 @@ ddb_parse_cmd_args(struct ddb_ctx *ctx, uint32_t argc, char **argv, struct ddb_c return dtx_stat_option_parse(ctx, &info->dci_cmd_option.dci_dtx_stat, argc, argv); } + if (same(cmd, COMMAND_NAME_DTX_AGGR)) { + info->dci_cmd = DDB_CMD_DTX_AGGR; + return dtx_aggr_option_parse(ctx, &info->dci_cmd_option.dci_dtx_aggr, argc, argv); + } + ddb_errorf(ctx, "'%s' is not a valid command. Available commands are:" "'help', " @@ -1005,7 +1087,8 @@ ddb_parse_cmd_args(struct ddb_ctx *ctx, uint32_t argc, char **argv, struct ddb_c "'rm_pool', " "'dev_list', " "'dev_replace', " - "'dtx_stat'\n", + "'dtx_stat', " + "'dtx_aggr'\n", cmd); return -DER_INVAL; @@ -1185,6 +1268,10 @@ ddb_run_cmd(struct ddb_ctx *ctx, const char *cmd_str) rc = ddb_run_dtx_stat(ctx, &info.dci_cmd_option.dci_dtx_stat); break; + case DDB_CMD_DTX_AGGR: + rc = ddb_run_dtx_aggr(ctx, &info.dci_cmd_option.dci_dtx_aggr); + break; + case DDB_CMD_UNKNOWN: ddb_error(ctx, "Unknown command\n"); rc = -DER_INVAL; @@ -1397,6 +1484,18 @@ ddb_commands_help(struct ddb_ctx *ctx) ddb_print(ctx, " [path]\n"); ddb_print(ctx, "\tOptional, VOS tree path to query.\n"); ddb_print(ctx, "\n"); + + /* Command: dtx_aggr */ + ddb_print(ctx, "dtx_aggr \n"); + ddb_print(ctx, "\tAggregate DTX entries until a given timestamp or duration.\n"); + ddb_print(ctx, " \n"); + ddb_print(ctx, "\tVOS tree path to aggregate.\n"); + ddb_print(ctx, "Options:\n"); + ddb_print(ctx, " -e, --epoch\n"); + ddb_print(ctx, "\tMax aggregation epoch\n"); + ddb_print(ctx, " -d, --date\n"); + ddb_print(ctx, "\tMax aggregation date (format '1970-01-01 00:00:00')\n"); + ddb_print(ctx, "\n"); } void @@ -1465,4 +1564,5 @@ ddb_program_help(struct ddb_ctx *ctx) ddb_print(ctx, " dev_list List all devices\n"); ddb_print(ctx, " dev_replace Replace an old device with a new unused device\n"); ddb_print(ctx, " dtx_stat Stat on DTX entries\n"); + ddb_print(ctx, " dtx_aggr Aggregate DTX entries\n"); } diff --git a/src/utils/ddb/ddb.h b/src/utils/ddb/ddb.h index 8cbf80bd385..d6b959b12b8 100644 --- a/src/utils/ddb/ddb.h +++ b/src/utils/ddb/ddb.h @@ -122,6 +122,7 @@ enum ddb_cmd { DDB_CMD_DEV_LIST = 24, DDB_CMD_DEV_REPLACE = 25, DDB_CMD_DTX_STAT = 26, + DDB_CMD_DTX_AGGR = 27, }; /* option and argument structures for commands that need them */ @@ -214,6 +215,15 @@ struct dtx_stat_options { char *path; }; +enum dtx_aggr_format { DDB_DTX_AGGR_NOW = 0, DDB_DTX_AGGR_EPOCH = 1, DDB_DTX_AGGR_DATE = 2 }; + +struct dtx_aggr_options { + char *path; + enum dtx_aggr_format format; + uint64_t epoch; + char *date; +}; + struct ddb_cmd_info { enum ddb_cmd dci_cmd; union { @@ -235,6 +245,7 @@ struct ddb_cmd_info { struct dev_list_options dci_dev_list; struct dev_replace_options dci_dev_replace; struct dtx_stat_options dci_dtx_stat; + struct dtx_aggr_options dci_dtx_aggr; } dci_cmd_option; }; @@ -302,6 +313,8 @@ int ddb_run_dev_replace(struct ddb_ctx *ctx, struct dev_replace_options *opt); int ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt); +int +ddb_run_dtx_aggr(struct ddb_ctx *ctx, struct dtx_aggr_options *opt); void ddb_program_help(struct ddb_ctx *ctx); diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index 1ff733b3162..596cfe9435d 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -1451,3 +1451,65 @@ ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt) return rc; } + +int +ddb_run_dtx_aggr(struct ddb_ctx *ctx, struct dtx_aggr_options *opt) +{ + struct dv_indexed_tree_path itp = {0}; + daos_handle_t coh = {0}; + uint64_t *epoch; + int rc; + + if (!ctx->dc_write_mode) { + ddb_error(ctx, error_msg_write_mode_only); + return -DER_INVAL; + } + + rc = init_path(ctx, opt->path, &itp); + if (!SUCCESS(rc)) + goto done; + + if (!itp_has_cont(&itp)) { + rc = -DER_INVAL; + goto done; + } + + rc = dv_cont_open(ctx->dc_poh, itp_cont(&itp), &coh); + if (!SUCCESS(rc)) + goto done; + + switch (opt->format) { + case DDB_DTX_AGGR_NOW: + epoch = NULL; + break; + case DDB_DTX_AGGR_DATE: + rc = ddb_date2epoch(optarg, &opt->epoch); + if (!SUCCESS(rc)) { + ddb_error(ctx, "'--date' option arg date format is invalid\n"); + goto done; + } + case DDB_DTX_AGGR_EPOCH: + epoch = &opt->epoch; + break; + default: + D_ASSERT(false && "Invalid dtx_aggr options"); + } + + ddb_print(ctx, "Starting DTX entries aggregation of "); + itp_print_full(ctx, &itp); + ddb_print(ctx, "\n"); + rc = vos_dtx_aggregate(coh, epoch); + if (!SUCCESS(rc)) { + ddb_errorf(ctx, "Aggregation of DTX entries failed: " DF_RC "\n", DP_RC(rc)); + goto done; + } + ddb_print(ctx, "Aggregation of DTX is done\n"); + + rc = 0; + +done: + itp_free(&itp); + dv_cont_close(&coh); + + return rc; +} diff --git a/src/utils/ddb/tests/ddb_cmd_options_tests.c b/src/utils/ddb/tests/ddb_cmd_options_tests.c index 199099abc77..7f347e31361 100644 --- a/src/utils/ddb/tests/ddb_cmd_options_tests.c +++ b/src/utils/ddb/tests/ddb_cmd_options_tests.c @@ -316,6 +316,39 @@ dtx_stat_parsing(void **state) assert_null(options->path); } +static void +dtx_aggr_parsing(void **state) +{ + struct ddb_cmd_info info = {0}; + struct dtx_aggr_options *options = &info.dci_cmd_option.dci_dtx_aggr; + + /* test invalid arguments and options */ + test_run_inval_cmd("dtx_aggr", "path", "extra"); + test_run_inval_cmd("dtx_aggr", "-z"); + test_run_inval_cmd("dtx_aggr", "-e", "foo", "path"); + test_run_inval_cmd("dtx_aggr", "-e", "666", "-e", "999", "path"); + test_run_inval_cmd("dtx_aggr", "-d", "1970-01-01 00:00:00", "-d", "2000-01-01 00:00:00", + "path"); + test_run_inval_cmd("dtx_aggr", "-e", "42", "-d", "1970-01-01 00:00:00", "path"); + + /* test all arguments */ + test_run_cmd(&info, "dtx_aggr", "path"); + assert_non_null(options->path); + assert_null(options->epoch); + assert_null(options->date); + + /* test all options and arguments */ + test_run_cmd(&info, "dtx_aggr", "-e", "42", "path"); + assert_non_null(options->path); + assert_non_null(options->epoch); + assert_null(options->date); + + test_run_cmd(&info, "dtx_aggr", "-d", "1970-01-01 00:00:00", "path"); + assert_non_null(options->path); + assert_null(options->epoch); + assert_non_null(options->date); +} + /* * ----------------------------------------------- * Execute @@ -341,6 +374,7 @@ ddb_cmd_options_tests_run() TEST(dtx_act_commit_options_parsing), TEST(dtx_act_abort_options_parsing), TEST(dtx_stat_parsing), + TEST(dtx_aggr_parsing), }; return cmocka_run_group_tests_name("DDB commands option parsing tests", tests, diff --git a/src/utils/ddb/tests/ddb_commands_tests.c b/src/utils/ddb/tests/ddb_commands_tests.c index 3e61649ec2b..bd36b13e05c 100644 --- a/src/utils/ddb/tests/ddb_commands_tests.c +++ b/src/utils/ddb/tests/ddb_commands_tests.c @@ -437,6 +437,206 @@ dtx_stat_tests(void **state) "^Number of committed DTX of the pool:[[:blank:]]+1$"); } +static uint64_t +get_epoch(const char *buf) +{ + size_t buf_len; + int idx; + uint64_t exp; + uint64_t epoch; + + buf_len = strlen(buf); + idx = buf_len - 1; + while (idx > 0 && (buf[idx] < '0' || buf[idx] > '9')) + --idx; + assert_true(idx > 0); + + exp = 1; + epoch = 0; + while (idx > 0 && buf[idx] >= '0' && buf[idx] <= '9') { + epoch += (buf[idx] - '0') * exp; + exp *= 10; + --idx; + } + assert_true(idx > 0); + assert_true(buf[idx] == ' '); + + return epoch; +} + +static void +print_regerror(int rc, regex_t *preg, const char *regex_str) +{ + char *buf; + size_t buf_size; + + buf_size = regerror(rc, preg, NULL, 0); + D_ALLOC_ARRAY(buf, buf_size); + assert_non_null(buf); + regerror(rc, preg, buf, buf_size); + /* XXX Needs of testing buf to avoid false positive error from gcc : '%s' directive argument + * is null [-Werror=format-overflow=] */ + if (buf) + print_error("ERROR: invalid regex '%s': %s\n", regex_str, buf); + D_FREE(buf); + if (preg != NULL) + regfree(preg); +} + +struct ddb_epochs { + size_t de_size; + uint64_t de_epochs[0]; +}; + +static int +cmp_epoch(const void *ep1, const void *ep2) +{ + return *(uint64_t *)ep1 - *(uint64_t *)ep2; +} + +static struct ddb_epochs * +get_committed_epochs(char *buf) +{ + const char *regex_str = "^[[:blank:]]+Epoch: [[:digit:]]+$"; + char *token; + regex_t preg; + struct ddb_epochs *epochs; + int rc; + + D_ALLOC_PTR(epochs); + assert_non_null(epochs); + memset(epochs, 0, sizeof(*epochs)); + + rc = regcomp(&preg, regex_str, REG_NOSUB | REG_EXTENDED); + if (rc != 0) { + print_regerror(rc, NULL, regex_str); + fail(); + } + token = strtok(buf, "\n"); + while (token != NULL) { + struct ddb_epochs *epochs_prev; + size_t epochs_size; + + rc = regexec(&preg, token, 0, NULL, 0); + if (rc == REG_NOMATCH) { + token = strtok(NULL, "\n"); + continue; + } + if (rc != 0) { + print_regerror(rc, &preg, regex_str); + fail(); + } + + ++epochs->de_size; + epochs_prev = epochs; + epochs_size = sizeof(*epochs) + sizeof(epochs->de_epochs[0]) * epochs->de_size; + D_REALLOC_NZ(epochs, epochs_prev, epochs_size); + epochs->de_epochs[epochs->de_size - 1] = get_epoch(token); + + token = strtok(NULL, "\n"); + } + regfree(&preg); + + qsort(&epochs->de_epochs[0], epochs->de_size, sizeof(epochs->de_epochs[0]), cmp_epoch); + + return epochs; +} + +static void +dtx_aggr_tests(void **state) +{ + uuid_t *p_uuid = &g_uuids[3]; + struct dt_vos_pool_ctx *tctx = *state; + struct ddb_ctx ctx = {0}; + struct dtx_dump_options opt_dump = {0}; + struct dtx_stat_options opt_stat = {0}; + struct dtx_aggr_options opt_aggr = {0}; + daos_handle_t coh; + char buf[256]; + uint64_t epoch_last; + struct ddb_epochs *epochs; + + ctx.dc_poh = tctx->dvt_poh; + ctx.dc_io_ft.ddb_print_error = dvt_fake_print; + ctx.dc_io_ft.ddb_print_message = dvt_fake_print; + ctx.dc_write_mode = true; + + buf[0] = '/'; + uuid_unparse(*p_uuid, &buf[1]); + + /* Insert 8 mocked DTX entries */ + assert_success(vos_cont_open(tctx->dvt_poh, *p_uuid, &coh)); + dvt_vos_insert_dtx_records(coh, 10, 8); + assert_success(vos_cont_close(coh)); + + opt_stat.path = buf; + dvt_fake_print_reset(); + assert_success(ddb_run_dtx_stat(&ctx, &opt_stat)); + assert_regex_match( + dvt_fake_print_buffer, + "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+8$"); + assert_int_equal(get_epoch(dvt_fake_print_buffer), 0); + + /* Test aggregation without epoch (i.e. all 8 DTX entries) */ + opt_aggr.path = buf; + opt_aggr.format = DDB_DTX_AGGR_NOW; + assert_success(ddb_run_dtx_aggr(&ctx, &opt_aggr)); + + dvt_fake_print_reset(); + assert_success(ddb_run_dtx_stat(&ctx, &opt_stat)); + assert_regex_match( + dvt_fake_print_buffer, + "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+0$"); + epoch_last = get_epoch(dvt_fake_print_buffer); + assert_true(epoch_last > 0); + + /* Insert 10 mocked DTX entries */ + assert_success(vos_cont_open(tctx->dvt_poh, *p_uuid, &coh)); + dvt_vos_insert_dtx_records(coh, 10, 10); + assert_success(vos_cont_close(coh)); + + dvt_fake_print_reset(); + assert_success(ddb_run_dtx_stat(&ctx, &opt_stat)); + assert_regex_match( + dvt_fake_print_buffer, + "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+10$"); + assert_int_equal(get_epoch(dvt_fake_print_buffer), epoch_last); + + opt_dump.path = buf; + opt_dump.active = false; + opt_dump.committed = true; + dvt_fake_print_reset(); + assert_success(ddb_run_dtx_dump(&ctx, &opt_dump)); + epochs = get_committed_epochs(dvt_fake_print_buffer); + assert_int_equal(epochs->de_size, 10); + + /* Test aggregation with an epoch (i.e. aggregate 7 first DTX entries) */ + opt_aggr.format = DDB_DTX_AGGR_EPOCH; + opt_aggr.epoch = epochs->de_epochs[6]; + assert_success(ddb_run_dtx_aggr(&ctx, &opt_aggr)); + + dvt_fake_print_reset(); + assert_success(ddb_run_dtx_stat(&ctx, &opt_stat)); + assert_regex_match( + dvt_fake_print_buffer, + "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+3$"); + assert_int_equal(get_epoch(dvt_fake_print_buffer), epochs->de_epochs[6]); + assert_true(get_epoch(dvt_fake_print_buffer) > epoch_last); + + /* Test aggregation without epoch (i.e. aggregate last 3 DTX entries) */ + opt_aggr.format = DDB_DTX_AGGR_NOW; + assert_success(ddb_run_dtx_aggr(&ctx, &opt_aggr)); + + dvt_fake_print_reset(); + assert_success(ddb_run_dtx_stat(&ctx, &opt_stat)); + assert_regex_match( + dvt_fake_print_buffer, + "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+0$"); + assert_int_equal(get_epoch(dvt_fake_print_buffer), epochs->de_epochs[9]); + + D_FREE(epochs); +} + /* * -------------------------------------------------------------- * End test functions @@ -497,6 +697,7 @@ ddb_commands_tests_run() TEST(dtx_act_discard_invalid_tests), TEST(dtx_abort_entry_tests), TEST(feature_cmd_tests), + TEST(dtx_aggr_tests), }; return cmocka_run_group_tests_name("DDB commands tests", tests, diff --git a/src/vos/tests/vts_dtx.c b/src/vos/tests/vts_dtx.c index 944fdc0f744..413a7b8b359 100644 --- a/src/vos/tests/vts_dtx.c +++ b/src/vos/tests/vts_dtx.c @@ -847,7 +847,7 @@ dtx_18(void **state) assert_int_equal(cnt, 10); /* Aggregate the DTXs. */ - rc = vos_dtx_aggregate(args->ctx.tc_co_hdl); + rc = vos_dtx_aggregate(args->ctx.tc_co_hdl, NULL); assert_rc_equal(rc, 0); rc = vos_dtx_get_cmt_cnt(args->ctx.tc_co_hdl, &cnt); diff --git a/src/vos/vos_dtx.c b/src/vos/vos_dtx.c index 7801bf7830d..b20b366886e 100644 --- a/src/vos/vos_dtx.c +++ b/src/vos/vos_dtx.c @@ -13,6 +13,7 @@ #define D_LOGFAC DD_FAC(vos) #include +#include #include #include "vos_layout.h" #include "vos_internal.h" @@ -3050,28 +3051,32 @@ vos_dtx_set_flags(daos_handle_t coh, struct dtx_id dtis[], int count, uint32_t f } int -vos_dtx_aggregate(daos_handle_t coh) +vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max) { - struct vos_tls *tls = vos_tls_get(false); - struct vos_container *cont; - struct vos_cont_df *cont_df; - struct umem_instance *umm; - struct vos_dtx_blob_df *dbd; - struct vos_dtx_blob_df *tmp; - uint64_t epoch; - umem_off_t dbd_off; - umem_off_t next = UMOFF_NULL; - int count = 0; - int rc; - int i; + struct vos_tls *tls; + struct vos_container *cont; + struct vos_cont_df *cont_df; + struct umem_instance *umm; + struct vos_dtx_blob_df *dbd; + struct vos_dtx_blob_df *tmp; + uint64_t epoch; + umem_off_t dbd_off; + umem_off_t next = UMOFF_NULL; + int count; + bool is_freed = false; + int i; + int rc; cont = vos_hdl2cont(coh); D_ASSERT(cont != NULL); + tls = vos_tls_get(false); + D_ASSERT(tls != NULL); + cont_df = cont->vc_cont_df; dbd_off = cont_df->cd_dtx_committed_head; - umm = vos_cont2umm(cont); - epoch = cont_df->cd_newest_aggregated; + umm = vos_cont2umm(cont); + epoch = cont_df->cd_newest_aggregated; dbd = umem_off2ptr(umm, dbd_off); if (dbd == NULL || dbd->dbd_count == 0) @@ -3082,93 +3087,120 @@ vos_dtx_aggregate(daos_handle_t coh) lrua_array_aggregate(cont->vc_dtx_array); rc = umem_tx_begin(umm, NULL); - if (rc != 0) { - D_ERROR("Failed to TX begin for DTX aggregation "UMOFF_PF": " - DF_RC"\n", UMOFF_P(dbd_off), DP_RC(rc)); + if (unlikely(rc != 0)) { + D_ERROR("Failed to TX begin for DTX aggregation " UMOFF_PF ": " DF_RC "\n", + UMOFF_P(dbd_off), DP_RC(rc)); return rc; } + count = 0; for (i = 0; i < dbd->dbd_count; i++) { - struct vos_dtx_cmt_ent_df *dce_df; - d_iov_t kiov; + struct vos_dtx_cmt_ent_df *dce_df; + d_iov_t kiov; dce_df = &dbd->dbd_committed_data[i]; - if (epoch < dce_df->dce_epoch) - epoch = dce_df->dce_epoch; + if (ep_max != NULL && *ep_max < dce_df->dce_epoch) + break; d_iov_set(&kiov, &dce_df->dce_xid, sizeof(dce_df->dce_xid)); rc = dbtree_delete(cont->vc_dtx_committed_hdl, BTR_PROBE_EQ, &kiov, NULL); - if (rc == 0) { - count++; - } else if (rc != -DER_NONEXIST) { + if (rc == -DER_NONEXIST) + rc = 0; + if (rc != 0) { D_ERROR("Failed to remove entry for DTX aggregation " UMOFF_PF": "DF_RC"\n", UMOFF_P(dbd_off), DP_RC(rc)); goto out; } + + count++; + if (epoch < dce_df->dce_epoch) + epoch = dce_df->dce_epoch; } if (epoch != cont_df->cd_newest_aggregated) { rc = umem_tx_add_ptr(umm, &cont_df->cd_newest_aggregated, sizeof(cont_df->cd_newest_aggregated)); - if (rc != 0) { - D_ERROR("Failed to refresh epoch for DTX aggregation "UMOFF_PF": "DF_RC"\n", + if (unlikely(rc != 0)) { + D_ERROR("Failed to refresh epoch for DTX aggregation " UMOFF_PF ": " DF_RC + "\n", UMOFF_P(dbd_off), DP_RC(rc)); goto out; } - cont_df->cd_newest_aggregated = epoch; } - next = dbd->dbd_next; - tmp = umem_off2ptr(umm, next); - if (tmp == NULL) { - /* The last blob for committed DTX blob. */ - D_ASSERT(cont_df->cd_dtx_committed_tail == - cont_df->cd_dtx_committed_head); + if (count < dbd->dbd_count) { + size_t buf_len; - rc = umem_tx_add_ptr(umm, &cont_df->cd_dtx_committed_tail, - sizeof(cont_df->cd_dtx_committed_tail)); - if (rc != 0) { - D_ERROR("Failed to update tail for DTX aggregation " - UMOFF_PF": "DF_RC"\n", + rc = umem_tx_add_ptr(umm, &dbd->dbd_committed_data[0], + sizeof(dbd->dbd_committed_data[0]) * dbd->dbd_count); + if (unlikely(rc != 0)) { + D_ERROR("Failed update committed DTX blob " UMOFF_PF ": " DF_RC "\n", UMOFF_P(dbd_off), DP_RC(rc)); goto out; } + buf_len = (dbd->dbd_count - count) * sizeof(dbd->dbd_committed_data[0]); + memmove(&dbd->dbd_committed_data[0], &dbd->dbd_committed_data[count], buf_len); - cont_df->cd_dtx_committed_tail = UMOFF_NULL; - } else { - rc = umem_tx_add_ptr(umm, &tmp->dbd_prev, - sizeof(tmp->dbd_prev)); - if (rc != 0) { - D_ERROR("Failed to update prev for DTX aggregation " - UMOFF_PF": "DF_RC"\n", + rc = umem_tx_add_ptr(umm, &dbd->dbd_count, sizeof(dbd->dbd_count)); + if (unlikely(rc != 0)) { + D_ERROR("Failed update committed DTX count " UMOFF_PF ": " DF_RC "\n", UMOFF_P(dbd_off), DP_RC(rc)); goto out; } + } else { + next = dbd->dbd_next; + tmp = umem_off2ptr(umm, next); + if (tmp == NULL) { + /* The last blob for committed DTX blob. */ + D_ASSERT(cont_df->cd_dtx_committed_tail == cont_df->cd_dtx_committed_head); + + rc = umem_tx_add_ptr(umm, &cont_df->cd_dtx_committed_tail, + sizeof(cont_df->cd_dtx_committed_tail)); + if (unlikely(rc != 0)) { + D_ERROR("Failed to update tail for DTX aggregation " UMOFF_PF + ": " DF_RC "\n", + UMOFF_P(dbd_off), DP_RC(rc)); + goto out; + } + cont_df->cd_dtx_committed_tail = UMOFF_NULL; + } else { + rc = umem_tx_add_ptr(umm, &tmp->dbd_prev, sizeof(tmp->dbd_prev)); + if (unlikely(rc != 0)) { + D_ERROR("Failed to update prev for DTX aggregation " UMOFF_PF + ": " DF_RC "\n", + UMOFF_P(dbd_off), DP_RC(rc)); + goto out; + } + tmp->dbd_prev = UMOFF_NULL; + } - tmp->dbd_prev = UMOFF_NULL; - } + rc = umem_tx_add_ptr(umm, &cont_df->cd_dtx_committed_head, + sizeof(cont_df->cd_dtx_committed_head)); + if (unlikely(rc != 0)) { + D_ERROR("Failed to update head for DTX aggregation " UMOFF_PF ": " DF_RC + "\n", + UMOFF_P(dbd_off), DP_RC(rc)); + goto out; + } + cont_df->cd_dtx_committed_head = next; - rc = umem_tx_add_ptr(umm, &cont_df->cd_dtx_committed_head, - sizeof(cont_df->cd_dtx_committed_head)); - if (rc != 0) { - D_ERROR("Failed to update head for DTX aggregation " - UMOFF_PF": "DF_RC"\n", - UMOFF_P(dbd_off), DP_RC(rc)); - goto out; + rc = umem_free(umm, dbd_off); + if (unlikely(rc != 0)) { + D_ERROR("Failed to free DTXs blob " UMOFF_PF ": " DF_RC "\n", + UMOFF_P(dbd_off), DP_RC(rc)); + goto out; + } + is_freed = true; } - - cont_df->cd_dtx_committed_head = next; - - rc = umem_free(umm, dbd_off); + dbd->dbd_count -= count; out: rc = umem_tx_end(umm, rc); - if (rc == 0) { - if (cont->vc_cmt_dtx_reindex_pos == dbd_off) + if (likely(rc == 0)) { + if (is_freed && cont->vc_cmt_dtx_reindex_pos == dbd_off) cont->vc_cmt_dtx_reindex_pos = next; - cont->vc_dtx_committed_count -= count; cont->vc_pool->vp_dtx_committed_count -= count; d_tm_dec_gauge(tls->vtl_committed, count); From c2b4bb7ed87accf1f9e8aa91697760d8f9ef2212 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Wed, 1 Oct 2025 14:07:34 +0000 Subject: [PATCH 03/20] DAOS-17322 ddb: DTX aggregation with ddb Fix reviewers comments: - Fix all DTX entries blob Miscellaneous fixes: - Fix coding standard issue: code refactoing of variable with upper case - Remove useless assignment Signed-off-by: Cedric Koch-Hofer --- src/dtx/dtx_common.c | 2 +- src/dtx/tests/dts_aggregate.c | 898 +++++++++++++++++++++++----------- src/dtx/tests/dts_count.c | 2 - src/include/daos_srv/vos.h | 3 +- src/utils/ddb/ddb_commands.c | 2 +- src/vos/tests/vts_dtx.c | 2 +- src/vos/vos_dtx.c | 133 +++-- 7 files changed, 710 insertions(+), 332 deletions(-) diff --git a/src/dtx/dtx_common.c b/src/dtx/dtx_common.c index b4c5ec3369c..5d2424fc0f4 100644 --- a/src/dtx/dtx_common.c +++ b/src/dtx/dtx_common.c @@ -430,7 +430,7 @@ dtx_aggregate(void *arg) struct dtx_stat stat = { 0 }; int rc; - rc = vos_dtx_aggregate(cont->sc_hdl, NULL); + rc = vos_dtx_aggregate(cont->sc_hdl, NULL, false); if (rc != 0) break; diff --git a/src/dtx/tests/dts_aggregate.c b/src/dtx/tests/dts_aggregate.c index 13e93f109f2..47e29c83f5e 100644 --- a/src/dtx/tests/dts_aggregate.c +++ b/src/dtx/tests/dts_aggregate.c @@ -25,12 +25,16 @@ /* mocks */ #define DTX_CMT_BLOB_MAGIC 0x2502191c +#define DBD_BLOBS_CAP 0x3 #define DBD_BLOB_DF_CAP 0x8 #define EPOCH_START 0x1000 +#define EPOCH_END 0x10000 #define EPOCH_STEP 0x10 #define MOCK_UMM_TX_MAX 0x100 +#define CELL_SIZE \ + sizeof(struct vos_dtx_blob_df) + DBD_BLOB_DF_CAP * sizeof(struct vos_dtx_cmt_ent_df) -struct mock_umm_tx { +struct mocked_umm_tx { bool in_tx; size_t count; struct { @@ -40,15 +44,15 @@ struct mock_umm_tx { } tx[MOCK_UMM_TX_MAX]; }; -static struct vos_pool Pool; -static struct vos_container Cont; -static struct vos_cont_df ContDf; -static struct vos_tls Tls; -static struct vos_dtx_blob_df *Dbd = NULL; -static umem_off_t DbdOff; -static struct btr_instance Btr; -static daos_handle_t Coh; -static struct mock_umm_tx MockUmmTx; +static struct vos_pool mock_pool; +static struct vos_container mock_cont; +static struct vos_cont_df mock_cont_df; +static struct vos_tls mock_tls; +static struct vos_dtx_blob_df *mock_dbds[DBD_BLOBS_CAP]; +static umem_off_t mock_dbds_off[DBD_BLOBS_CAP]; +static struct btr_instance mock_btr; +static daos_handle_t mock_coh; +static struct mocked_umm_tx mock_umm_tx; struct vos_tls * __wrap_vos_tls_get(bool standalone) @@ -66,7 +70,7 @@ __wrap_lrua_array_aggregate(struct lru_array *array) int __wrap_dbtree_delete(daos_handle_t toh, dbtree_probe_opc_t opc, d_iov_t *key, void *args) { - assert_int_equal(toh.cookie, Cont.vc_dtx_committed_hdl.cookie); + assert_int_equal(toh.cookie, mock_cont.vc_dtx_committed_hdl.cookie); assert_int_equal(opc, BTR_PROBE_EQ); assert_non_null(key); assert_null(args); @@ -85,11 +89,11 @@ tx_begin(struct umem_instance *umm, struct umem_tx_stage_data *txd) { int rc; - assert_ptr_equal(umm, &Pool.vp_umm); + assert_ptr_equal(umm, &mock_pool.vp_umm); assert_null(txd); - assert_int_equal(MockUmmTx.count, 0); + assert_int_equal(mock_umm_tx.count, 0); rc = mock(); - MockUmmTx.in_tx = (rc == 0); + mock_umm_tx.in_tx = (rc == 0); return rc; } @@ -99,18 +103,18 @@ tx_commit(struct umem_instance *umm, void *data) int i; int rc; - assert_ptr_equal(umm, &Pool.vp_umm); + assert_ptr_equal(umm, &mock_pool.vp_umm); assert_null(data); - assert_true(MockUmmTx.in_tx); + assert_true(mock_umm_tx.in_tx); rc = mock(); - for (i = 0; i < MockUmmTx.count; i++) { + for (i = 0; i < mock_umm_tx.count; i++) { if (rc != 0) - memcpy(MockUmmTx.tx[i].target, MockUmmTx.tx[i].snapshot, - MockUmmTx.tx[i].size); - D_FREE(MockUmmTx.tx[i].snapshot); + memcpy(mock_umm_tx.tx[i].target, mock_umm_tx.tx[i].snapshot, + mock_umm_tx.tx[i].size); + D_FREE(mock_umm_tx.tx[i].snapshot); } - MockUmmTx.count = 0; - MockUmmTx.in_tx = false; + mock_umm_tx.count = 0; + mock_umm_tx.in_tx = false; return rc; } @@ -119,15 +123,16 @@ tx_abort(struct umem_instance *umm, int error) { int i; - assert_ptr_equal(umm, &Pool.vp_umm); + assert_ptr_equal(umm, &mock_pool.vp_umm); check_expected(error); - assert_true(MockUmmTx.in_tx); - for (i = 0; i < MockUmmTx.count; i++) { - memcpy(MockUmmTx.tx[i].target, MockUmmTx.tx[i].snapshot, MockUmmTx.tx[i].size); - D_FREE(MockUmmTx.tx[i].snapshot); + assert_true(mock_umm_tx.in_tx); + for (i = 0; i < mock_umm_tx.count; i++) { + memcpy(mock_umm_tx.tx[i].target, mock_umm_tx.tx[i].snapshot, + mock_umm_tx.tx[i].size); + D_FREE(mock_umm_tx.tx[i].snapshot); } - MockUmmTx.count = 0; - MockUmmTx.in_tx = false; + mock_umm_tx.count = 0; + mock_umm_tx.in_tx = false; if (error) { return error; } @@ -137,63 +142,77 @@ tx_abort(struct umem_instance *umm, int error) static int tx_add_ptr(struct umem_instance *umm, void *ptr, size_t ptr_size) { - assert_ptr_equal(umm, &Pool.vp_umm); + assert_ptr_equal(umm, &mock_pool.vp_umm); check_expected_ptr(ptr); check_expected(ptr_size); - D_ALLOC(MockUmmTx.tx[MockUmmTx.count].snapshot, ptr_size); - assert_non_null(MockUmmTx.tx[MockUmmTx.count].snapshot); - memcpy(MockUmmTx.tx[MockUmmTx.count].snapshot, ptr, ptr_size); - MockUmmTx.tx[MockUmmTx.count].target = ptr; - MockUmmTx.tx[MockUmmTx.count].size = ptr_size; - MockUmmTx.count++; + D_ALLOC(mock_umm_tx.tx[mock_umm_tx.count].snapshot, ptr_size); + assert_non_null(mock_umm_tx.tx[mock_umm_tx.count].snapshot); + memcpy(mock_umm_tx.tx[mock_umm_tx.count].snapshot, ptr, ptr_size); + mock_umm_tx.tx[mock_umm_tx.count].target = ptr; + mock_umm_tx.tx[mock_umm_tx.count].size = ptr_size; + mock_umm_tx.count++; return mock(); } static int tx_free(struct umem_instance *umm, umem_off_t umoff) { - assert_ptr_equal(umm, &Pool.vp_umm); + assert_ptr_equal(umm, &mock_pool.vp_umm); check_expected(umoff); - assert_true(MockUmmTx.in_tx); + assert_true(mock_umm_tx.in_tx); return mock(); } /* Helpers */ static void -prep_dtx_entries(size_t count) +prep_dtx_entries(void) { int i; - daos_epoch_t epoch = EPOCH_START; + daos_epoch_t epoch; - for (i = 0; i < count; i++) { - struct vos_dtx_cmt_ent_df *dce_df; + epoch = EPOCH_START; + for (i = 0; i < DBD_BLOBS_CAP; i++) { + int j; - dce_df = &Dbd->dbd_committed_data[i]; - dce_df->dce_epoch = epoch; + for (j = 0; j < DBD_BLOB_DF_CAP; j++) { + struct vos_dtx_cmt_ent_df *dce_df; - epoch += EPOCH_STEP; + dce_df = &mock_dbds[i]->dbd_committed_data[j]; + dce_df->dce_epoch = epoch; + epoch += EPOCH_STEP; + } + mock_dbds[i]->dbd_count = DBD_BLOB_DF_CAP; } - Dbd->dbd_count = count; - Cont.vc_dtx_committed_count = count; - Pool.vp_dtx_committed_count = count; + mock_cont.vc_dtx_committed_count = DBD_BLOBS_CAP * DBD_BLOB_DF_CAP; + mock_pool.vp_dtx_committed_count = DBD_BLOBS_CAP * DBD_BLOB_DF_CAP; } static void -check_rollback(size_t count) +check_rollback(void) { - int i; + int i; + daos_epoch_t epoch; + + epoch = EPOCH_START; + for (i = 0; i < DBD_BLOBS_CAP; i++) { + int j; - assert_int_equal(Dbd->dbd_count, count); - for (i = 0; i < count; i++) - assert_int_equal(Dbd->dbd_committed_data[i].dce_epoch, - EPOCH_START + i * EPOCH_STEP); - assert_int_equal(ContDf.cd_newest_aggregated, 0); - assert_int_equal(ContDf.cd_dtx_committed_head, umem_ptr2off(&Pool.vp_umm, Dbd)); - assert_int_equal(ContDf.cd_dtx_committed_tail, umem_ptr2off(&Pool.vp_umm, Dbd)); - assert_int_equal(Cont.vc_dtx_committed_count, count); - assert_int_equal(Pool.vp_dtx_committed_count, count); - assert_int_equal(Cont.vc_cmt_dtx_reindex_pos, umem_ptr2off(&Pool.vp_umm, Dbd)); + for (j = 0; j < DBD_BLOB_DF_CAP; j++) { + assert_int_equal(mock_dbds[i]->dbd_committed_data[j].dce_epoch, epoch); + epoch += EPOCH_STEP; + } + assert_int_equal(mock_dbds[i]->dbd_count, DBD_BLOB_DF_CAP); + } + assert_int_equal(mock_cont_df.cd_newest_aggregated, 0); + assert_int_equal(mock_cont_df.cd_dtx_committed_head, + umem_ptr2off(&mock_pool.vp_umm, mock_dbds[0])); + assert_int_equal(mock_cont_df.cd_dtx_committed_tail, + umem_ptr2off(&mock_pool.vp_umm, mock_dbds[DBD_BLOBS_CAP - 1])); + assert_int_equal(mock_cont.vc_dtx_committed_count, DBD_BLOBS_CAP * DBD_BLOB_DF_CAP); + assert_int_equal(mock_pool.vp_dtx_committed_count, DBD_BLOBS_CAP * DBD_BLOB_DF_CAP); + assert_int_equal(mock_cont.vc_cmt_dtx_reindex_pos, + umem_ptr2off(&mock_pool.vp_umm, mock_dbds[0])); } /* setup & teardown */ @@ -207,28 +226,65 @@ static umem_ops_t umm_ops = {.mo_tx_begin = tx_begin, static int test_setup(void **unused) { - memset(&Pool, 0, sizeof(Pool)); - memset(&Cont, 0, sizeof(Cont)); - memset(&ContDf, 0, sizeof(ContDf)); - memset(&Tls, 0, sizeof(Tls)); - memset(&MockUmmTx, 0, sizeof(MockUmmTx)); - - D_ALLOC(Dbd, sizeof(struct vos_dtx_blob_df) + - DBD_BLOB_DF_CAP * sizeof(struct vos_dtx_cmt_ent_df)); - assert_non_null(Dbd); - Dbd->dbd_magic = DTX_CMT_BLOB_MAGIC; - Dbd->dbd_cap = DBD_BLOB_DF_CAP; - Dbd->dbd_next = UMOFF_NULL; - DbdOff = umem_ptr2off(&Pool.vp_umm, Dbd); - - Pool.vp_umm.umm_ops = &umm_ops; - Cont.vc_pool = &Pool; - Cont.vc_cont_df = &ContDf; - Cont.vc_cmt_dtx_reindex_pos = DbdOff; - Cont.vc_dtx_committed_hdl.cookie = (uint64_t)&Btr; - ContDf.cd_dtx_committed_head = DbdOff; - ContDf.cd_dtx_committed_tail = DbdOff; - Coh.cookie = (uint64_t)&Cont; + int i; + + memset(&mock_pool, 0, sizeof(mock_pool)); + memset(&mock_cont, 0, sizeof(mock_cont)); + memset(&mock_cont_df, 0, sizeof(mock_cont_df)); + memset(&mock_tls, 0, sizeof(mock_tls)); + memset(&mock_umm_tx, 0, sizeof(mock_umm_tx)); + + for (i = 0; i < DBD_BLOBS_CAP; i++) { + D_ALLOC(mock_dbds[i], CELL_SIZE); + assert_non_null(mock_dbds[i]); + mock_dbds_off[i] = umem_ptr2off(&mock_pool.vp_umm, mock_dbds[i]); + } + + for (i = 0; i < DBD_BLOBS_CAP; i++) { + mock_dbds[i]->dbd_magic = DTX_CMT_BLOB_MAGIC; + mock_dbds[i]->dbd_cap = DBD_BLOB_DF_CAP; + mock_dbds[i]->dbd_next = + (i == DBD_BLOBS_CAP - 1) ? UMOFF_NULL : mock_dbds_off[i + 1]; + mock_dbds[i]->dbd_prev = (i == 0) ? UMOFF_NULL : mock_dbds_off[i - 1]; + mock_dbds[i]->dbd_count = 0; + assert_true(mock_dbds[i]->dbd_count <= DBD_BLOB_DF_CAP); + } + + mock_pool.vp_umm.umm_ops = &umm_ops; + mock_cont.vc_pool = &mock_pool; + mock_cont.vc_cont_df = &mock_cont_df; + mock_cont.vc_cmt_dtx_reindex_pos = mock_dbds_off[0]; + mock_cont.vc_dtx_committed_hdl.cookie = (uint64_t)&mock_btr; + mock_cont_df.cd_dtx_committed_head = mock_dbds_off[0]; + mock_cont_df.cd_dtx_committed_tail = mock_dbds_off[DBD_BLOBS_CAP - 1]; + mock_coh.cookie = (uint64_t)&mock_cont; + + return 0; +} + +static int +test_empty_setup(void **unused) +{ + int i; + + memset(&mock_pool, 0, sizeof(mock_pool)); + memset(&mock_cont, 0, sizeof(mock_cont)); + memset(&mock_cont_df, 0, sizeof(mock_cont_df)); + memset(&mock_tls, 0, sizeof(mock_tls)); + memset(&mock_umm_tx, 0, sizeof(mock_umm_tx)); + + for (i = 0; i < DBD_BLOBS_CAP; i++) { + mock_dbds[i] = NULL; + mock_dbds_off[i] = umem_ptr2off(&mock_pool.vp_umm, mock_dbds[i]); + } + mock_pool.vp_umm.umm_ops = &umm_ops; + mock_cont.vc_pool = &mock_pool; + mock_cont.vc_cont_df = &mock_cont_df; + mock_cont.vc_cmt_dtx_reindex_pos = UMOFF_NULL; + mock_cont.vc_dtx_committed_hdl.cookie = (uint64_t)&mock_btr; + mock_cont_df.cd_dtx_committed_head = UMOFF_NULL; + mock_cont_df.cd_dtx_committed_tail = UMOFF_NULL; + mock_coh.cookie = (uint64_t)&mock_cont; return 0; } @@ -236,8 +292,19 @@ test_setup(void **unused) static int test_teardown(void **unused) { - assert_int_equal(MockUmmTx.count, 0); - D_FREE(Dbd); + int i; + + assert_int_equal(mock_umm_tx.count, 0); + for (i = 0; i < DBD_BLOBS_CAP; i++) + D_FREE(mock_dbds[i]); + + return 0; +} + +static int +test_empty_teardown(void **unused) +{ + assert_int_equal(mock_umm_tx.count, 0); return 0; } @@ -250,258 +317,360 @@ test_asserts(void **unused) daos_handle_t hdl_null = {0}; /* Missing argument. */ - expect_assert_failure(vos_dtx_aggregate(hdl_null, NULL)); + expect_assert_failure(vos_dtx_aggregate(hdl_null, NULL, true)); /* Invalid Telemetry global variable. */ will_return(__wrap_vos_tls_get, NULL); - expect_assert_failure(vos_dtx_aggregate(Coh, NULL)); + expect_assert_failure(vos_dtx_aggregate(mock_coh, NULL, true)); /* Invalid pool type. */ - Pool.vp_sysdb = true; - Dbd->dbd_count = 1; - will_return(__wrap_vos_tls_get, &Tls); - expect_assert_failure(vos_dtx_aggregate(Coh, NULL)); + will_return(__wrap_vos_tls_get, &mock_tls); + mock_pool.vp_sysdb = true; + expect_assert_failure(vos_dtx_aggregate(mock_coh, NULL, true)); } /* Can not start a PMEM transtation */ static void test_tx_begin_error(void **unused) { - const size_t dtx_count = 5; int rc; - will_return(__wrap_vos_tls_get, &Tls); + prep_dtx_entries(); + + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, -DER_UNKNOWN); - prep_dtx_entries(dtx_count); - rc = vos_dtx_aggregate(Coh, NULL); + rc = vos_dtx_aggregate(mock_coh, NULL, true); assert_rc_equal(rc, -DER_UNKNOWN); - check_rollback(dtx_count); + check_rollback(); } /* DAOS B-tree delete failure */ static void test_dbtree_delete_error(void **unused) { - const size_t dtx_count = 5; int i; int rc; - will_return(__wrap_vos_tls_get, &Tls); + prep_dtx_entries(); + + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); - prep_dtx_entries(dtx_count); - for (i = 0; i < 3; i++) { + for (i = 0; i < 3; i++) will_return(__wrap_dbtree_delete, 0); - } will_return(__wrap_dbtree_delete, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(Coh, NULL); + rc = vos_dtx_aggregate(mock_coh, NULL, true); assert_rc_equal(rc, -DER_UNKNOWN); - check_rollback(dtx_count); + check_rollback(); } /* Update of newest aggregated epoch failure */ static void test_newest_aggregated_error(void **unused) { - const size_t dtx_count = 5; int i; int rc; - will_return(__wrap_vos_tls_get, &Tls); + prep_dtx_entries(); + + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); - prep_dtx_entries(dtx_count); - for (i = 0; i < dtx_count; i++) { + for (i = 0; i < DBD_BLOB_DF_CAP; i++) will_return(__wrap_dbtree_delete, 0); - } - expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(Coh, NULL); + rc = vos_dtx_aggregate(mock_coh, NULL, true); assert_rc_equal(rc, -DER_UNKNOWN); - check_rollback(dtx_count); + check_rollback(); } /* Update of DTX blob list failure */ static void -test_committed_tail_error(void **unused) +test_committed_prev_error(void **unused) { - const size_t dtx_count = 5; int i; int rc; - will_return(__wrap_vos_tls_get, &Tls); + prep_dtx_entries(); + + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); - prep_dtx_entries(dtx_count); - for (i = 0; i < dtx_count; i++) { + for (i = 0; i < DBD_BLOB_DF_CAP; i++) will_return(__wrap_dbtree_delete, 0); - } - expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_tail); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_tail)); + expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[1]->dbd_prev)); will_return(tx_add_ptr, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(Coh, NULL); + rc = vos_dtx_aggregate(mock_coh, NULL, true); assert_rc_equal(rc, -DER_UNKNOWN); - check_rollback(dtx_count); + check_rollback(); } /* Update of DTX blob list failure */ static void test_committed_head_error(void **unused) { - const size_t dtx_count = 5; int i; int rc; - will_return(__wrap_vos_tls_get, &Tls); + prep_dtx_entries(); + + will_return(__wrap_vos_tls_get, &mock_tls); + will_return(tx_begin, 0); + for (i = 0; i < DBD_BLOB_DF_CAP; i++) + will_return(__wrap_dbtree_delete, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[1]->dbd_prev)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_head)); + will_return(tx_add_ptr, -DER_UNKNOWN); + expect_value(tx_abort, error, -DER_UNKNOWN); + + rc = vos_dtx_aggregate(mock_coh, NULL, false); + assert_rc_equal(rc, -DER_UNKNOWN); + check_rollback(); +} + +/* Update of DTX blob list failure */ +static void +test_committed_tail_error(void **unused) +{ + int i; + int rc; + daos_epoch_t epoch; + + prep_dtx_entries(); + + will_return(__wrap_vos_tls_get, &mock_tls); + for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { + int j; + + will_return(tx_begin, 0); + for (j = 0; j < DBD_BLOB_DF_CAP; j++) + mock_dbds[i]->dbd_committed_data[j].dce_epoch += EPOCH_END; + expect_value(tx_add_ptr, ptr, &mock_dbds[i]->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, + sizeof(mock_dbds[i]->dbd_committed_data[0]) * mock_dbds[i]->dbd_count); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[i]->dbd_count); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[i]->dbd_count)); + will_return(tx_add_ptr, 0); + will_return(tx_commit, 0); + expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, 0); + } will_return(tx_begin, 0); - prep_dtx_entries(dtx_count); - for (i = 0; i < dtx_count; i++) { + for (i = 0; i < DBD_BLOB_DF_CAP; i++) will_return(__wrap_dbtree_delete, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_tail); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_tail)); + will_return(tx_add_ptr, -DER_UNKNOWN); + expect_value(tx_abort, error, -DER_UNKNOWN); + + epoch = EPOCH_END; + rc = vos_dtx_aggregate(mock_coh, &epoch, true); + assert_rc_equal(rc, -DER_UNKNOWN); + for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { + int j; + + for (j = 0; j < DBD_BLOB_DF_CAP; j++) + mock_dbds[i]->dbd_committed_data[j].dce_epoch -= EPOCH_END; + } + check_rollback(); +} + +/* Update of DTX blob list failure */ +static void +test_committed_next_error(void **unused) +{ + int i; + int rc; + daos_epoch_t epoch; + + prep_dtx_entries(); + + will_return(__wrap_vos_tls_get, &mock_tls); + for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { + int j; + + will_return(tx_begin, 0); + for (j = 0; j < DBD_BLOB_DF_CAP; j++) + mock_dbds[i]->dbd_committed_data[j].dce_epoch += EPOCH_END; + expect_value(tx_add_ptr, ptr, &mock_dbds[i]->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, + sizeof(mock_dbds[i]->dbd_committed_data[0]) * mock_dbds[i]->dbd_count); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[i]->dbd_count); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[i]->dbd_count)); + will_return(tx_add_ptr, 0); + will_return(tx_commit, 0); + expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, 0); } - expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + will_return(tx_begin, 0); + for (i = 0; i < DBD_BLOB_DF_CAP; i++) + will_return(__wrap_dbtree_delete, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_tail); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_tail)); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_tail); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_tail)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_head); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_head)); + expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_next); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[1]->dbd_next)); will_return(tx_add_ptr, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(Coh, NULL); + epoch = EPOCH_END; + rc = vos_dtx_aggregate(mock_coh, &epoch, true); assert_rc_equal(rc, -DER_UNKNOWN); - check_rollback(dtx_count); + for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { + int j; + + for (j = 0; j < DBD_BLOB_DF_CAP; j++) + mock_dbds[i]->dbd_committed_data[j].dce_epoch -= EPOCH_END; + } + check_rollback(); } /* Pmem free failure */ static void test_umm_free_error(void **unused) { - const size_t dtx_count = 5; int i; int rc; - will_return(__wrap_vos_tls_get, &Tls); + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); - prep_dtx_entries(dtx_count); - for (i = 0; i < dtx_count; i++) { + prep_dtx_entries(); + for (i = 0; i < DBD_BLOB_DF_CAP; i++) will_return(__wrap_dbtree_delete, 0); - } - expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_tail); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_tail)); + expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[1]->dbd_prev)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_head); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_head)); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_head)); will_return(tx_add_ptr, 0); - expect_value(tx_free, umoff, ContDf.cd_dtx_committed_head); + + expect_value(tx_free, umoff, mock_dbds_off[0]); will_return(tx_free, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(Coh, NULL); + rc = vos_dtx_aggregate(mock_coh, NULL, true); assert_rc_equal(rc, -DER_UNKNOWN); - check_rollback(dtx_count); + check_rollback(); } /* Update of committed DTX entries failure */ static void test_committed_data_error(void **unused) { - const daos_epoch_t ep_max = EPOCH_START + 2 * EPOCH_STEP; - const size_t dtx_count = 5; - int i; - int rc; + daos_epoch_t epoch; + int i; + int rc; + const int dtx_count = 3; - will_return(__wrap_vos_tls_get, &Tls); + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); - prep_dtx_entries(dtx_count); - for (i = 0; i < 3; i++) { + prep_dtx_entries(); + for (i = 0; i < dtx_count; i++) will_return(__wrap_dbtree_delete, -DER_NONEXIST); - } - expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &Dbd->dbd_committed_data[0]); - expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_committed_data[0]) * dtx_count); + expect_value(tx_add_ptr, ptr, &mock_dbds[0]->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, + sizeof(mock_dbds[0]->dbd_committed_data[0]) * mock_dbds[0]->dbd_count); will_return(tx_add_ptr, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(Coh, &ep_max); + epoch = EPOCH_START + (dtx_count - 1) * EPOCH_STEP; + rc = vos_dtx_aggregate(mock_coh, &epoch, true); assert_rc_equal(rc, -DER_UNKNOWN); - check_rollback(dtx_count); + check_rollback(); } /* Update of committed DTX entries count failure */ static void test_dbd_count_error(void **unused) { - const daos_epoch_t ep_max = EPOCH_START + 2 * EPOCH_STEP; - const size_t dtx_count = 5; - int i; - int rc; + daos_epoch_t epoch; + int i; + int rc; + const int dtx_count = 3; - will_return(__wrap_vos_tls_get, &Tls); + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); - prep_dtx_entries(dtx_count); - for (i = 0; i < 3; i++) { + prep_dtx_entries(); + for (i = 0; i < dtx_count; i++) will_return(__wrap_dbtree_delete, -DER_NONEXIST); - } - expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &Dbd->dbd_committed_data[0]); - expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_committed_data[0]) * dtx_count); + expect_value(tx_add_ptr, ptr, &mock_dbds[0]->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, + sizeof(mock_dbds[0]->dbd_committed_data[0]) * mock_dbds[0]->dbd_count); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &Dbd->dbd_count); - expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_count)); + expect_value(tx_add_ptr, ptr, &mock_dbds[0]->dbd_count); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[0]->dbd_count)); will_return(tx_add_ptr, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(Coh, &ep_max); + epoch = EPOCH_START + (dtx_count - 1) * EPOCH_STEP; + rc = vos_dtx_aggregate(mock_coh, &epoch, true); assert_rc_equal(rc, -DER_UNKNOWN); - check_rollback(dtx_count); + check_rollback(); } /* Pmem commit transaction failure */ static void test_umm_commit_error(void **unused) { - const daos_epoch_t ep_max = EPOCH_START + 2 * EPOCH_STEP; - const size_t dtx_count = 5; - int i; - int rc; + daos_epoch_t epoch; + int i; + int rc; + const int dtx_count = 3; - will_return(__wrap_vos_tls_get, &Tls); + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); - prep_dtx_entries(dtx_count); - for (i = 0; i < 3; i++) { + prep_dtx_entries(); + for (i = 0; i < dtx_count; i++) will_return(__wrap_dbtree_delete, -DER_NONEXIST); - } - expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &Dbd->dbd_committed_data[0]); - expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_committed_data[0]) * dtx_count); + expect_value(tx_add_ptr, ptr, &mock_dbds[0]->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, + sizeof(mock_dbds[0]->dbd_committed_data[0]) * mock_dbds[0]->dbd_count); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &Dbd->dbd_count); - expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_count)); + expect_value(tx_add_ptr, ptr, &mock_dbds[0]->dbd_count); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[0]->dbd_count)); will_return(tx_add_ptr, 0); will_return(tx_commit, -DER_UNKNOWN); - rc = vos_dtx_aggregate(Coh, &ep_max); + epoch = EPOCH_START + (dtx_count - 1) * EPOCH_STEP; + rc = vos_dtx_aggregate(mock_coh, &epoch, true); assert_rc_equal(rc, -DER_UNKNOWN); - check_rollback(dtx_count); + check_rollback(); } /* Pool without DTX committed transaction */ @@ -510,139 +679,302 @@ test_empty(void **unused) { int rc; - will_return(__wrap_vos_tls_get, &Tls); + will_return(__wrap_vos_tls_get, &mock_tls); + + rc = vos_dtx_aggregate(mock_coh, NULL, false); + assert_rc_equal(rc, 0); + assert_int_equal(mock_cont_df.cd_newest_aggregated, 0); + assert_int_equal(mock_cont_df.cd_dtx_committed_head, UMOFF_NULL); + assert_int_equal(mock_cont_df.cd_dtx_committed_tail, UMOFF_NULL); + assert_int_equal(mock_cont.vc_dtx_committed_count, 0); + assert_int_equal(mock_pool.vp_dtx_committed_count, 0); + assert_int_equal(mock_cont.vc_cmt_dtx_reindex_pos, UMOFF_NULL); +} + +/* Pool with empty DTX entries blob*/ +static void +test_empty_blob(void **unused) +{ + int i; + int rc; + + will_return(__wrap_vos_tls_get, &mock_tls); + for (i = 0; i < DBD_BLOBS_CAP; i++) { + will_return(tx_begin, 0); + if (i == DBD_BLOBS_CAP - 1) + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_tail); + else + expect_value(tx_add_ptr, ptr, &mock_dbds[i + 1]->dbd_prev); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); + will_return(tx_add_ptr, 0); + expect_value(tx_free, umoff, mock_dbds_off[i]); + will_return(tx_free, 0); + will_return(tx_commit, 0); + expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, 0); + } - /* No committed DTX entries to aggregate. */ - rc = vos_dtx_aggregate(Coh, NULL); + rc = vos_dtx_aggregate(mock_coh, NULL, true); assert_rc_equal(rc, 0); + assert_int_equal(mock_cont_df.cd_newest_aggregated, 0); + assert_int_equal(mock_cont_df.cd_dtx_committed_head, UMOFF_NULL); + assert_int_equal(mock_cont_df.cd_dtx_committed_tail, UMOFF_NULL); + assert_int_equal(mock_cont.vc_dtx_committed_count, 0); + assert_int_equal(mock_pool.vp_dtx_committed_count, 0); + assert_int_equal(mock_cont.vc_cmt_dtx_reindex_pos, UMOFF_NULL); } -/* Aggregation of all the 5 committed DTX transactions */ +/* Aggregation of the first 10 committed DTX transactions */ static void -test_5_entries(void **unused) +test_10_entries(void **unused) { - const size_t dtx_count = 5; int i; int rc; + daos_epoch_t epoch; + + prep_dtx_entries(); - will_return(__wrap_vos_tls_get, &Tls); + will_return(__wrap_vos_tls_get, &mock_tls); + /* First DTX entries blob */ will_return(tx_begin, 0); - prep_dtx_entries(dtx_count); - for (i = 0; i < dtx_count; i++) { + for (i = 0; i < DBD_BLOB_DF_CAP; i++) will_return(__wrap_dbtree_delete, 0); - } - expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_tail); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_tail)); + expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[1]->dbd_prev)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &ContDf.cd_dtx_committed_head); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_dtx_committed_head)); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_head)); will_return(tx_add_ptr, 0); - expect_value(tx_free, umoff, ContDf.cd_dtx_committed_head); + expect_value(tx_free, umoff, mock_dbds_off[0]); will_return(tx_free, 0); will_return(tx_commit, 0); - expect_value(__wrap_d_tm_dec_gauge, metric, Tls.vtl_committed); - expect_value(__wrap_d_tm_dec_gauge, value, dtx_count); + expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, DBD_BLOB_DF_CAP); + /* Second DTX entries blob */ + will_return(tx_begin, 0); + for (i = 0; i < 3; i++) + will_return(__wrap_dbtree_delete, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, + sizeof(mock_dbds[1]->dbd_committed_data[0]) * mock_dbds[1]->dbd_count); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_count); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[1]->dbd_count)); + will_return(tx_add_ptr, 0); + will_return(tx_commit, 0); + expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, 3); + /* Third DTX entries blob */ + will_return(tx_begin, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[2]->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, + sizeof(mock_dbds[2]->dbd_committed_data[0]) * mock_dbds[2]->dbd_count); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[2]->dbd_count); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[2]->dbd_count)); + will_return(tx_add_ptr, 0); + will_return(tx_commit, 0); + expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, 0); - rc = vos_dtx_aggregate(Coh, NULL); + epoch = EPOCH_START + (DBD_BLOB_DF_CAP + 2) * EPOCH_STEP; + rc = vos_dtx_aggregate(mock_coh, &epoch, true); assert_rc_equal(rc, 0); - assert_int_equal(ContDf.cd_newest_aggregated, EPOCH_START + (dtx_count - 1) * EPOCH_STEP); - assert_int_equal(ContDf.cd_dtx_committed_head, UMOFF_NULL); - assert_int_equal(ContDf.cd_dtx_committed_tail, UMOFF_NULL); - assert_int_equal(Cont.vc_dtx_committed_count, 0); - assert_int_equal(Pool.vp_dtx_committed_count, 0); - assert_int_equal(Cont.vc_cmt_dtx_reindex_pos, UMOFF_NULL); + assert_int_equal(mock_cont_df.cd_newest_aggregated, epoch); + assert_int_equal(mock_cont_df.cd_dtx_committed_head, mock_dbds_off[1]); + assert_int_equal(mock_cont_df.cd_dtx_committed_tail, mock_dbds_off[DBD_BLOBS_CAP - 1]); + assert_int_equal(mock_cont.vc_dtx_committed_count, DBD_BLOB_DF_CAP * 2 - 3); + assert_int_equal(mock_pool.vp_dtx_committed_count, DBD_BLOB_DF_CAP * 2 - 3); + assert_int_equal(mock_cont.vc_cmt_dtx_reindex_pos, mock_dbds_off[1]); } -/* Aggregation of 3 committed DTX transactions */ +/* Aggregation of all the committed DTX transactions */ static void -test_3_entries(void **unused) +test_all_entries(void **unused) { - const daos_epoch_t ep_max = EPOCH_START + 2 * EPOCH_STEP; - const size_t dtx_count = 5; - int i; - int rc; + int i; + int rc; - will_return(__wrap_vos_tls_get, &Tls); - will_return(tx_begin, 0); - prep_dtx_entries(dtx_count); - for (i = 0; i < 3; i++) { - will_return(__wrap_dbtree_delete, -DER_NONEXIST); + prep_dtx_entries(); + + will_return(__wrap_vos_tls_get, &mock_tls); + for (i = 0; i < DBD_BLOBS_CAP; i++) { + int j; + + will_return(tx_begin, 0); + for (j = 0; j < DBD_BLOB_DF_CAP; j++) + will_return(__wrap_dbtree_delete, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + if (i == DBD_BLOBS_CAP - 1) + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_tail); + else + expect_value(tx_add_ptr, ptr, &mock_dbds[i + 1]->dbd_prev); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_head)); + will_return(tx_add_ptr, 0); + expect_value(tx_free, umoff, mock_dbds_off[i]); + will_return(tx_free, 0); + will_return(tx_commit, 0); + expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, DBD_BLOB_DF_CAP); } - expect_value(tx_add_ptr, ptr, &ContDf.cd_newest_aggregated); - expect_value(tx_add_ptr, ptr_size, sizeof(ContDf.cd_newest_aggregated)); + + rc = vos_dtx_aggregate(mock_coh, NULL, true); + assert_rc_equal(rc, 0); + assert_int_equal(mock_cont_df.cd_newest_aggregated, + EPOCH_START + (3 * DBD_BLOB_DF_CAP - 1) * EPOCH_STEP); + assert_int_equal(mock_cont_df.cd_dtx_committed_head, UMOFF_NULL); + assert_int_equal(mock_cont_df.cd_dtx_committed_tail, UMOFF_NULL); + assert_int_equal(mock_cont.vc_dtx_committed_count, 0); + assert_int_equal(mock_pool.vp_dtx_committed_count, 0); + assert_int_equal(mock_cont.vc_cmt_dtx_reindex_pos, UMOFF_NULL); +} + +/* Aggregation of all the committed DTX transactions of one blob */ +static void +test_one_blob(void **unused) +{ + int i; + int rc; + + prep_dtx_entries(); + + will_return(__wrap_vos_tls_get, &mock_tls); + will_return(tx_begin, 0); + for (i = 0; i < DBD_BLOB_DF_CAP; i++) + will_return(__wrap_dbtree_delete, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &Dbd->dbd_committed_data[0]); - expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_committed_data[0]) * dtx_count); + expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &Dbd->dbd_count); - expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_count)); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_head)); will_return(tx_add_ptr, 0); + expect_value(tx_free, umoff, mock_dbds_off[0]); + will_return(tx_free, 0); will_return(tx_commit, 0); - expect_value(__wrap_d_tm_dec_gauge, metric, Tls.vtl_committed); - expect_value(__wrap_d_tm_dec_gauge, value, 3); + expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, DBD_BLOB_DF_CAP); - rc = vos_dtx_aggregate(Coh, &ep_max); + rc = vos_dtx_aggregate(mock_coh, NULL, false); assert_rc_equal(rc, 0); - assert_int_equal(Dbd->dbd_count, 2); - assert_int_equal(Dbd->dbd_committed_data[0].dce_epoch, EPOCH_START + 3 * EPOCH_STEP); - assert_int_equal(Dbd->dbd_committed_data[1].dce_epoch, EPOCH_START + 4 * EPOCH_STEP); - assert_int_equal(ContDf.cd_newest_aggregated, EPOCH_START + 2 * EPOCH_STEP); - assert_int_equal(ContDf.cd_dtx_committed_head, DbdOff); - assert_int_equal(ContDf.cd_dtx_committed_tail, DbdOff); - assert_int_equal(Cont.vc_dtx_committed_count, 2); - assert_int_equal(Pool.vp_dtx_committed_count, 2); - assert_int_equal(Cont.vc_cmt_dtx_reindex_pos, DbdOff); -} - -/* Aggregation of 0 committed DTX transactions */ + assert_int_equal(mock_cont_df.cd_newest_aggregated, + EPOCH_START + (DBD_BLOB_DF_CAP - 1) * EPOCH_STEP); + assert_int_equal(mock_cont_df.cd_dtx_committed_head, mock_dbds_off[1]); + assert_int_equal(mock_cont_df.cd_dtx_committed_tail, mock_dbds_off[DBD_BLOBS_CAP - 1]); + assert_int_equal(mock_cont.vc_dtx_committed_count, DBD_BLOB_DF_CAP * (DBD_BLOBS_CAP - 1)); + assert_int_equal(mock_pool.vp_dtx_committed_count, DBD_BLOB_DF_CAP * (DBD_BLOBS_CAP - 1)); + assert_int_equal(mock_cont.vc_cmt_dtx_reindex_pos, mock_dbds_off[1]); +} + +/* Aggregation of all the committed DTX transactions of the second blob */ static void -test_0_entry(void **unused) +test_second_blob(void **unused) { - const daos_epoch_t ep_max = EPOCH_START - 1; - const size_t dtx_count = 5; - int rc; + int i; + daos_epoch_t epoch; + int rc; - will_return(__wrap_vos_tls_get, &Tls); + prep_dtx_entries(); + for (i = 0; i < DBD_BLOB_DF_CAP; i++) { + mock_dbds[0]->dbd_committed_data[i].dce_epoch += EPOCH_END; + mock_dbds[2]->dbd_committed_data[i].dce_epoch += EPOCH_END; + } + + will_return(__wrap_vos_tls_get, &mock_tls); + /* First DTX entries blob */ + will_return(tx_begin, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[0]->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, + sizeof(mock_dbds[0]->dbd_committed_data[0]) * mock_dbds[2]->dbd_count); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[0]->dbd_count); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[0]->dbd_count)); + will_return(tx_add_ptr, 0); + will_return(tx_commit, 0); + expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, 0); + /* Second DTX entries blob */ + will_return(tx_begin, 0); + for (i = 0; i < DBD_BLOB_DF_CAP; i++) + will_return(__wrap_dbtree_delete, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[2]->dbd_prev); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[0]->dbd_next); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); + will_return(tx_add_ptr, 0); + expect_value(tx_free, umoff, mock_dbds_off[1]); + will_return(tx_free, 0); + will_return(tx_commit, 0); + expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, DBD_BLOB_DF_CAP); + /* Third DTX entries blob */ will_return(tx_begin, 0); - prep_dtx_entries(dtx_count); - expect_value(tx_add_ptr, ptr, &Dbd->dbd_committed_data[0]); - expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_committed_data[0]) * dtx_count); + expect_value(tx_add_ptr, ptr, &mock_dbds[2]->dbd_committed_data[0]); + expect_value(tx_add_ptr, ptr_size, + sizeof(mock_dbds[2]->dbd_committed_data[0]) * mock_dbds[2]->dbd_count); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &Dbd->dbd_count); - expect_value(tx_add_ptr, ptr_size, sizeof(Dbd->dbd_count)); + expect_value(tx_add_ptr, ptr, &mock_dbds[2]->dbd_count); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[2]->dbd_count)); will_return(tx_add_ptr, 0); will_return(tx_commit, 0); - expect_value(__wrap_d_tm_dec_gauge, metric, Tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); expect_value(__wrap_d_tm_dec_gauge, value, 0); - rc = vos_dtx_aggregate(Coh, &ep_max); + epoch = EPOCH_START + (DBD_BLOB_DF_CAP * 2) * EPOCH_STEP; + rc = vos_dtx_aggregate(mock_coh, &epoch, true); assert_rc_equal(rc, 0); - check_rollback(dtx_count); + assert_int_equal(mock_cont_df.cd_newest_aggregated, + EPOCH_START + (2 * DBD_BLOB_DF_CAP - 1) * EPOCH_STEP); + assert_int_equal(mock_cont_df.cd_dtx_committed_head, mock_dbds_off[0]); + assert_int_equal(mock_cont_df.cd_dtx_committed_tail, mock_dbds_off[DBD_BLOBS_CAP - 1]); + assert_int_equal(mock_cont.vc_dtx_committed_count, DBD_BLOB_DF_CAP * (DBD_BLOBS_CAP - 1)); + assert_int_equal(mock_pool.vp_dtx_committed_count, DBD_BLOB_DF_CAP * (DBD_BLOBS_CAP - 1)); + assert_int_equal(mock_cont.vc_cmt_dtx_reindex_pos, mock_dbds_off[0]); } -/* clang-format off */ /* compilation unit's entry point */ -#define TEST(name, func) {name ": vos_dtx_aggregate - " #func, func, test_setup, test_teardown} -/* clang-format on */ +#define TEST(name, func, setup_func, teardown_func) \ + {name ": vos_dtx_aggregate - " #func, func, setup_func, teardown_func} static const struct CMUnitTest vos_dtx_aggregate_tests_all[] = { - TEST("DTX500", test_asserts), - TEST("DTX501", test_tx_begin_error), - TEST("DTX502", test_dbtree_delete_error), - TEST("DTX503", test_newest_aggregated_error), - TEST("DTX504", test_committed_tail_error), - TEST("DTX505", test_committed_head_error), - TEST("DTX506", test_umm_free_error), - TEST("DTX507", test_committed_data_error), - TEST("DTX508", test_dbd_count_error), - TEST("DTX509", test_umm_commit_error), - TEST("DTX550", test_empty), - TEST("DTX551", test_5_entries), - TEST("DTX552", test_3_entries), - TEST("DTX554", test_0_entry), + TEST("DTX500", test_asserts, test_setup, test_teardown), + TEST("DTX501", test_tx_begin_error, test_setup, test_teardown), + TEST("DTX502", test_dbtree_delete_error, test_setup, test_teardown), + TEST("DTX503", test_newest_aggregated_error, test_setup, test_teardown), + TEST("DTX504", test_committed_prev_error, test_setup, test_teardown), + TEST("DTX505", test_committed_head_error, test_setup, test_teardown), + TEST("DTX506", test_committed_tail_error, test_setup, test_teardown), + TEST("DTX507", test_committed_next_error, test_setup, test_teardown), + TEST("DTX508", test_umm_free_error, test_setup, test_teardown), + TEST("DTX509", test_committed_data_error, test_setup, test_teardown), + TEST("DTX510", test_dbd_count_error, test_setup, test_teardown), + TEST("DTX509", test_umm_commit_error, test_setup, test_teardown), + TEST("DTX550", test_empty, test_empty_setup, test_empty_teardown), + TEST("DTX551", test_empty_blob, test_setup, test_teardown), + TEST("DTX552", test_10_entries, test_setup, test_teardown), + TEST("DTX553", test_all_entries, test_setup, test_teardown), + TEST("DTX554", test_one_blob, test_setup, test_teardown), + TEST("DTX555", test_second_blob, test_setup, test_teardown), }; int diff --git a/src/dtx/tests/dts_count.c b/src/dtx/tests/dts_count.c index 2c54641b1e3..451ab2e4e40 100644 --- a/src/dtx/tests/dts_count.c +++ b/src/dtx/tests/dts_count.c @@ -58,8 +58,6 @@ test_setup(void **unused) mock_dbds[i]->dbd_prev = (i == 0) ? UMOFF_NULL : mock_dbds_off[i - 1]; mock_dbds[i]->dbd_count = (i + 1) * 2; assert_true(mock_dbds[i]->dbd_count <= DBD_BLOB_DF_CAP); - - mock_dbds_off[i] = umem_ptr2off(&mock_pool.vp_umm, mock_dbds[i]); } mock_cont.vc_pool = &mock_pool; diff --git a/src/include/daos_srv/vos.h b/src/include/daos_srv/vos.h index 91fe6f52630..5ad7e06e108 100644 --- a/src/include/daos_srv/vos.h +++ b/src/include/daos_srv/vos.h @@ -237,11 +237,12 @@ vos_dtx_set_flags(daos_handle_t coh, struct dtx_id dtis[], int count, uint32_t f * * \param coh [IN] Container open handle. * \param ep_max [IN] The upper epoch to aggregate. + * \param all_blobs [IN] Aggregate all DTX blob entries or just one. * * \return Zero on success, negative value if error. */ int -vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max); +vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max, bool all_blobs); /** * Query the container's DTXs statistics information. diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index 596cfe9435d..25dfc4ec64d 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -1498,7 +1498,7 @@ ddb_run_dtx_aggr(struct ddb_ctx *ctx, struct dtx_aggr_options *opt) ddb_print(ctx, "Starting DTX entries aggregation of "); itp_print_full(ctx, &itp); ddb_print(ctx, "\n"); - rc = vos_dtx_aggregate(coh, epoch); + rc = vos_dtx_aggregate(coh, epoch, true); if (!SUCCESS(rc)) { ddb_errorf(ctx, "Aggregation of DTX entries failed: " DF_RC "\n", DP_RC(rc)); goto done; diff --git a/src/vos/tests/vts_dtx.c b/src/vos/tests/vts_dtx.c index 413a7b8b359..a808cdaddb7 100644 --- a/src/vos/tests/vts_dtx.c +++ b/src/vos/tests/vts_dtx.c @@ -847,7 +847,7 @@ dtx_18(void **state) assert_int_equal(cnt, 10); /* Aggregate the DTXs. */ - rc = vos_dtx_aggregate(args->ctx.tc_co_hdl, NULL); + rc = vos_dtx_aggregate(args->ctx.tc_co_hdl, NULL, true); assert_rc_equal(rc, 0); rc = vos_dtx_get_cmt_cnt(args->ctx.tc_co_hdl, &cnt); diff --git a/src/vos/vos_dtx.c b/src/vos/vos_dtx.c index fd5538bb3a7..6696528ac19 100644 --- a/src/vos/vos_dtx.c +++ b/src/vos/vos_dtx.c @@ -3053,42 +3053,24 @@ vos_dtx_set_flags(daos_handle_t coh, struct dtx_id dtis[], int count, uint32_t f return rc; } -int -vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max) +static int +dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_container *cont, + struct vos_cont_df *cont_df, umem_off_t dbd_off, const daos_epoch_t *ep_max) { - struct vos_tls *tls; - struct vos_container *cont; - struct vos_cont_df *cont_df; - struct umem_instance *umm; struct vos_dtx_blob_df *dbd; - struct vos_dtx_blob_df *tmp; + umem_off_t dbd_next_off; uint64_t epoch; - umem_off_t dbd_off; - umem_off_t next = UMOFF_NULL; int count; bool is_freed = false; int i; int rc; - cont = vos_hdl2cont(coh); - D_ASSERT(cont != NULL); - - tls = vos_tls_get(false); - D_ASSERT(tls != NULL); - - cont_df = cont->vc_cont_df; - dbd_off = cont_df->cd_dtx_committed_head; - umm = vos_cont2umm(cont); epoch = cont_df->cd_newest_aggregated; dbd = umem_off2ptr(umm, dbd_off); - if (dbd == NULL || dbd->dbd_count == 0) + if (dbd == NULL) return 0; - D_ASSERT(cont->vc_pool->vp_sysdb == false); - /* Take the opportunity to free some memory if we can */ - lrua_array_aggregate(cont->vc_dtx_array); - rc = umem_tx_begin(umm, NULL); if (unlikely(rc != 0)) { D_ERROR("Failed to TX begin for DTX aggregation " UMOFF_PF ": " DF_RC "\n", @@ -3152,13 +3134,19 @@ vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max) UMOFF_P(dbd_off), DP_RC(rc)); goto out; } + dbd->dbd_count -= count; } else { - next = dbd->dbd_next; - tmp = umem_off2ptr(umm, next); - if (tmp == NULL) { - /* The last blob for committed DTX blob. */ - D_ASSERT(cont_df->cd_dtx_committed_tail == cont_df->cd_dtx_committed_head); + struct vos_dtx_blob_df *dbd_next; + struct vos_dtx_blob_df *dbd_prev; + umem_off_t dbd_prev_off; + dbd_next_off = dbd->dbd_next; + dbd_next = umem_off2ptr(umm, dbd_next_off); + dbd_prev_off = dbd->dbd_prev; + dbd_prev = umem_off2ptr(umm, dbd_prev_off); + + if (dbd_next == NULL) { + D_ASSERT(dbd_off == cont_df->cd_dtx_committed_tail); rc = umem_tx_add_ptr(umm, &cont_df->cd_dtx_committed_tail, sizeof(cont_df->cd_dtx_committed_tail)); if (unlikely(rc != 0)) { @@ -3167,27 +3155,40 @@ vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max) UMOFF_P(dbd_off), DP_RC(rc)); goto out; } - cont_df->cd_dtx_committed_tail = UMOFF_NULL; + cont_df->cd_dtx_committed_tail = dbd_prev_off; } else { - rc = umem_tx_add_ptr(umm, &tmp->dbd_prev, sizeof(tmp->dbd_prev)); + rc = umem_tx_add_ptr(umm, &dbd_next->dbd_prev, sizeof(dbd_next->dbd_prev)); if (unlikely(rc != 0)) { - D_ERROR("Failed to update prev for DTX aggregation " UMOFF_PF - ": " DF_RC "\n", + D_ERROR("Failed to update previous DTXs blob for DTX " + "aggregation " UMOFF_PF ": " DF_RC "\n", UMOFF_P(dbd_off), DP_RC(rc)); goto out; } - tmp->dbd_prev = UMOFF_NULL; + dbd_next->dbd_prev = dbd_prev_off; } - rc = umem_tx_add_ptr(umm, &cont_df->cd_dtx_committed_head, - sizeof(cont_df->cd_dtx_committed_head)); - if (unlikely(rc != 0)) { - D_ERROR("Failed to update head for DTX aggregation " UMOFF_PF ": " DF_RC - "\n", - UMOFF_P(dbd_off), DP_RC(rc)); - goto out; + if (dbd_prev == NULL) { + D_ASSERT(dbd_off == cont_df->cd_dtx_committed_head); + rc = umem_tx_add_ptr(umm, &cont_df->cd_dtx_committed_head, + sizeof(cont_df->cd_dtx_committed_head)); + if (unlikely(rc != 0)) { + D_ERROR("Failed to update head for DTX aggregation " UMOFF_PF + ": " DF_RC "\n", + UMOFF_P(dbd_off), DP_RC(rc)); + goto out; + } + cont_df->cd_dtx_committed_head = dbd_next_off; + } else { + rc = umem_tx_add_ptr(umm, &dbd_prev->dbd_next, sizeof(dbd_prev->dbd_next)); + if (unlikely(rc != 0)) { + D_ERROR( + "Failed to update next DTXs blob for DTX aggregation " UMOFF_PF + ": " DF_RC "\n", + UMOFF_P(dbd_off), DP_RC(rc)); + goto out; + } + dbd_prev->dbd_next = dbd_next_off; } - cont_df->cd_dtx_committed_head = next; rc = umem_free(umm, dbd_off); if (unlikely(rc != 0)) { @@ -3197,13 +3198,12 @@ vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max) } is_freed = true; } - dbd->dbd_count -= count; out: rc = umem_tx_end(umm, rc); if (likely(rc == 0)) { if (is_freed && cont->vc_cmt_dtx_reindex_pos == dbd_off) - cont->vc_cmt_dtx_reindex_pos = next; + cont->vc_cmt_dtx_reindex_pos = dbd_next_off; cont->vc_dtx_committed_count -= count; cont->vc_pool->vp_dtx_committed_count -= count; d_tm_dec_gauge(tls->vtl_committed, count); @@ -3216,6 +3216,53 @@ vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max) return rc; } +int +vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max, bool all_blobs) +{ + struct vos_container *cont; + struct vos_tls *tls; + struct umem_instance *umm; + struct vos_cont_df *cont_df; + struct vos_dtx_blob_df *dbd; + umem_off_t dbd_off; + int rc; + + cont = vos_hdl2cont(coh); + D_ASSERT(cont != NULL); + + tls = vos_tls_get(false); + D_ASSERT(tls != NULL); + + umm = vos_cont2umm(cont); + cont_df = cont->vc_cont_df; + dbd_off = cont_df->cd_dtx_committed_head; + + D_ASSERT(cont->vc_pool->vp_sysdb == false); + /* Take the opportunity to free some memory if we can */ + lrua_array_aggregate(cont->vc_dtx_array); + if (!all_blobs) { + rc = dtx_blob_aggregate(umm, tls, cont, cont_df, dbd_off, ep_max); + goto out; + } + + rc = 0; + dbd = umem_off2ptr(umm, dbd_off); + while (dbd != NULL) { + umem_off_t dbd_next_off; + + dbd_next_off = dbd->dbd_next; + rc = dtx_blob_aggregate(umm, tls, cont, cont_df, dbd_off, ep_max); + if (rc != 0) + goto out; + + dbd_off = dbd_next_off; + dbd = umem_off2ptr(umm, dbd_off); + } + +out: + return rc; +} + void vos_dtx_stat(daos_handle_t coh, struct dtx_stat *stat, uint32_t flags) { From 5e45d30a538ae6525f92c6100c8199189033cea1 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Thu, 2 Oct 2025 08:29:08 +0000 Subject: [PATCH 04/20] DAOS-17322 ddb: DTX aggregation with ddb Fix reviewers comments: - epoch of date option has to be defined Miscellaneous updates: - Fix invalid C return code - Improvement of error messages Signed-off-by: Cedric Koch-Hofer --- src/control/cmd/ddb/commands_wrapper.go | 12 ++++++++++-- src/control/cmd/ddb/main.go | 4 ++-- src/utils/ddb/ddb.c | 4 ++++ src/utils/ddb/tests/ddb_cmd_options_tests.c | 7 +------ 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/control/cmd/ddb/commands_wrapper.go b/src/control/cmd/ddb/commands_wrapper.go index 690fcc70c45..ebcd6d689d1 100644 --- a/src/control/cmd/ddb/commands_wrapper.go +++ b/src/control/cmd/ddb/commands_wrapper.go @@ -13,6 +13,7 @@ import ( "unsafe" "github.com/daos-stack/daos/src/control/lib/daos" + "github.com/daos-stack/daos/src/control/logging" ) /* @@ -36,7 +37,7 @@ func freeString(s *C.char) { } // InitDdb initializes the ddb context and returns a closure to finalize it. -func InitDdb() (*DdbContext, func(), error) { +func InitDdb(log *logging.LeveledLogger) (*DdbContext, func(), error) { // Must lock to OS thread because vos init/fini uses ABT init and finalize which must be called on the same thread runtime.LockOSThread() @@ -47,6 +48,7 @@ func InitDdb() (*DdbContext, func(), error) { ctx := &DdbContext{} C.ddb_ctx_init(&ctx.ctx) // Initialize with ctx default values + ctx.log = log return ctx, func() { C.ddb_fini() @@ -57,6 +59,7 @@ func InitDdb() (*DdbContext, func(), error) { // DdbContext structure for wrapping the C code context structure type DdbContext struct { ctx C.struct_ddb_ctx + log *logging.LeveledLogger } func ddbPoolIsOpen(ctx *DdbContext) bool { @@ -303,7 +306,12 @@ func ddbDtxStat(ctx *DdbContext, path string) error { func ddbDtxAggr(ctx *DdbContext, path string, epoch uint64, date string) error { if epoch != math.MaxUint64 && date != "" { - return daosError(C.DER_INVAL) + ctx.log.Error("'--epoch' and '--date' options are mutually exclusive") + return daosError(-C.DER_INVAL) + } + if epoch == math.MaxUint64 && date == "" { + ctx.log.Error("'--epoch' or '--date' option has to be defined") + return daosError(-C.DER_INVAL) } /* Set up the options */ diff --git a/src/control/cmd/ddb/main.go b/src/control/cmd/ddb/main.go index 4ee37947141..5fd0b167707 100644 --- a/src/control/cmd/ddb/main.go +++ b/src/control/cmd/ddb/main.go @@ -70,7 +70,7 @@ type ddbCmdStr string func (cmdStr ddbCmdStr) Complete(match string) (comps []flags.Completion) { // hack to get at command names - ctx, cleanup, err := InitDdb() + ctx, cleanup, err := InitDdb(nil) if err != nil { return } @@ -175,7 +175,7 @@ Example Paths: log.Debug("debug output enabled") } - ctx, cleanup, err := InitDdb() + ctx, cleanup, err := InitDdb(log) if err != nil { return errors.Wrap(err, "Error initializing the DDB Context") } diff --git a/src/utils/ddb/ddb.c b/src/utils/ddb/ddb.c index e0f79123155..16cfc8aa685 100644 --- a/src/utils/ddb/ddb.c +++ b/src/utils/ddb/ddb.c @@ -912,6 +912,10 @@ dtx_aggr_option_parse(struct ddb_ctx *ctx, struct dtx_aggr_options *cmd_args, ui return -DER_INVAL; } } + if (cmd_args->format == DDB_DTX_AGGR_NOW) { + ddb_error(ctx, "'--epoch' or '--date' option has to be defined\n"); + return -DER_INVAL; + } index = optind; if (argc - index > 0) { diff --git a/src/utils/ddb/tests/ddb_cmd_options_tests.c b/src/utils/ddb/tests/ddb_cmd_options_tests.c index 7f347e31361..7023b915f26 100644 --- a/src/utils/ddb/tests/ddb_cmd_options_tests.c +++ b/src/utils/ddb/tests/ddb_cmd_options_tests.c @@ -323,6 +323,7 @@ dtx_aggr_parsing(void **state) struct dtx_aggr_options *options = &info.dci_cmd_option.dci_dtx_aggr; /* test invalid arguments and options */ + test_run_inval_cmd("dtx_aggr", "path"); test_run_inval_cmd("dtx_aggr", "path", "extra"); test_run_inval_cmd("dtx_aggr", "-z"); test_run_inval_cmd("dtx_aggr", "-e", "foo", "path"); @@ -331,12 +332,6 @@ dtx_aggr_parsing(void **state) "path"); test_run_inval_cmd("dtx_aggr", "-e", "42", "-d", "1970-01-01 00:00:00", "path"); - /* test all arguments */ - test_run_cmd(&info, "dtx_aggr", "path"); - assert_non_null(options->path); - assert_null(options->epoch); - assert_null(options->date); - /* test all options and arguments */ test_run_cmd(&info, "dtx_aggr", "-e", "42", "path"); assert_non_null(options->path); From 4017abf4e4fc7d4827808a58ba78fb37b1539638 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Thu, 2 Oct 2025 09:11:14 +0000 Subject: [PATCH 05/20] DAOS-17322 ddb: DTX aggregation with ddb Fixed clang format issues. Signed-off-by: Cedric Koch-Hofer --- src/dtx/tests/dts_aggregate.c | 30 +++++++++++++++++------------- src/vos/vos_dtx.c | 3 +-- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/dtx/tests/dts_aggregate.c b/src/dtx/tests/dts_aggregate.c index 47e29c83f5e..1587479d5d3 100644 --- a/src/dtx/tests/dts_aggregate.c +++ b/src/dtx/tests/dts_aggregate.c @@ -92,7 +92,7 @@ tx_begin(struct umem_instance *umm, struct umem_tx_stage_data *txd) assert_ptr_equal(umm, &mock_pool.vp_umm); assert_null(txd); assert_int_equal(mock_umm_tx.count, 0); - rc = mock(); + rc = mock(); mock_umm_tx.in_tx = (rc == 0); return rc; } @@ -333,7 +333,7 @@ test_asserts(void **unused) static void test_tx_begin_error(void **unused) { - int rc; + int rc; prep_dtx_entries(); @@ -349,8 +349,8 @@ test_tx_begin_error(void **unused) static void test_dbtree_delete_error(void **unused) { - int i; - int rc; + int i; + int rc; prep_dtx_entries(); @@ -370,8 +370,8 @@ test_dbtree_delete_error(void **unused) static void test_newest_aggregated_error(void **unused) { - int i; - int rc; + int i; + int rc; prep_dtx_entries(); @@ -393,8 +393,8 @@ test_newest_aggregated_error(void **unused) static void test_committed_prev_error(void **unused) { - int i; - int rc; + int i; + int rc; prep_dtx_entries(); @@ -419,8 +419,8 @@ test_committed_prev_error(void **unused) static void test_committed_head_error(void **unused) { - int i; - int rc; + int i; + int rc; prep_dtx_entries(); @@ -553,8 +553,8 @@ test_committed_next_error(void **unused) static void test_umm_free_error(void **unused) { - int i; - int rc; + int i; + int rc; will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); @@ -952,9 +952,13 @@ test_second_blob(void **unused) assert_int_equal(mock_cont.vc_cmt_dtx_reindex_pos, mock_dbds_off[0]); } +/* clang-format off */ /* compilation unit's entry point */ #define TEST(name, func, setup_func, teardown_func) \ - {name ": vos_dtx_aggregate - " #func, func, setup_func, teardown_func} + { \ + name ": vos_dtx_aggregate - " #func, func, setup_func, teardown_func \ + } +/* clang-format on */ static const struct CMUnitTest vos_dtx_aggregate_tests_all[] = { TEST("DTX500", test_asserts, test_setup, test_teardown), diff --git a/src/vos/vos_dtx.c b/src/vos/vos_dtx.c index 6696528ac19..d0d512fe470 100644 --- a/src/vos/vos_dtx.c +++ b/src/vos/vos_dtx.c @@ -3065,8 +3065,6 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co int i; int rc; - epoch = cont_df->cd_newest_aggregated; - dbd = umem_off2ptr(umm, dbd_off); if (dbd == NULL) return 0; @@ -3079,6 +3077,7 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co } count = 0; + epoch = cont_df->cd_newest_aggregated; for (i = 0; i < dbd->dbd_count; i++) { struct vos_dtx_cmt_ent_df *dce_df; d_iov_t kiov; From 81ed63414d09fa615f207f52729f8e4ad0265693 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Thu, 2 Oct 2025 15:03:49 +0000 Subject: [PATCH 06/20] DAOS-17322 ddb: DTX aggregation with ddb Fix reviewers comments: - Aggregate all containers by default - Fix help Miscellaneous updates: - Code refactoring of the vos_dtx_stat to make it more redable Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/control/cmd/ddb/ddb_commands.go | 4 +- src/utils/ddb/ddb.c | 16 +- src/utils/ddb/ddb_commands.c | 169 +++++++++++++++----- src/utils/ddb/tests/ddb_cmd_options_tests.c | 11 ++ src/utils/ddb/tests/ddb_commands_tests.c | 10 ++ 5 files changed, 157 insertions(+), 53 deletions(-) diff --git a/src/control/cmd/ddb/ddb_commands.go b/src/control/cmd/ddb/ddb_commands.go index 3b78ea01be8..7083d747434 100644 --- a/src/control/cmd/ddb/ddb_commands.go +++ b/src/control/cmd/ddb/ddb_commands.go @@ -391,7 +391,7 @@ the path must include the extent, otherwise, it must not.`, LongHelp: "Print statistic on the DTX entries", HelpGroup: "vos", Args: func(a *grumble.Args) { - a.String("path", "Optional, VOS tree path to query.", grumble.Default("")) + a.String("path", "Optional, VOS tree path of a container to query.", grumble.Default("")) }, Run: func(c *grumble.Context) error { return ddbDtxStat(ctx, c.Args.String("path")) @@ -406,7 +406,7 @@ the path must include the extent, otherwise, it must not.`, LongHelp: "Aggregate DTX entries until a given epoch or date", HelpGroup: "vos", Args: func(a *grumble.Args) { - a.String("path", "VOS tree path to aggregate.") + a.String("path", "Optional, VOS tree path of a container to aggregate.", grumble.Default("")) }, Flags: func(f *grumble.Flags) { f.Uint64("e", "epoch", math.MaxUint64, "Max aggregation epoch") diff --git a/src/utils/ddb/ddb.c b/src/utils/ddb/ddb.c index 16cfc8aa685..5b82320b1eb 100644 --- a/src/utils/ddb/ddb.c +++ b/src/utils/ddb/ddb.c @@ -917,13 +917,11 @@ dtx_aggr_option_parse(struct ddb_ctx *ctx, struct dtx_aggr_options *cmd_args, ui return -DER_INVAL; } - index = optind; - if (argc - index > 0) { + index = optind; + cmd_args->path = NULL; + if (argc - index > 0 && *argv[index] != '\0') { cmd_args->path = argv[index]; index++; - } else { - ddb_error(ctx, "Expected argument 'path'\n"); - return -DER_INVAL; } if (argc - index > 0) { @@ -1486,14 +1484,14 @@ ddb_commands_help(struct ddb_ctx *ctx) ddb_print(ctx, "dtx_stat [path]\n"); ddb_print(ctx, "\tPrint statistic on the DTX entries.\n"); ddb_print(ctx, " [path]\n"); - ddb_print(ctx, "\tOptional, VOS tree path to query.\n"); + ddb_print(ctx, "\tOptional, VOS tree path of a container to query.\n"); ddb_print(ctx, "\n"); /* Command: dtx_aggr */ - ddb_print(ctx, "dtx_aggr \n"); + ddb_print(ctx, "dtx_aggr [path]\n"); ddb_print(ctx, "\tAggregate DTX entries until a given timestamp or duration.\n"); - ddb_print(ctx, " \n"); - ddb_print(ctx, "\tVOS tree path to aggregate.\n"); + ddb_print(ctx, " [path]\n"); + ddb_print(ctx, "\tOptional, VOS tree path of a container to aggregate.\n"); ddb_print(ctx, "Options:\n"); ddb_print(ctx, " -e, --epoch\n"); ddb_print(ctx, "\tMax aggregation epoch\n"); diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index 25dfc4ec64d..a7ee83e9dc3 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -1342,7 +1342,7 @@ struct dtx_stat_args { }; static int -dtx_stat(struct ddb_ctx *ctx, struct dv_indexed_tree_path *itp, uint32_t *cnt) +dtx_stat_cont(struct ddb_ctx *ctx, struct dv_indexed_tree_path *itp, uint32_t *cnt) { struct dtx_stat stat; daos_handle_t coh = {0}; @@ -1395,7 +1395,7 @@ dtx_stat_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type, itp_set_cont(&itp, entry->ie_couuid, args->cont_seen); ++(args->cont_seen); - rc = dtx_stat(ctx, &itp, &cnt); + rc = dtx_stat_cont(ctx, &itp, &cnt); if (!SUCCESS(rc)) goto done; args->cmt_cnt += cnt; @@ -1406,6 +1406,28 @@ dtx_stat_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type, return rc; } +static int +dtx_stat_path(struct ddb_ctx *ctx, char *path) +{ + struct dv_indexed_tree_path itp = {0}; + int rc; + + rc = init_path(ctx, path, &itp); + if (!SUCCESS(rc)) + goto done; + + if (!itp_has_cont(&itp)) { + ddb_errorf(ctx, "Invalid container path " DF_PATH ".\n", DP_PATH(path)); + rc = -DER_INVAL; + goto done; + } + + rc = dtx_stat_cont(ctx, &itp, NULL); +done: + itp_free(&itp); + return rc; +} + int ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt) { @@ -1416,25 +1438,13 @@ ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt) if (daos_handle_is_inval(ctx->dc_poh)) { ddb_error(ctx, "Not connected to a pool. Use 'open' to connect to a pool.\n"); - return -DER_NONEXIST; + rc = -DER_NONEXIST; + goto done; } if (opt->path != NULL && opt->path[0] != '\0') { - struct dv_indexed_tree_path itp = {0}; - - rc = init_path(ctx, opt->path, &itp); - if (!SUCCESS(rc)) - goto done; - - if (!itp_has_cont(&itp)) { - rc = -DER_INVAL; - goto done; - } - - rc = dtx_stat(ctx, &itp, NULL); -done: - itp_free(&itp); - return rc; + rc = dtx_stat_path(ctx, opt->path); + goto done; } args.ctx = ctx; @@ -1449,67 +1459,142 @@ ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt) ddb_printf(ctx, "Number of committed DTX of the pool:\t\t\t%" PRIu32 "\n", args.cmt_cnt); +done: return rc; } -int -ddb_run_dtx_aggr(struct ddb_ctx *ctx, struct dtx_aggr_options *opt) +struct dtx_aggr_args { + struct ddb_ctx *ctx; + uint32_t cont_seen; + uint64_t *epoch; +}; + +static int +dtx_aggr_cont(struct ddb_ctx *ctx, struct dv_indexed_tree_path *itp, uint64_t *epoch) +{ + daos_handle_t coh = {0}; + int rc; + + rc = dv_cont_open(ctx->dc_poh, itp_cont(itp), &coh); + if (!SUCCESS(rc)) + goto done; + + ddb_print(ctx, "Aggregating DTX entries of container "); + itp_print_full(ctx, itp); + ddb_print(ctx, "\n"); + rc = vos_dtx_aggregate(coh, epoch, true); + if (!SUCCESS(rc)) + ddb_errorf(ctx, "Aggregation of DTX entries failed: " DF_RC "\n", DP_RC(rc)); + +done: + dv_cont_close(&coh); + + return rc; +} + +static int +dtx_aggr_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type, + vos_iter_param_t *param, void *cb_arg, unsigned int *acts) { struct dv_indexed_tree_path itp = {0}; - daos_handle_t coh = {0}; - uint64_t *epoch; + struct dtx_aggr_args *args; int rc; - if (!ctx->dc_write_mode) { - ddb_error(ctx, error_msg_write_mode_only); - return -DER_INVAL; + if (type != VOS_ITER_COUUID) { + rc = 0; + goto done; } - rc = init_path(ctx, opt->path, &itp); + args = (struct dtx_aggr_args *)cb_arg; + + itp_set_cont(&itp, entry->ie_couuid, args->cont_seen); + ++(args->cont_seen); + + rc = dtx_aggr_cont(args->ctx, &itp, args->epoch); + +done: + itp_free(&itp); + + return rc; +} + +static int +dtx_aggr_path(struct ddb_ctx *ctx, char *path, uint64_t *epoch) +{ + struct dv_indexed_tree_path itp = {0}; + int rc; + + rc = init_path(ctx, path, &itp); if (!SUCCESS(rc)) goto done; if (!itp_has_cont(&itp)) { + ddb_errorf(ctx, "Invalid container path " DF_PATH ".\n", DP_PATH(path)); rc = -DER_INVAL; goto done; } - rc = dv_cont_open(ctx->dc_poh, itp_cont(&itp), &coh); - if (!SUCCESS(rc)) + rc = dtx_aggr_cont(ctx, &itp, epoch); + +done: + itp_free(&itp); + + return rc; +} + +int +ddb_run_dtx_aggr(struct ddb_ctx *ctx, struct dtx_aggr_options *opt) +{ + vos_iter_param_t param = {0}; + struct dtx_aggr_args args = {0}; + struct vos_iter_anchors anchors = {0}; + uint64_t epoch; + int rc; + + if (!ctx->dc_write_mode) { + ddb_error(ctx, error_msg_write_mode_only); + rc = -DER_INVAL; goto done; + } + + if (daos_handle_is_inval(ctx->dc_poh)) { + ddb_error(ctx, "Not connected to a pool. Use 'open' to connect to a pool.\n"); + rc = -DER_NONEXIST; + goto done; + } switch (opt->format) { case DDB_DTX_AGGR_NOW: - epoch = NULL; + args.epoch = NULL; break; case DDB_DTX_AGGR_DATE: - rc = ddb_date2epoch(optarg, &opt->epoch); + rc = ddb_date2epoch(optarg, &epoch); if (!SUCCESS(rc)) { ddb_error(ctx, "'--date' option arg date format is invalid\n"); goto done; } + args.epoch = &epoch; + break; case DDB_DTX_AGGR_EPOCH: - epoch = &opt->epoch; + args.epoch = &opt->epoch; break; default: D_ASSERT(false && "Invalid dtx_aggr options"); } - ddb_print(ctx, "Starting DTX entries aggregation of "); - itp_print_full(ctx, &itp); - ddb_print(ctx, "\n"); - rc = vos_dtx_aggregate(coh, epoch, true); - if (!SUCCESS(rc)) { - ddb_errorf(ctx, "Aggregation of DTX entries failed: " DF_RC "\n", DP_RC(rc)); + if (opt->path != NULL && opt->path[0] != '\0') { + rc = dtx_aggr_path(ctx, opt->path, args.epoch); goto done; } - ddb_print(ctx, "Aggregation of DTX is done\n"); - rc = 0; + args.ctx = ctx; + param.ip_hdl = ctx->dc_poh; + param.ip_epr.epr_hi = DAOS_EPOCH_MAX; + do { + rc = vos_iterate(¶m, VOS_ITER_COUUID, false, &anchors, NULL, dtx_aggr_cb, &args, + NULL); + } while (rc > 0); done: - itp_free(&itp); - dv_cont_close(&coh); - return rc; } diff --git a/src/utils/ddb/tests/ddb_cmd_options_tests.c b/src/utils/ddb/tests/ddb_cmd_options_tests.c index 7023b915f26..524873780e2 100644 --- a/src/utils/ddb/tests/ddb_cmd_options_tests.c +++ b/src/utils/ddb/tests/ddb_cmd_options_tests.c @@ -323,6 +323,7 @@ dtx_aggr_parsing(void **state) struct dtx_aggr_options *options = &info.dci_cmd_option.dci_dtx_aggr; /* test invalid arguments and options */ + test_run_inval_cmd("dtx_aggr"); test_run_inval_cmd("dtx_aggr", "path"); test_run_inval_cmd("dtx_aggr", "path", "extra"); test_run_inval_cmd("dtx_aggr", "-z"); @@ -333,6 +334,16 @@ dtx_aggr_parsing(void **state) test_run_inval_cmd("dtx_aggr", "-e", "42", "-d", "1970-01-01 00:00:00", "path"); /* test all options and arguments */ + test_run_cmd(&info, "dtx_aggr", "-e", "42"); + assert_null(options->path); + assert_non_null(options->epoch); + assert_null(options->date); + + test_run_cmd(&info, "dtx_aggr", "-d", "1970-01-01 00:00:00"); + assert_null(options->path); + assert_null(options->epoch); + assert_non_null(options->date); + test_run_cmd(&info, "dtx_aggr", "-e", "42", "path"); assert_non_null(options->path); assert_non_null(options->epoch); diff --git a/src/utils/ddb/tests/ddb_commands_tests.c b/src/utils/ddb/tests/ddb_commands_tests.c index bd36b13e05c..79d7a9ee541 100644 --- a/src/utils/ddb/tests/ddb_commands_tests.c +++ b/src/utils/ddb/tests/ddb_commands_tests.c @@ -634,6 +634,16 @@ dtx_aggr_tests(void **state) "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+0$"); assert_int_equal(get_epoch(dvt_fake_print_buffer), epochs->de_epochs[9]); + /* Test aggregaton of all the containers */ + opt_aggr.path = NULL; + assert_success(ddb_run_dtx_aggr(&ctx, &opt_aggr)); + + opt_stat.path = NULL; + dvt_fake_print_reset(); + assert_success(ddb_run_dtx_stat(&ctx, &opt_stat)); + assert_regex_match(dvt_fake_print_buffer, + "^Number of committed DTX of the pool:[[:blank:]]+0$"); + D_FREE(epochs); } From bf835020ba405b0575d92f0c183886d46e30864e Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Fri, 3 Oct 2025 13:55:57 +0000 Subject: [PATCH 07/20] DAOS-17322 ddb: DTX aggregation with ddb Fix following issues: - Process all DTX entries of a DTX entries during blob aggregation, as they are not sorted. - Remove useless call to tzset. Signtures: aggregation ed-off-by: Cedric Koch-Hofer --- src/utils/ddb/ddb_commands.c | 1 - src/vos/vos_dtx.c | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index a7ee83e9dc3..e27d66fa6dc 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -1293,7 +1293,6 @@ timespec2str(struct timespec *tspec, char *buf, size_t buf_size) size_t buf_len; int rc; - tzset(); if (localtime_r(&(tspec->tv_sec), &date) == NULL) return d_errno2der(errno); diff --git a/src/vos/vos_dtx.c b/src/vos/vos_dtx.c index d0d512fe470..0b238728323 100644 --- a/src/vos/vos_dtx.c +++ b/src/vos/vos_dtx.c @@ -3084,7 +3084,8 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co dce_df = &dbd->dbd_committed_data[i]; if (ep_max != NULL && *ep_max < dce_df->dce_epoch) - break; + continue; + d_iov_set(&kiov, &dce_df->dce_xid, sizeof(dce_df->dce_xid)); rc = dbtree_delete(cont->vc_dtx_committed_hdl, BTR_PROBE_EQ, &kiov, NULL); From 5a242a97d53add8385c1008c8213e429201da0c8 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Sun, 5 Oct 2025 16:56:59 +0000 Subject: [PATCH 08/20] DAOS-17322 ddb: DTX aggregation with ddb Revert fix solving the aggregation issue with unsorted blob DTX entries. Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/vos/vos_dtx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vos/vos_dtx.c b/src/vos/vos_dtx.c index 0b238728323..6ce746910d4 100644 --- a/src/vos/vos_dtx.c +++ b/src/vos/vos_dtx.c @@ -3084,7 +3084,7 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co dce_df = &dbd->dbd_committed_data[i]; if (ep_max != NULL && *ep_max < dce_df->dce_epoch) - continue; + break; d_iov_set(&kiov, &dce_df->dce_xid, sizeof(dce_df->dce_xid)); rc = dbtree_delete(cont->vc_dtx_committed_hdl, BTR_PROBE_EQ, From f35e5313aa36360cfe49b4eaa34ad3fed16722b5 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Tue, 7 Oct 2025 06:56:35 +0000 Subject: [PATCH 09/20] DAOS-17322 ddb: DTX aggregation with ddb Fix merging issue. Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/utils/ddb/ddb.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/ddb/ddb.h b/src/utils/ddb/ddb.h index 3d93b842c6b..9497d2cc04d 100644 --- a/src/utils/ddb/ddb.h +++ b/src/utils/ddb/ddb.h @@ -327,6 +327,7 @@ int ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt); int ddb_run_prov_mem(struct ddb_ctx *ctx, struct prov_mem_options *opt); +int ddb_run_dtx_aggr(struct ddb_ctx *ctx, struct dtx_aggr_options *opt); void From f474fcbdb3622e192f3dde3b2723c5a99faa6a12 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Mon, 13 Oct 2025 07:44:35 +0000 Subject: [PATCH 10/20] DAOS-17322 ddb: DTX aggregation with ddb Fix reviewers comments: - Remove useless initialisation - Refactor function name Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/control/cmd/ddb/commands_wrapper.go | 1 - src/utils/ddb/ddb_commands.c | 24 ++++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/control/cmd/ddb/commands_wrapper.go b/src/control/cmd/ddb/commands_wrapper.go index e31ad74500d..dabb6a5f34a 100644 --- a/src/control/cmd/ddb/commands_wrapper.go +++ b/src/control/cmd/ddb/commands_wrapper.go @@ -336,7 +336,6 @@ func ddbDtxAggr(ctx *DdbContext, path string, epoch uint64, date string) error { options := C.struct_dtx_aggr_options{} options.path = C.CString(path) defer freeString(options.path) - options.format = C.DDB_DTX_AGGR_NOW if epoch != math.MaxUint64 { options.format = C.DDB_DTX_AGGR_EPOCH options.epoch = C.uint64_t(epoch) diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index 6f72b3c9514..76dc8b1dfe3 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -1378,8 +1378,8 @@ dtx_stat_cont(struct ddb_ctx *ctx, struct dv_indexed_tree_path *itp, uint32_t *c } static int -dtx_stat_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type, - vos_iter_param_t *param, void *cb_arg, unsigned int *acts) +dtx_stat_cont_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type, + vos_iter_param_t *param, void *cb_arg, unsigned int *acts) { struct ddb_ctx *ctx; struct dv_indexed_tree_path itp = {0}; @@ -1410,7 +1410,7 @@ dtx_stat_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type, } static int -dtx_stat_path(struct ddb_ctx *ctx, char *path) +dtx_stat_cont_warp(struct ddb_ctx *ctx, char *path) { struct dv_indexed_tree_path itp = {0}; int rc; @@ -1446,7 +1446,7 @@ ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt) } if (opt->path != NULL && opt->path[0] != '\0') { - rc = dtx_stat_path(ctx, opt->path); + rc = dtx_stat_cont_warp(ctx, opt->path); goto done; } @@ -1455,8 +1455,8 @@ ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt) param.ip_hdl = ctx->dc_poh; param.ip_epr.epr_hi = DAOS_EPOCH_MAX; do { - rc = vos_iterate(¶m, VOS_ITER_COUUID, false, &anchors, NULL, dtx_stat_cb, &args, - NULL); + rc = vos_iterate(¶m, VOS_ITER_COUUID, false, &anchors, NULL, dtx_stat_cont_cb, + &args, NULL); } while (rc > 0); if (rc == 0) ddb_printf(ctx, "Number of committed DTX of the pool:\t\t\t%" PRIu32 "\n", @@ -1496,8 +1496,8 @@ dtx_aggr_cont(struct ddb_ctx *ctx, struct dv_indexed_tree_path *itp, uint64_t *e } static int -dtx_aggr_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type, - vos_iter_param_t *param, void *cb_arg, unsigned int *acts) +dtx_aggr_cont_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type, + vos_iter_param_t *param, void *cb_arg, unsigned int *acts) { struct dv_indexed_tree_path itp = {0}; struct dtx_aggr_args *args; @@ -1522,7 +1522,7 @@ dtx_aggr_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type, } static int -dtx_aggr_path(struct ddb_ctx *ctx, char *path, uint64_t *epoch) +dtx_aggr_cont_warp(struct ddb_ctx *ctx, char *path, uint64_t *epoch) { struct dv_indexed_tree_path itp = {0}; int rc; @@ -1586,7 +1586,7 @@ ddb_run_dtx_aggr(struct ddb_ctx *ctx, struct dtx_aggr_options *opt) } if (opt->path != NULL && opt->path[0] != '\0') { - rc = dtx_aggr_path(ctx, opt->path, args.epoch); + rc = dtx_aggr_cont_warp(ctx, opt->path, args.epoch); goto done; } @@ -1594,8 +1594,8 @@ ddb_run_dtx_aggr(struct ddb_ctx *ctx, struct dtx_aggr_options *opt) param.ip_hdl = ctx->dc_poh; param.ip_epr.epr_hi = DAOS_EPOCH_MAX; do { - rc = vos_iterate(¶m, VOS_ITER_COUUID, false, &anchors, NULL, dtx_aggr_cb, &args, - NULL); + rc = vos_iterate(¶m, VOS_ITER_COUUID, false, &anchors, NULL, dtx_aggr_cont_cb, + &args, NULL); } while (rc > 0); done: From 7a36240e4ae927264a104100ace0c52c3c7e26c3 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Mon, 13 Oct 2025 13:56:31 +0000 Subject: [PATCH 11/20] DAOS-17322 ddb: DTX aggregation with ddb Fix reviewers comments: - Loop over DTX blobs into ddb Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/dtx/dtx_common.c | 2 +- src/dtx/tests/dts_aggregate.c | 124 ++++++++++++++++++++++------------ src/include/daos_srv/vos.h | 3 +- src/utils/ddb/ddb_commands.c | 13 +++- src/vos/tests/vts_dtx.c | 2 +- src/vos/vos_dtx.c | 38 +++++------ 6 files changed, 113 insertions(+), 69 deletions(-) diff --git a/src/dtx/dtx_common.c b/src/dtx/dtx_common.c index 5d2424fc0f4..fc0d49b6768 100644 --- a/src/dtx/dtx_common.c +++ b/src/dtx/dtx_common.c @@ -430,7 +430,7 @@ dtx_aggregate(void *arg) struct dtx_stat stat = { 0 }; int rc; - rc = vos_dtx_aggregate(cont->sc_hdl, NULL, false); + rc = vos_dtx_aggregate(cont->sc_hdl, NULL, NULL); if (rc != 0) break; diff --git a/src/dtx/tests/dts_aggregate.c b/src/dtx/tests/dts_aggregate.c index 1587479d5d3..709fd6cfea6 100644 --- a/src/dtx/tests/dts_aggregate.c +++ b/src/dtx/tests/dts_aggregate.c @@ -317,16 +317,16 @@ test_asserts(void **unused) daos_handle_t hdl_null = {0}; /* Missing argument. */ - expect_assert_failure(vos_dtx_aggregate(hdl_null, NULL, true)); + expect_assert_failure(vos_dtx_aggregate(hdl_null, NULL, NULL)); /* Invalid Telemetry global variable. */ will_return(__wrap_vos_tls_get, NULL); - expect_assert_failure(vos_dtx_aggregate(mock_coh, NULL, true)); + expect_assert_failure(vos_dtx_aggregate(mock_coh, NULL, NULL)); /* Invalid pool type. */ will_return(__wrap_vos_tls_get, &mock_tls); mock_pool.vp_sysdb = true; - expect_assert_failure(vos_dtx_aggregate(mock_coh, NULL, true)); + expect_assert_failure(vos_dtx_aggregate(mock_coh, NULL, NULL)); } /* Can not start a PMEM transtation */ @@ -340,7 +340,7 @@ test_tx_begin_error(void **unused) will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, -DER_UNKNOWN); - rc = vos_dtx_aggregate(mock_coh, NULL, true); + rc = vos_dtx_aggregate(mock_coh, NULL, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -361,7 +361,7 @@ test_dbtree_delete_error(void **unused) will_return(__wrap_dbtree_delete, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(mock_coh, NULL, true); + rc = vos_dtx_aggregate(mock_coh, NULL, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -384,7 +384,7 @@ test_newest_aggregated_error(void **unused) will_return(tx_add_ptr, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(mock_coh, NULL, true); + rc = vos_dtx_aggregate(mock_coh, NULL, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -410,7 +410,7 @@ test_committed_prev_error(void **unused) will_return(tx_add_ptr, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(mock_coh, NULL, true); + rc = vos_dtx_aggregate(mock_coh, NULL, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -439,7 +439,7 @@ test_committed_head_error(void **unused) will_return(tx_add_ptr, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(mock_coh, NULL, false); + rc = vos_dtx_aggregate(mock_coh, NULL, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -448,16 +448,17 @@ test_committed_head_error(void **unused) static void test_committed_tail_error(void **unused) { + daos_epoch_t epoch; + umem_off_t dbd_off; int i; int rc; - daos_epoch_t epoch; prep_dtx_entries(); - will_return(__wrap_vos_tls_get, &mock_tls); for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { int j; + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); for (j = 0; j < DBD_BLOB_DF_CAP; j++) mock_dbds[i]->dbd_committed_data[j].dce_epoch += EPOCH_END; @@ -472,6 +473,8 @@ test_committed_tail_error(void **unused) expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); expect_value(__wrap_d_tm_dec_gauge, value, 0); } + + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); for (i = 0; i < DBD_BLOB_DF_CAP; i++) will_return(__wrap_dbtree_delete, 0); @@ -484,7 +487,12 @@ test_committed_tail_error(void **unused) expect_value(tx_abort, error, -DER_UNKNOWN); epoch = EPOCH_END; - rc = vos_dtx_aggregate(mock_coh, &epoch, true); + dbd_off = UMOFF_NULL; + for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { + rc = vos_dtx_aggregate(mock_coh, &epoch, &dbd_off); + assert_rc_equal(rc, 0); + } + rc = vos_dtx_aggregate(mock_coh, &epoch, &dbd_off); assert_rc_equal(rc, -DER_UNKNOWN); for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { int j; @@ -499,16 +507,17 @@ test_committed_tail_error(void **unused) static void test_committed_next_error(void **unused) { + daos_epoch_t epoch; + umem_off_t dbd_off; int i; int rc; - daos_epoch_t epoch; prep_dtx_entries(); - will_return(__wrap_vos_tls_get, &mock_tls); for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { int j; + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); for (j = 0; j < DBD_BLOB_DF_CAP; j++) mock_dbds[i]->dbd_committed_data[j].dce_epoch += EPOCH_END; @@ -523,6 +532,8 @@ test_committed_next_error(void **unused) expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); expect_value(__wrap_d_tm_dec_gauge, value, 0); } + + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); for (i = 0; i < DBD_BLOB_DF_CAP; i++) will_return(__wrap_dbtree_delete, 0); @@ -538,7 +549,12 @@ test_committed_next_error(void **unused) expect_value(tx_abort, error, -DER_UNKNOWN); epoch = EPOCH_END; - rc = vos_dtx_aggregate(mock_coh, &epoch, true); + dbd_off = UMOFF_NULL; + for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { + rc = vos_dtx_aggregate(mock_coh, &epoch, &dbd_off); + assert_rc_equal(rc, 0); + } + rc = vos_dtx_aggregate(mock_coh, &epoch, &dbd_off); assert_rc_equal(rc, -DER_UNKNOWN); for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { int j; @@ -575,7 +591,7 @@ test_umm_free_error(void **unused) will_return(tx_free, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(mock_coh, NULL, true); + rc = vos_dtx_aggregate(mock_coh, NULL, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -604,7 +620,7 @@ test_committed_data_error(void **unused) expect_value(tx_abort, error, -DER_UNKNOWN); epoch = EPOCH_START + (dtx_count - 1) * EPOCH_STEP; - rc = vos_dtx_aggregate(mock_coh, &epoch, true); + rc = vos_dtx_aggregate(mock_coh, &epoch, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -636,7 +652,7 @@ test_dbd_count_error(void **unused) expect_value(tx_abort, error, -DER_UNKNOWN); epoch = EPOCH_START + (dtx_count - 1) * EPOCH_STEP; - rc = vos_dtx_aggregate(mock_coh, &epoch, true); + rc = vos_dtx_aggregate(mock_coh, &epoch, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -668,7 +684,7 @@ test_umm_commit_error(void **unused) will_return(tx_commit, -DER_UNKNOWN); epoch = EPOCH_START + (dtx_count - 1) * EPOCH_STEP; - rc = vos_dtx_aggregate(mock_coh, &epoch, true); + rc = vos_dtx_aggregate(mock_coh, &epoch, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -681,7 +697,7 @@ test_empty(void **unused) will_return(__wrap_vos_tls_get, &mock_tls); - rc = vos_dtx_aggregate(mock_coh, NULL, false); + rc = vos_dtx_aggregate(mock_coh, NULL, NULL); assert_rc_equal(rc, 0); assert_int_equal(mock_cont_df.cd_newest_aggregated, 0); assert_int_equal(mock_cont_df.cd_dtx_committed_head, UMOFF_NULL); @@ -696,10 +712,10 @@ static void test_empty_blob(void **unused) { int i; - int rc; + umem_off_t dbd_off; - will_return(__wrap_vos_tls_get, &mock_tls); for (i = 0; i < DBD_BLOBS_CAP; i++) { + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); if (i == DBD_BLOBS_CAP - 1) expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_tail); @@ -717,8 +733,12 @@ test_empty_blob(void **unused) expect_value(__wrap_d_tm_dec_gauge, value, 0); } - rc = vos_dtx_aggregate(mock_coh, NULL, true); - assert_rc_equal(rc, 0); + dbd_off = UMOFF_NULL; + do { + int rc; + rc = vos_dtx_aggregate(mock_coh, NULL, &dbd_off); + assert_rc_equal(rc, 0); + } while (!UMOFF_IS_NULL(dbd_off)); assert_int_equal(mock_cont_df.cd_newest_aggregated, 0); assert_int_equal(mock_cont_df.cd_dtx_committed_head, UMOFF_NULL); assert_int_equal(mock_cont_df.cd_dtx_committed_tail, UMOFF_NULL); @@ -731,14 +751,14 @@ test_empty_blob(void **unused) static void test_10_entries(void **unused) { - int i; - int rc; daos_epoch_t epoch; + umem_off_t dbd_off; + int i; prep_dtx_entries(); - will_return(__wrap_vos_tls_get, &mock_tls); /* First DTX entries blob */ + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); for (i = 0; i < DBD_BLOB_DF_CAP; i++) will_return(__wrap_dbtree_delete, 0); @@ -756,7 +776,9 @@ test_10_entries(void **unused) will_return(tx_commit, 0); expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); expect_value(__wrap_d_tm_dec_gauge, value, DBD_BLOB_DF_CAP); + /* Second DTX entries blob */ + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); for (i = 0; i < 3; i++) will_return(__wrap_dbtree_delete, 0); @@ -773,7 +795,9 @@ test_10_entries(void **unused) will_return(tx_commit, 0); expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); expect_value(__wrap_d_tm_dec_gauge, value, 3); + /* Third DTX entries blob */ + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); expect_value(tx_add_ptr, ptr, &mock_dbds[2]->dbd_committed_data[0]); expect_value(tx_add_ptr, ptr_size, @@ -787,8 +811,12 @@ test_10_entries(void **unused) expect_value(__wrap_d_tm_dec_gauge, value, 0); epoch = EPOCH_START + (DBD_BLOB_DF_CAP + 2) * EPOCH_STEP; - rc = vos_dtx_aggregate(mock_coh, &epoch, true); - assert_rc_equal(rc, 0); + dbd_off = UMOFF_NULL; + do { + int rc; + rc = vos_dtx_aggregate(mock_coh, &epoch, &dbd_off); + assert_rc_equal(rc, 0); + } while (!UMOFF_IS_NULL(dbd_off)); assert_int_equal(mock_cont_df.cd_newest_aggregated, epoch); assert_int_equal(mock_cont_df.cd_dtx_committed_head, mock_dbds_off[1]); assert_int_equal(mock_cont_df.cd_dtx_committed_tail, mock_dbds_off[DBD_BLOBS_CAP - 1]); @@ -801,15 +829,15 @@ test_10_entries(void **unused) static void test_all_entries(void **unused) { - int i; - int rc; + umem_off_t dbd_off; + int i; prep_dtx_entries(); - will_return(__wrap_vos_tls_get, &mock_tls); for (i = 0; i < DBD_BLOBS_CAP; i++) { int j; + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); for (j = 0; j < DBD_BLOB_DF_CAP; j++) will_return(__wrap_dbtree_delete, 0); @@ -832,8 +860,12 @@ test_all_entries(void **unused) expect_value(__wrap_d_tm_dec_gauge, value, DBD_BLOB_DF_CAP); } - rc = vos_dtx_aggregate(mock_coh, NULL, true); - assert_rc_equal(rc, 0); + dbd_off = UMOFF_NULL; + do { + int rc; + rc = vos_dtx_aggregate(mock_coh, NULL, &dbd_off); + assert_rc_equal(rc, 0); + } while (!UMOFF_IS_NULL(dbd_off)); assert_int_equal(mock_cont_df.cd_newest_aggregated, EPOCH_START + (3 * DBD_BLOB_DF_CAP - 1) * EPOCH_STEP); assert_int_equal(mock_cont_df.cd_dtx_committed_head, UMOFF_NULL); @@ -871,7 +903,7 @@ test_one_blob(void **unused) expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); expect_value(__wrap_d_tm_dec_gauge, value, DBD_BLOB_DF_CAP); - rc = vos_dtx_aggregate(mock_coh, NULL, false); + rc = vos_dtx_aggregate(mock_coh, NULL, NULL); assert_rc_equal(rc, 0); assert_int_equal(mock_cont_df.cd_newest_aggregated, EPOCH_START + (DBD_BLOB_DF_CAP - 1) * EPOCH_STEP); @@ -886,9 +918,9 @@ test_one_blob(void **unused) static void test_second_blob(void **unused) { - int i; + umem_off_t dbd_off; daos_epoch_t epoch; - int rc; + int i; prep_dtx_entries(); for (i = 0; i < DBD_BLOB_DF_CAP; i++) { @@ -896,8 +928,8 @@ test_second_blob(void **unused) mock_dbds[2]->dbd_committed_data[i].dce_epoch += EPOCH_END; } - will_return(__wrap_vos_tls_get, &mock_tls); /* First DTX entries blob */ + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); expect_value(tx_add_ptr, ptr, &mock_dbds[0]->dbd_committed_data[0]); expect_value(tx_add_ptr, ptr_size, @@ -909,7 +941,9 @@ test_second_blob(void **unused) will_return(tx_commit, 0); expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); expect_value(__wrap_d_tm_dec_gauge, value, 0); + /* Second DTX entries blob */ + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); for (i = 0; i < DBD_BLOB_DF_CAP; i++) will_return(__wrap_dbtree_delete, 0); @@ -927,7 +961,9 @@ test_second_blob(void **unused) will_return(tx_commit, 0); expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); expect_value(__wrap_d_tm_dec_gauge, value, DBD_BLOB_DF_CAP); + /* Third DTX entries blob */ + will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); expect_value(tx_add_ptr, ptr, &mock_dbds[2]->dbd_committed_data[0]); expect_value(tx_add_ptr, ptr_size, @@ -941,8 +977,12 @@ test_second_blob(void **unused) expect_value(__wrap_d_tm_dec_gauge, value, 0); epoch = EPOCH_START + (DBD_BLOB_DF_CAP * 2) * EPOCH_STEP; - rc = vos_dtx_aggregate(mock_coh, &epoch, true); - assert_rc_equal(rc, 0); + dbd_off = UMOFF_NULL; + do { + int rc; + rc = vos_dtx_aggregate(mock_coh, &epoch, &dbd_off); + assert_rc_equal(rc, 0); + } while (!UMOFF_IS_NULL(dbd_off)); assert_int_equal(mock_cont_df.cd_newest_aggregated, EPOCH_START + (2 * DBD_BLOB_DF_CAP - 1) * EPOCH_STEP); assert_int_equal(mock_cont_df.cd_dtx_committed_head, mock_dbds_off[0]); @@ -955,9 +995,9 @@ test_second_blob(void **unused) /* clang-format off */ /* compilation unit's entry point */ #define TEST(name, func, setup_func, teardown_func) \ - { \ - name ": vos_dtx_aggregate - " #func, func, setup_func, teardown_func \ - } +{ \ + name ": vos_dtx_aggregate - " #func, func, setup_func, teardown_func \ +} /* clang-format on */ static const struct CMUnitTest vos_dtx_aggregate_tests_all[] = { diff --git a/src/include/daos_srv/vos.h b/src/include/daos_srv/vos.h index 5ad7e06e108..b7d9d604769 100644 --- a/src/include/daos_srv/vos.h +++ b/src/include/daos_srv/vos.h @@ -237,12 +237,11 @@ vos_dtx_set_flags(daos_handle_t coh, struct dtx_id dtis[], int count, uint32_t f * * \param coh [IN] Container open handle. * \param ep_max [IN] The upper epoch to aggregate. - * \param all_blobs [IN] Aggregate all DTX blob entries or just one. * * \return Zero on success, negative value if error. */ int -vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max, bool all_blobs); +vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max, umem_off_t *blob); /** * Query the container's DTXs statistics information. diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index 76dc8b1dfe3..1f4d1b82b69 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -1476,6 +1476,7 @@ static int dtx_aggr_cont(struct ddb_ctx *ctx, struct dv_indexed_tree_path *itp, uint64_t *epoch) { daos_handle_t coh = {0}; + umem_off_t dbd_off; int rc; rc = dv_cont_open(ctx->dc_poh, itp_cont(itp), &coh); @@ -1485,9 +1486,15 @@ dtx_aggr_cont(struct ddb_ctx *ctx, struct dv_indexed_tree_path *itp, uint64_t *e ddb_print(ctx, "Aggregating DTX entries of container "); itp_print_full(ctx, itp); ddb_print(ctx, "\n"); - rc = vos_dtx_aggregate(coh, epoch, true); - if (!SUCCESS(rc)) - ddb_errorf(ctx, "Aggregation of DTX entries failed: " DF_RC "\n", DP_RC(rc)); + dbd_off = UMOFF_NULL; + do { + rc = vos_dtx_aggregate(coh, epoch, &dbd_off); + if (!SUCCESS(rc)) { + ddb_errorf(ctx, "Aggregation of DTX entries failed: " DF_RC "\n", + DP_RC(rc)); + goto done; + } + } while (!UMOFF_IS_NULL(dbd_off)); done: dv_cont_close(&coh); diff --git a/src/vos/tests/vts_dtx.c b/src/vos/tests/vts_dtx.c index a808cdaddb7..d378da8845e 100644 --- a/src/vos/tests/vts_dtx.c +++ b/src/vos/tests/vts_dtx.c @@ -847,7 +847,7 @@ dtx_18(void **state) assert_int_equal(cnt, 10); /* Aggregate the DTXs. */ - rc = vos_dtx_aggregate(args->ctx.tc_co_hdl, NULL, true); + rc = vos_dtx_aggregate(args->ctx.tc_co_hdl, NULL, NULL); assert_rc_equal(rc, 0); rc = vos_dtx_get_cmt_cnt(args->ctx.tc_co_hdl, &cnt); diff --git a/src/vos/vos_dtx.c b/src/vos/vos_dtx.c index 6ce746910d4..f1cfecb05cb 100644 --- a/src/vos/vos_dtx.c +++ b/src/vos/vos_dtx.c @@ -3217,14 +3217,14 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co } int -vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max, bool all_blobs) +vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max, umem_off_t *blob) { struct vos_container *cont; struct vos_tls *tls; struct umem_instance *umm; struct vos_cont_df *cont_df; - struct vos_dtx_blob_df *dbd; umem_off_t dbd_off; + umem_off_t dbd_next_off; int rc; cont = vos_hdl2cont(coh); @@ -3236,30 +3236,28 @@ vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max, bool all_blobs) umm = vos_cont2umm(cont); cont_df = cont->vc_cont_df; dbd_off = cont_df->cd_dtx_committed_head; - - D_ASSERT(cont->vc_pool->vp_sysdb == false); - /* Take the opportunity to free some memory if we can */ - lrua_array_aggregate(cont->vc_dtx_array); - if (!all_blobs) { - rc = dtx_blob_aggregate(umm, tls, cont, cont_df, dbd_off, ep_max); - goto out; + if (UMOFF_IS_NULL(dbd_off)) { + rc = 0; + goto done; } - rc = 0; - dbd = umem_off2ptr(umm, dbd_off); - while (dbd != NULL) { - umem_off_t dbd_next_off; + if (blob != NULL) { + struct vos_dtx_blob_df *dbd; + if (!UMOFF_IS_NULL(*blob)) + dbd_off = *blob; + dbd = umem_off2ptr(umm, dbd_off); dbd_next_off = dbd->dbd_next; - rc = dtx_blob_aggregate(umm, tls, cont, cont_df, dbd_off, ep_max); - if (rc != 0) - goto out; - - dbd_off = dbd_next_off; - dbd = umem_off2ptr(umm, dbd_off); } -out: + D_ASSERT(cont->vc_pool->vp_sysdb == false); + /* Take the opportunity to free some memory if we can */ + lrua_array_aggregate(cont->vc_dtx_array); + rc = dtx_blob_aggregate(umm, tls, cont, cont_df, dbd_off, ep_max); + if (rc == 0 && blob != NULL) + *blob = dbd_next_off; + +done: return rc; } From f798b07e86676d1607a3ce25afcead6c5a91abc6 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Wed, 15 Oct 2025 19:18:20 +0000 Subject: [PATCH 12/20] DAOS-17322 ddb: DTX aggregation with ddb Fix reviewers comments: - Use dce_cmt_time to provide the max timestamp. - Add epoch statistic from PR 16954 to dtx_stat. Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/control/cmd/ddb/commands_wrapper.go | 27 +- src/control/cmd/ddb/ddb_commands.go | 13 +- src/dtx/dtx_common.c | 4 +- src/dtx/tests/SConscript | 2 +- src/dtx/tests/dts_aggregate.c | 468 ++++++-------------- src/dtx/tests/dts_cmt_stat.c | 190 ++++++++ src/dtx/tests/dts_count.c | 133 ------ src/dtx/tests/dtx_ut.c | 4 +- src/include/daos_srv/vos.h | 19 +- src/utils/ddb/ddb.c | 50 ++- src/utils/ddb/ddb.h | 7 +- src/utils/ddb/ddb_commands.c | 287 +++++++++--- src/utils/ddb/ddb_parse.c | 15 +- src/utils/ddb/ddb_parse.h | 2 +- src/utils/ddb/tests/ddb_cmd_options_tests.c | 16 +- src/utils/ddb/tests/ddb_cmocka.h | 12 + src/utils/ddb/tests/ddb_commands_tests.c | 214 +++------ src/utils/ddb/tests/ddb_parse_tests.c | 40 +- src/utils/ddb/tests/ddb_test_driver.h | 2 +- src/vos/tests/vts_dtx.c | 8 +- src/vos/vos_dtx.c | 209 +++++---- 21 files changed, 847 insertions(+), 875 deletions(-) create mode 100644 src/dtx/tests/dts_cmt_stat.c delete mode 100644 src/dtx/tests/dts_count.c diff --git a/src/control/cmd/ddb/commands_wrapper.go b/src/control/cmd/ddb/commands_wrapper.go index dabb6a5f34a..e393b7b7b47 100644 --- a/src/control/cmd/ddb/commands_wrapper.go +++ b/src/control/cmd/ddb/commands_wrapper.go @@ -300,10 +300,11 @@ func ddbDevReplace(ctx *DdbContext, db_path string, old_devid string, new_devid return daosError(C.ddb_run_dev_replace(&ctx.ctx, &options)) } -func ddbDtxStat(ctx *DdbContext, path string) error { +func ddbDtxStat(ctx *DdbContext, path string, details bool) error { /* Set up the options */ options := C.struct_dtx_stat_options{} options.path = C.CString(path) + options.details = C.bool(details) defer freeString(options.path) /* Run the c code command */ return daosError(C.ddb_run_dtx_stat(&ctx.ctx, &options)) @@ -322,13 +323,13 @@ func ddbProvMem(ctx *DdbContext, db_path string, tmpfs_mount string, tmpfs_mount return daosError(C.ddb_run_prov_mem(&ctx.ctx, &options)) } -func ddbDtxAggr(ctx *DdbContext, path string, epoch uint64, date string) error { - if epoch != math.MaxUint64 && date != "" { - ctx.log.Error("'--epoch' and '--date' options are mutually exclusive") +func ddbDtxAggr(ctx *DdbContext, path string, cmt_time uint64, cmt_date string) error { + if cmt_time != math.MaxUint64 && cmt_date != "" { + ctx.log.Error("'--cmt_time' and '--cmt_date' options are mutually exclusive") return daosError(-C.DER_INVAL) } - if epoch == math.MaxUint64 && date == "" { - ctx.log.Error("'--epoch' or '--date' option has to be defined") + if cmt_time == math.MaxUint64 && cmt_date == "" { + ctx.log.Error("'--cmt_time' or '--cmt_date' option has to be defined") return daosError(-C.DER_INVAL) } @@ -336,14 +337,14 @@ func ddbDtxAggr(ctx *DdbContext, path string, epoch uint64, date string) error { options := C.struct_dtx_aggr_options{} options.path = C.CString(path) defer freeString(options.path) - if epoch != math.MaxUint64 { - options.format = C.DDB_DTX_AGGR_EPOCH - options.epoch = C.uint64_t(epoch) + if cmt_time != math.MaxUint64 { + options.format = C.DDB_DTX_AGGR_CMT_TIME + options.cmt_time = C.uint64_t(cmt_time) } - if date != "" { - options.format = C.DDB_DTX_AGGR_DATE - options.date = C.CString(date) - defer freeString(options.date) + if cmt_date != "" { + options.format = C.DDB_DTX_AGGR_CMT_DATE + options.cmt_date = C.CString(cmt_date) + defer freeString(options.cmt_date) } /* Run the c code command */ return daosError(C.ddb_run_dtx_aggr(&ctx.ctx, &options)) diff --git a/src/control/cmd/ddb/ddb_commands.go b/src/control/cmd/ddb/ddb_commands.go index 8d3f9a1a5e2..bee8aeddc4d 100644 --- a/src/control/cmd/ddb/ddb_commands.go +++ b/src/control/cmd/ddb/ddb_commands.go @@ -393,11 +393,14 @@ the path must include the extent, otherwise, it must not.`, Help: "Stat on DTX entries", LongHelp: "Print statistic on the DTX entries", HelpGroup: "vos", + Flags: func(a *grumble.Flags) { + a.Bool("d", "details", false, "Show detailed time related stats.") + }, Args: func(a *grumble.Args) { a.String("path", "Optional, VOS tree path of a container to query.", grumble.Default("")) }, Run: func(c *grumble.Context) error { - return ddbDtxStat(ctx, c.Args.String("path")) + return ddbDtxStat(ctx, c.Args.String("path"), c.Flags.Bool("details")) }, Completer: nil, }) @@ -425,17 +428,17 @@ the path must include the extent, otherwise, it must not.`, Name: "dtx_aggr", Aliases: nil, Help: "Aggregate DTX entries", - LongHelp: "Aggregate DTX entries until a given epoch or date", + LongHelp: "Aggregate DTX entries until a given aggregation commit time or date", HelpGroup: "vos", Args: func(a *grumble.Args) { a.String("path", "Optional, VOS tree path of a container to aggregate.", grumble.Default("")) }, Flags: func(f *grumble.Flags) { - f.Uint64("e", "epoch", math.MaxUint64, "Max aggregation epoch") - f.String("d", "date", "", "Max aggregation date (format '1970-01-01 00:00:00')") + f.Uint64("t", "cmt_time", math.MaxUint64, "Max aggregfation committed time in seconds") + f.String("d", "cmt_date", "", "Max aggregation committed date (format '1970-01-01 00:00:00')") }, Run: func(c *grumble.Context) error { - return ddbDtxAggr(ctx, c.Args.String("path"), c.Flags.Uint64("epoch"), c.Flags.String("date")) + return ddbDtxAggr(ctx, c.Args.String("path"), c.Flags.Uint64("cmt_time"), c.Flags.String("cmt_date")) }, Completer: nil, }) diff --git a/src/dtx/dtx_common.c b/src/dtx/dtx_common.c index fc0d49b6768..175f440dc4e 100644 --- a/src/dtx/dtx_common.c +++ b/src/dtx/dtx_common.c @@ -430,8 +430,8 @@ dtx_aggregate(void *arg) struct dtx_stat stat = { 0 }; int rc; - rc = vos_dtx_aggregate(cont->sc_hdl, NULL, NULL); - if (rc != 0) + rc = vos_dtx_aggregate(cont->sc_hdl, NULL); + if (rc < 0) break; ABT_thread_yield(); diff --git a/src/dtx/tests/SConscript b/src/dtx/tests/SConscript index 295319c4b01..2ea2e93eec5 100644 --- a/src/dtx/tests/SConscript +++ b/src/dtx/tests/SConscript @@ -65,7 +65,7 @@ def scons(): 'sched_mock.c', 'srv_mock.c', 'srv_module_mock.c', - 'dts_count.c', + 'dts_cmt_stat.c', 'dts_aggregate.c' ] dtx_ut = tenv.d_program('dtx_ut', test_src + vos_src, LIBS=libraries) diff --git a/src/dtx/tests/dts_aggregate.c b/src/dtx/tests/dts_aggregate.c index 709fd6cfea6..0bc06e1c3b8 100644 --- a/src/dtx/tests/dts_aggregate.c +++ b/src/dtx/tests/dts_aggregate.c @@ -27,9 +27,10 @@ #define DTX_CMT_BLOB_MAGIC 0x2502191c #define DBD_BLOBS_CAP 0x3 #define DBD_BLOB_DF_CAP 0x8 -#define EPOCH_START 0x1000 -#define EPOCH_END 0x10000 -#define EPOCH_STEP 0x10 +#define CMT_TIME_START 0x1000 +#define CMT_TIME_END 0x10000 +#define CMT_TIME_STEP 0x10 +#define EPOCH_OFFSET 0x20000 #define MOCK_UMM_TX_MAX 0x100 #define CELL_SIZE \ sizeof(struct vos_dtx_blob_df) + DBD_BLOB_DF_CAP * sizeof(struct vos_dtx_cmt_ent_df) @@ -169,9 +170,9 @@ static void prep_dtx_entries(void) { int i; - daos_epoch_t epoch; + uint64_t cmt_time; - epoch = EPOCH_START; + cmt_time = CMT_TIME_START; for (i = 0; i < DBD_BLOBS_CAP; i++) { int j; @@ -179,8 +180,9 @@ prep_dtx_entries(void) struct vos_dtx_cmt_ent_df *dce_df; dce_df = &mock_dbds[i]->dbd_committed_data[j]; - dce_df->dce_epoch = epoch; - epoch += EPOCH_STEP; + dce_df->dce_cmt_time = cmt_time; + dce_df->dce_epoch = cmt_time + EPOCH_OFFSET; + cmt_time += CMT_TIME_STEP; } mock_dbds[i]->dbd_count = DBD_BLOB_DF_CAP; } @@ -192,15 +194,18 @@ static void check_rollback(void) { int i; - daos_epoch_t epoch; + uint64_t cmt_time; - epoch = EPOCH_START; + cmt_time = CMT_TIME_START; for (i = 0; i < DBD_BLOBS_CAP; i++) { int j; for (j = 0; j < DBD_BLOB_DF_CAP; j++) { - assert_int_equal(mock_dbds[i]->dbd_committed_data[j].dce_epoch, epoch); - epoch += EPOCH_STEP; + assert_int_equal(mock_dbds[i]->dbd_committed_data[j].dce_cmt_time, + cmt_time); + assert_int_equal(mock_dbds[i]->dbd_committed_data[j].dce_epoch, + cmt_time + EPOCH_OFFSET); + cmt_time += CMT_TIME_STEP; } assert_int_equal(mock_dbds[i]->dbd_count, DBD_BLOB_DF_CAP); } @@ -316,17 +321,18 @@ test_asserts(void **unused) { daos_handle_t hdl_null = {0}; - /* Missing argument. */ - expect_assert_failure(vos_dtx_aggregate(hdl_null, NULL, NULL)); - /* Invalid Telemetry global variable. */ will_return(__wrap_vos_tls_get, NULL); - expect_assert_failure(vos_dtx_aggregate(mock_coh, NULL, NULL)); + expect_assert_failure(vos_dtx_aggregate(mock_coh, NULL)); + + /* Missing argument. */ + will_return(__wrap_vos_tls_get, NULL); + expect_assert_failure(vos_dtx_aggregate(hdl_null, NULL)); /* Invalid pool type. */ will_return(__wrap_vos_tls_get, &mock_tls); mock_pool.vp_sysdb = true; - expect_assert_failure(vos_dtx_aggregate(mock_coh, NULL, NULL)); + expect_assert_failure(vos_dtx_aggregate(mock_coh, NULL)); } /* Can not start a PMEM transtation */ @@ -340,7 +346,7 @@ test_tx_begin_error(void **unused) will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, -DER_UNKNOWN); - rc = vos_dtx_aggregate(mock_coh, NULL, NULL); + rc = vos_dtx_aggregate(mock_coh, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -361,7 +367,7 @@ test_dbtree_delete_error(void **unused) will_return(__wrap_dbtree_delete, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(mock_coh, NULL, NULL); + rc = vos_dtx_aggregate(mock_coh, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -384,14 +390,14 @@ test_newest_aggregated_error(void **unused) will_return(tx_add_ptr, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(mock_coh, NULL, NULL); + rc = vos_dtx_aggregate(mock_coh, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } /* Update of DTX blob list failure */ static void -test_committed_prev_error(void **unused) +test_committed_head_error(void **unused) { int i; int rc; @@ -405,19 +411,19 @@ test_committed_prev_error(void **unused) expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[1]->dbd_prev)); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); will_return(tx_add_ptr, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(mock_coh, NULL, NULL); + rc = vos_dtx_aggregate(mock_coh, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } /* Update of DTX blob list failure */ static void -test_committed_head_error(void **unused) +test_committed_prev_error(void **unused) { int i; int rc; @@ -431,137 +437,16 @@ test_committed_head_error(void **unused) expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[1]->dbd_prev)); - will_return(tx_add_ptr, 0); expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_head)); - will_return(tx_add_ptr, -DER_UNKNOWN); - expect_value(tx_abort, error, -DER_UNKNOWN); - - rc = vos_dtx_aggregate(mock_coh, NULL, NULL); - assert_rc_equal(rc, -DER_UNKNOWN); - check_rollback(); -} - -/* Update of DTX blob list failure */ -static void -test_committed_tail_error(void **unused) -{ - daos_epoch_t epoch; - umem_off_t dbd_off; - int i; - int rc; - - prep_dtx_entries(); - - for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { - int j; - - will_return(__wrap_vos_tls_get, &mock_tls); - will_return(tx_begin, 0); - for (j = 0; j < DBD_BLOB_DF_CAP; j++) - mock_dbds[i]->dbd_committed_data[j].dce_epoch += EPOCH_END; - expect_value(tx_add_ptr, ptr, &mock_dbds[i]->dbd_committed_data[0]); - expect_value(tx_add_ptr, ptr_size, - sizeof(mock_dbds[i]->dbd_committed_data[0]) * mock_dbds[i]->dbd_count); - will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[i]->dbd_count); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[i]->dbd_count)); - will_return(tx_add_ptr, 0); - will_return(tx_commit, 0); - expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); - expect_value(__wrap_d_tm_dec_gauge, value, 0); - } - - will_return(__wrap_vos_tls_get, &mock_tls); - will_return(tx_begin, 0); - for (i = 0; i < DBD_BLOB_DF_CAP; i++) - will_return(__wrap_dbtree_delete, 0); - expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); - will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_tail); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_tail)); - will_return(tx_add_ptr, -DER_UNKNOWN); - expect_value(tx_abort, error, -DER_UNKNOWN); - - epoch = EPOCH_END; - dbd_off = UMOFF_NULL; - for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { - rc = vos_dtx_aggregate(mock_coh, &epoch, &dbd_off); - assert_rc_equal(rc, 0); - } - rc = vos_dtx_aggregate(mock_coh, &epoch, &dbd_off); - assert_rc_equal(rc, -DER_UNKNOWN); - for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { - int j; - - for (j = 0; j < DBD_BLOB_DF_CAP; j++) - mock_dbds[i]->dbd_committed_data[j].dce_epoch -= EPOCH_END; - } - check_rollback(); -} - -/* Update of DTX blob list failure */ -static void -test_committed_next_error(void **unused) -{ - daos_epoch_t epoch; - umem_off_t dbd_off; - int i; - int rc; - - prep_dtx_entries(); - - for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { - int j; - - will_return(__wrap_vos_tls_get, &mock_tls); - will_return(tx_begin, 0); - for (j = 0; j < DBD_BLOB_DF_CAP; j++) - mock_dbds[i]->dbd_committed_data[j].dce_epoch += EPOCH_END; - expect_value(tx_add_ptr, ptr, &mock_dbds[i]->dbd_committed_data[0]); - expect_value(tx_add_ptr, ptr_size, - sizeof(mock_dbds[i]->dbd_committed_data[0]) * mock_dbds[i]->dbd_count); - will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[i]->dbd_count); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[i]->dbd_count)); - will_return(tx_add_ptr, 0); - will_return(tx_commit, 0); - expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); - expect_value(__wrap_d_tm_dec_gauge, value, 0); - } - - will_return(__wrap_vos_tls_get, &mock_tls); - will_return(tx_begin, 0); - for (i = 0; i < DBD_BLOB_DF_CAP; i++) - will_return(__wrap_dbtree_delete, 0); - expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); - will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_tail); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_tail)); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_next); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[1]->dbd_next)); + expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); will_return(tx_add_ptr, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - epoch = EPOCH_END; - dbd_off = UMOFF_NULL; - for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { - rc = vos_dtx_aggregate(mock_coh, &epoch, &dbd_off); - assert_rc_equal(rc, 0); - } - rc = vos_dtx_aggregate(mock_coh, &epoch, &dbd_off); + rc = vos_dtx_aggregate(mock_coh, NULL); assert_rc_equal(rc, -DER_UNKNOWN); - for (i = 0; i < DBD_BLOBS_CAP - 1; i++) { - int j; - - for (j = 0; j < DBD_BLOB_DF_CAP; j++) - mock_dbds[i]->dbd_committed_data[j].dce_epoch -= EPOCH_END; - } check_rollback(); } @@ -580,18 +465,18 @@ test_umm_free_error(void **unused) expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[1]->dbd_prev)); - will_return(tx_add_ptr, 0); expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_head)); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); will_return(tx_add_ptr, 0); expect_value(tx_free, umoff, mock_dbds_off[0]); will_return(tx_free, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - rc = vos_dtx_aggregate(mock_coh, NULL, NULL); + rc = vos_dtx_aggregate(mock_coh, NULL); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -600,7 +485,7 @@ test_umm_free_error(void **unused) static void test_committed_data_error(void **unused) { - daos_epoch_t epoch; + uint64_t cmt_time; int i; int rc; const int dtx_count = 3; @@ -619,8 +504,8 @@ test_committed_data_error(void **unused) will_return(tx_add_ptr, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - epoch = EPOCH_START + (dtx_count - 1) * EPOCH_STEP; - rc = vos_dtx_aggregate(mock_coh, &epoch, NULL); + cmt_time = CMT_TIME_START + (dtx_count - 1) * CMT_TIME_STEP; + rc = vos_dtx_aggregate(mock_coh, &cmt_time); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -629,7 +514,7 @@ test_committed_data_error(void **unused) static void test_dbd_count_error(void **unused) { - daos_epoch_t epoch; + uint64_t cmt_time; int i; int rc; const int dtx_count = 3; @@ -651,8 +536,8 @@ test_dbd_count_error(void **unused) will_return(tx_add_ptr, -DER_UNKNOWN); expect_value(tx_abort, error, -DER_UNKNOWN); - epoch = EPOCH_START + (dtx_count - 1) * EPOCH_STEP; - rc = vos_dtx_aggregate(mock_coh, &epoch, NULL); + cmt_time = CMT_TIME_START + (dtx_count - 1) * CMT_TIME_STEP; + rc = vos_dtx_aggregate(mock_coh, &cmt_time); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -661,7 +546,7 @@ test_dbd_count_error(void **unused) static void test_umm_commit_error(void **unused) { - daos_epoch_t epoch; + uint64_t cmt_time; int i; int rc; const int dtx_count = 3; @@ -683,8 +568,8 @@ test_umm_commit_error(void **unused) will_return(tx_add_ptr, 0); will_return(tx_commit, -DER_UNKNOWN); - epoch = EPOCH_START + (dtx_count - 1) * EPOCH_STEP; - rc = vos_dtx_aggregate(mock_coh, &epoch, NULL); + cmt_time = CMT_TIME_START + (dtx_count - 1) * CMT_TIME_STEP; + rc = vos_dtx_aggregate(mock_coh, &cmt_time); assert_rc_equal(rc, -DER_UNKNOWN); check_rollback(); } @@ -697,7 +582,7 @@ test_empty(void **unused) will_return(__wrap_vos_tls_get, &mock_tls); - rc = vos_dtx_aggregate(mock_coh, NULL, NULL); + rc = vos_dtx_aggregate(mock_coh, NULL); assert_rc_equal(rc, 0); assert_int_equal(mock_cont_df.cd_newest_aggregated, 0); assert_int_equal(mock_cont_df.cd_dtx_committed_head, UMOFF_NULL); @@ -712,33 +597,29 @@ static void test_empty_blob(void **unused) { int i; - umem_off_t dbd_off; + int rc; for (i = 0; i < DBD_BLOBS_CAP; i++) { will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); + will_return(tx_add_ptr, 0); if (i == DBD_BLOBS_CAP - 1) expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_tail); else expect_value(tx_add_ptr, ptr, &mock_dbds[i + 1]->dbd_prev); expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); - expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); - will_return(tx_add_ptr, 0); expect_value(tx_free, umoff, mock_dbds_off[i]); will_return(tx_free, 0); will_return(tx_commit, 0); - expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); - expect_value(__wrap_d_tm_dec_gauge, value, 0); } - dbd_off = UMOFF_NULL; do { - int rc; - rc = vos_dtx_aggregate(mock_coh, NULL, &dbd_off); - assert_rc_equal(rc, 0); - } while (!UMOFF_IS_NULL(dbd_off)); + rc = vos_dtx_aggregate(mock_coh, NULL); + assert_true(rc >= 0); + } while (rc > 0); assert_int_equal(mock_cont_df.cd_newest_aggregated, 0); assert_int_equal(mock_cont_df.cd_dtx_committed_head, UMOFF_NULL); assert_int_equal(mock_cont_df.cd_dtx_committed_tail, UMOFF_NULL); @@ -747,13 +628,52 @@ test_empty_blob(void **unused) assert_int_equal(mock_cont.vc_cmt_dtx_reindex_pos, UMOFF_NULL); } +/* Aggregation of all the committed DTX transactions of one blob */ +static void +test_one_blob(void **unused) +{ + int i; + int rc; + + prep_dtx_entries(); + + will_return(__wrap_vos_tls_get, &mock_tls); + will_return(tx_begin, 0); + for (i = 0; i < DBD_BLOB_DF_CAP; i++) + will_return(__wrap_dbtree_delete, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); + expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); + will_return(tx_add_ptr, 0); + expect_value(tx_free, umoff, mock_dbds_off[0]); + will_return(tx_free, 0); + will_return(tx_commit, 0); + expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); + expect_value(__wrap_d_tm_dec_gauge, value, DBD_BLOB_DF_CAP); + + rc = vos_dtx_aggregate(mock_coh, NULL); + assert_rc_equal(rc, 1); + assert_int_equal(mock_cont_df.cd_newest_aggregated, + EPOCH_OFFSET + CMT_TIME_START + (DBD_BLOB_DF_CAP - 1) * CMT_TIME_STEP); + assert_int_equal(mock_cont_df.cd_dtx_committed_head, mock_dbds_off[1]); + assert_int_equal(mock_cont_df.cd_dtx_committed_tail, mock_dbds_off[DBD_BLOBS_CAP - 1]); + assert_int_equal(mock_cont.vc_dtx_committed_count, DBD_BLOB_DF_CAP * (DBD_BLOBS_CAP - 1)); + assert_int_equal(mock_pool.vp_dtx_committed_count, DBD_BLOB_DF_CAP * (DBD_BLOBS_CAP - 1)); + assert_int_equal(mock_cont.vc_cmt_dtx_reindex_pos, mock_dbds_off[1]); +} + /* Aggregation of the first 10 committed DTX transactions */ static void test_10_entries(void **unused) { - daos_epoch_t epoch; - umem_off_t dbd_off; + uint64_t cmt_time; int i; + int rc; prep_dtx_entries(); @@ -765,11 +685,11 @@ test_10_entries(void **unused) expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[1]->dbd_prev)); - will_return(tx_add_ptr, 0); expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_head)); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); + will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); will_return(tx_add_ptr, 0); expect_value(tx_free, umoff, mock_dbds_off[0]); will_return(tx_free, 0); @@ -796,28 +716,12 @@ test_10_entries(void **unused) expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); expect_value(__wrap_d_tm_dec_gauge, value, 3); - /* Third DTX entries blob */ - will_return(__wrap_vos_tls_get, &mock_tls); - will_return(tx_begin, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[2]->dbd_committed_data[0]); - expect_value(tx_add_ptr, ptr_size, - sizeof(mock_dbds[2]->dbd_committed_data[0]) * mock_dbds[2]->dbd_count); - will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[2]->dbd_count); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[2]->dbd_count)); - will_return(tx_add_ptr, 0); - will_return(tx_commit, 0); - expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); - expect_value(__wrap_d_tm_dec_gauge, value, 0); - - epoch = EPOCH_START + (DBD_BLOB_DF_CAP + 2) * EPOCH_STEP; - dbd_off = UMOFF_NULL; + cmt_time = CMT_TIME_START + (DBD_BLOB_DF_CAP + 2) * CMT_TIME_STEP; do { - int rc; - rc = vos_dtx_aggregate(mock_coh, &epoch, &dbd_off); - assert_rc_equal(rc, 0); - } while (!UMOFF_IS_NULL(dbd_off)); - assert_int_equal(mock_cont_df.cd_newest_aggregated, epoch); + rc = vos_dtx_aggregate(mock_coh, &cmt_time); + assert_true(rc >= 0); + } while (rc > 0); + assert_int_equal(mock_cont_df.cd_newest_aggregated, EPOCH_OFFSET + cmt_time); assert_int_equal(mock_cont_df.cd_dtx_committed_head, mock_dbds_off[1]); assert_int_equal(mock_cont_df.cd_dtx_committed_tail, mock_dbds_off[DBD_BLOBS_CAP - 1]); assert_int_equal(mock_cont.vc_dtx_committed_count, DBD_BLOB_DF_CAP * 2 - 3); @@ -829,8 +733,8 @@ test_10_entries(void **unused) static void test_all_entries(void **unused) { - umem_off_t dbd_off; - int i; + int i; + int rc; prep_dtx_entries(); @@ -844,15 +748,15 @@ test_all_entries(void **unused) expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); will_return(tx_add_ptr, 0); + expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); + expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); + will_return(tx_add_ptr, 0); if (i == DBD_BLOBS_CAP - 1) expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_tail); else expect_value(tx_add_ptr, ptr, &mock_dbds[i + 1]->dbd_prev); expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_head)); - will_return(tx_add_ptr, 0); expect_value(tx_free, umoff, mock_dbds_off[i]); will_return(tx_free, 0); will_return(tx_commit, 0); @@ -860,14 +764,12 @@ test_all_entries(void **unused) expect_value(__wrap_d_tm_dec_gauge, value, DBD_BLOB_DF_CAP); } - dbd_off = UMOFF_NULL; do { - int rc; - rc = vos_dtx_aggregate(mock_coh, NULL, &dbd_off); - assert_rc_equal(rc, 0); - } while (!UMOFF_IS_NULL(dbd_off)); + rc = vos_dtx_aggregate(mock_coh, NULL); + assert_true(rc >= 0); + } while (rc > 0); assert_int_equal(mock_cont_df.cd_newest_aggregated, - EPOCH_START + (3 * DBD_BLOB_DF_CAP - 1) * EPOCH_STEP); + EPOCH_OFFSET + CMT_TIME_START + (3 * DBD_BLOB_DF_CAP - 1) * CMT_TIME_STEP); assert_int_equal(mock_cont_df.cd_dtx_committed_head, UMOFF_NULL); assert_int_equal(mock_cont_df.cd_dtx_committed_tail, UMOFF_NULL); assert_int_equal(mock_cont.vc_dtx_committed_count, 0); @@ -875,128 +777,11 @@ test_all_entries(void **unused) assert_int_equal(mock_cont.vc_cmt_dtx_reindex_pos, UMOFF_NULL); } -/* Aggregation of all the committed DTX transactions of one blob */ -static void -test_one_blob(void **unused) -{ - int i; - int rc; - - prep_dtx_entries(); - - will_return(__wrap_vos_tls_get, &mock_tls); - will_return(tx_begin, 0); - for (i = 0; i < DBD_BLOB_DF_CAP; i++) - will_return(__wrap_dbtree_delete, 0); - expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); - will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[1]->dbd_prev); - expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); - will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_dtx_committed_head); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_dtx_committed_head)); - will_return(tx_add_ptr, 0); - expect_value(tx_free, umoff, mock_dbds_off[0]); - will_return(tx_free, 0); - will_return(tx_commit, 0); - expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); - expect_value(__wrap_d_tm_dec_gauge, value, DBD_BLOB_DF_CAP); - - rc = vos_dtx_aggregate(mock_coh, NULL, NULL); - assert_rc_equal(rc, 0); - assert_int_equal(mock_cont_df.cd_newest_aggregated, - EPOCH_START + (DBD_BLOB_DF_CAP - 1) * EPOCH_STEP); - assert_int_equal(mock_cont_df.cd_dtx_committed_head, mock_dbds_off[1]); - assert_int_equal(mock_cont_df.cd_dtx_committed_tail, mock_dbds_off[DBD_BLOBS_CAP - 1]); - assert_int_equal(mock_cont.vc_dtx_committed_count, DBD_BLOB_DF_CAP * (DBD_BLOBS_CAP - 1)); - assert_int_equal(mock_pool.vp_dtx_committed_count, DBD_BLOB_DF_CAP * (DBD_BLOBS_CAP - 1)); - assert_int_equal(mock_cont.vc_cmt_dtx_reindex_pos, mock_dbds_off[1]); -} - -/* Aggregation of all the committed DTX transactions of the second blob */ -static void -test_second_blob(void **unused) -{ - umem_off_t dbd_off; - daos_epoch_t epoch; - int i; - - prep_dtx_entries(); - for (i = 0; i < DBD_BLOB_DF_CAP; i++) { - mock_dbds[0]->dbd_committed_data[i].dce_epoch += EPOCH_END; - mock_dbds[2]->dbd_committed_data[i].dce_epoch += EPOCH_END; - } - - /* First DTX entries blob */ - will_return(__wrap_vos_tls_get, &mock_tls); - will_return(tx_begin, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[0]->dbd_committed_data[0]); - expect_value(tx_add_ptr, ptr_size, - sizeof(mock_dbds[0]->dbd_committed_data[0]) * mock_dbds[2]->dbd_count); - will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[0]->dbd_count); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[0]->dbd_count)); - will_return(tx_add_ptr, 0); - will_return(tx_commit, 0); - expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); - expect_value(__wrap_d_tm_dec_gauge, value, 0); - - /* Second DTX entries blob */ - will_return(__wrap_vos_tls_get, &mock_tls); - will_return(tx_begin, 0); - for (i = 0; i < DBD_BLOB_DF_CAP; i++) - will_return(__wrap_dbtree_delete, 0); - expect_value(tx_add_ptr, ptr, &mock_cont_df.cd_newest_aggregated); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_cont_df.cd_newest_aggregated)); - will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[2]->dbd_prev); - expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); - will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[0]->dbd_next); - expect_value(tx_add_ptr, ptr_size, sizeof(umem_off_t)); - will_return(tx_add_ptr, 0); - expect_value(tx_free, umoff, mock_dbds_off[1]); - will_return(tx_free, 0); - will_return(tx_commit, 0); - expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); - expect_value(__wrap_d_tm_dec_gauge, value, DBD_BLOB_DF_CAP); - - /* Third DTX entries blob */ - will_return(__wrap_vos_tls_get, &mock_tls); - will_return(tx_begin, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[2]->dbd_committed_data[0]); - expect_value(tx_add_ptr, ptr_size, - sizeof(mock_dbds[2]->dbd_committed_data[0]) * mock_dbds[2]->dbd_count); - will_return(tx_add_ptr, 0); - expect_value(tx_add_ptr, ptr, &mock_dbds[2]->dbd_count); - expect_value(tx_add_ptr, ptr_size, sizeof(mock_dbds[2]->dbd_count)); - will_return(tx_add_ptr, 0); - will_return(tx_commit, 0); - expect_value(__wrap_d_tm_dec_gauge, metric, mock_tls.vtl_committed); - expect_value(__wrap_d_tm_dec_gauge, value, 0); - - epoch = EPOCH_START + (DBD_BLOB_DF_CAP * 2) * EPOCH_STEP; - dbd_off = UMOFF_NULL; - do { - int rc; - rc = vos_dtx_aggregate(mock_coh, &epoch, &dbd_off); - assert_rc_equal(rc, 0); - } while (!UMOFF_IS_NULL(dbd_off)); - assert_int_equal(mock_cont_df.cd_newest_aggregated, - EPOCH_START + (2 * DBD_BLOB_DF_CAP - 1) * EPOCH_STEP); - assert_int_equal(mock_cont_df.cd_dtx_committed_head, mock_dbds_off[0]); - assert_int_equal(mock_cont_df.cd_dtx_committed_tail, mock_dbds_off[DBD_BLOBS_CAP - 1]); - assert_int_equal(mock_cont.vc_dtx_committed_count, DBD_BLOB_DF_CAP * (DBD_BLOBS_CAP - 1)); - assert_int_equal(mock_pool.vp_dtx_committed_count, DBD_BLOB_DF_CAP * (DBD_BLOBS_CAP - 1)); - assert_int_equal(mock_cont.vc_cmt_dtx_reindex_pos, mock_dbds_off[0]); -} - /* clang-format off */ /* compilation unit's entry point */ #define TEST(name, func, setup_func, teardown_func) \ -{ \ - name ": vos_dtx_aggregate - " #func, func, setup_func, teardown_func \ +{ \ + name ": vos_dtx_aggregate - " #func, func, setup_func, teardown_func \ } /* clang-format on */ @@ -1005,20 +790,17 @@ static const struct CMUnitTest vos_dtx_aggregate_tests_all[] = { TEST("DTX501", test_tx_begin_error, test_setup, test_teardown), TEST("DTX502", test_dbtree_delete_error, test_setup, test_teardown), TEST("DTX503", test_newest_aggregated_error, test_setup, test_teardown), - TEST("DTX504", test_committed_prev_error, test_setup, test_teardown), - TEST("DTX505", test_committed_head_error, test_setup, test_teardown), - TEST("DTX506", test_committed_tail_error, test_setup, test_teardown), - TEST("DTX507", test_committed_next_error, test_setup, test_teardown), - TEST("DTX508", test_umm_free_error, test_setup, test_teardown), - TEST("DTX509", test_committed_data_error, test_setup, test_teardown), - TEST("DTX510", test_dbd_count_error, test_setup, test_teardown), + TEST("DTX504", test_committed_head_error, test_setup, test_teardown), + TEST("DTX505", test_committed_prev_error, test_setup, test_teardown), + TEST("DTX506", test_umm_free_error, test_setup, test_teardown), + TEST("DTX507", test_committed_data_error, test_setup, test_teardown), + TEST("DTX508", test_dbd_count_error, test_setup, test_teardown), TEST("DTX509", test_umm_commit_error, test_setup, test_teardown), TEST("DTX550", test_empty, test_empty_setup, test_empty_teardown), TEST("DTX551", test_empty_blob, test_setup, test_teardown), - TEST("DTX552", test_10_entries, test_setup, test_teardown), - TEST("DTX553", test_all_entries, test_setup, test_teardown), - TEST("DTX554", test_one_blob, test_setup, test_teardown), - TEST("DTX555", test_second_blob, test_setup, test_teardown), + TEST("DTX552", test_one_blob, test_setup, test_teardown), + TEST("DTX553", test_10_entries, test_setup, test_teardown), + TEST("DTX554", test_all_entries, test_setup, test_teardown), }; int diff --git a/src/dtx/tests/dts_cmt_stat.c b/src/dtx/tests/dts_cmt_stat.c new file mode 100644 index 00000000000..dfcac14b09a --- /dev/null +++ b/src/dtx/tests/dts_cmt_stat.c @@ -0,0 +1,190 @@ +/** + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. + * + * SPDX-License-Identifier: BSD-2-Clause-Patent + */ +#define D_LOGFAC DD_FAC(tests) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "vos_internal.h" + +/* mocks */ + +#define DTX_CMT_BLOB_MAGIC 0x2502191c +#define DBD_BLOBS_CAP 0x3 +#define DBD_BLOB_DF_CAP 0x8 +#define CMT_TIME_START 0x1000 +#define CMT_TIME_END 0x10000 +#define CMT_TIME_STEP 0x10 +#define CELL_SIZE \ + sizeof(struct vos_dtx_blob_df) + DBD_BLOB_DF_CAP * sizeof(struct vos_dtx_cmt_ent_df) + +static const daos_epoch_t epoch_offsets[] = {0x20000, 0x40000}; + +static struct vos_pool mock_pool; +static struct vos_container mock_cont; +static struct vos_cont_df mock_cont_df; +static struct vos_dtx_blob_df *mock_dbds[DBD_BLOBS_CAP]; +static umem_off_t mock_dbds_off[DBD_BLOBS_CAP]; +static daos_handle_t mock_coh; + +/* Helpers */ + +static void +prep_dtx_entries(void) +{ + int i; + uint64_t cmt_time = CMT_TIME_START; + + for (i = 0; i < DBD_BLOBS_CAP; i++) { + int j; + + for (j = 0; j < DBD_BLOB_DF_CAP; j++) { + struct vos_dtx_cmt_ent_df *dce_df; + + dce_df = &mock_dbds[i]->dbd_committed_data[j]; + dce_df->dce_cmt_time = cmt_time; + dce_df->dce_epoch = cmt_time + epoch_offsets[j % 2]; + cmt_time += CMT_TIME_STEP; + } + mock_dbds[i]->dbd_count = DBD_BLOB_DF_CAP; + } + mock_cont.vc_dtx_committed_count = DBD_BLOBS_CAP * DBD_BLOB_DF_CAP; + mock_pool.vp_dtx_committed_count = DBD_BLOBS_CAP * DBD_BLOB_DF_CAP; +} + +/* setup & teardown */ + +static int +test_setup(void **unused) +{ + int i; + + memset(&mock_pool, 0, sizeof(mock_pool)); + memset(&mock_cont, 0, sizeof(mock_cont)); + memset(&mock_cont_df, 0, sizeof(mock_cont_df)); + + for (i = 0; i < DBD_BLOBS_CAP; i++) { + D_ALLOC(mock_dbds[i], CELL_SIZE); + assert_non_null(mock_dbds[i]); + mock_dbds_off[i] = umem_ptr2off(&mock_pool.vp_umm, mock_dbds[i]); + } + + for (i = 0; i < DBD_BLOBS_CAP; i++) { + mock_dbds[i]->dbd_magic = DTX_CMT_BLOB_MAGIC; + mock_dbds[i]->dbd_cap = DBD_BLOB_DF_CAP; + mock_dbds[i]->dbd_next = + (i == DBD_BLOBS_CAP - 1) ? UMOFF_NULL : mock_dbds_off[i + 1]; + mock_dbds[i]->dbd_prev = (i == 0) ? UMOFF_NULL : mock_dbds_off[i - 1]; + mock_dbds[i]->dbd_count = (i + 1) * 2; + assert_true(mock_dbds[i]->dbd_count <= DBD_BLOB_DF_CAP); + } + + mock_cont.vc_pool = &mock_pool; + mock_cont.vc_cont_df = &mock_cont_df; + mock_cont_df.cd_dtx_committed_head = mock_dbds_off[0]; + mock_cont_df.cd_dtx_committed_tail = mock_dbds_off[DBD_BLOBS_CAP - 1]; + mock_coh.cookie = (uint64_t)&mock_cont; + + return 0; +} + +static int +test_teardown(void **unused) +{ + int i; + + for (i = 0; i < DBD_BLOBS_CAP; i++) + D_FREE(mock_dbds[i]); + + return 0; +} + +/* tests */ + +static void +test_errors(void **unused) +{ + daos_handle_t hdl_null = {0}; + uint64_t cmt_cnt; + int rc; + + /* Invalid arguments. */ + rc = vos_dtx_get_cmt_stat(hdl_null, &cmt_cnt, NULL); + assert_rc_equal(rc, -DER_INVAL); + rc = vos_dtx_get_cmt_stat(mock_coh, NULL, NULL); + assert_rc_equal(rc, -DER_INVAL); + + /* Corrupted dbd */ + mock_dbds[1]->dbd_magic = 42; + rc = vos_dtx_get_cmt_stat(mock_coh, &cmt_cnt, NULL); + assert_rc_equal(rc, -DER_INVAL); +} + +static void +test_cmt_cnt(void **unused) +{ + uint64_t cmt_cnt; + int rc; + + prep_dtx_entries(); + + rc = vos_dtx_get_cmt_stat(mock_coh, &cmt_cnt, NULL); + assert_rc_equal(rc, 0); + assert_int_equal(cmt_cnt, DBD_BLOB_DF_CAP * DBD_BLOBS_CAP); +} + +static void +test_time_stat(void **unused) +{ + uint64_t cmt_cnt; + struct dtx_time_stat dts; + int rc; + + prep_dtx_entries(); + + rc = vos_dtx_get_cmt_stat(mock_coh, &cmt_cnt, &dts); + assert_rc_equal(rc, 0); + assert_int_equal(cmt_cnt, DBD_BLOB_DF_CAP * DBD_BLOBS_CAP); + + assert_int_equal(dts.dts_cmt_time[0], mock_dbds[0]->dbd_committed_data[0].dce_cmt_time); + assert_int_equal(dts.dts_cmt_time[1], mock_dbds[2]->dbd_committed_data[7].dce_cmt_time); + assert_int_equal(dts.dts_cmt_time[2], (dts.dts_cmt_time[0] + dts.dts_cmt_time[1]) / 2); + + assert_int_equal(dts.dts_epoch[0], mock_dbds[0]->dbd_committed_data[0].dce_epoch); + assert_int_equal(dts.dts_epoch[1], mock_dbds[2]->dbd_committed_data[7].dce_epoch); + assert_int_equal(dts.dts_epoch[2], (dts.dts_epoch[0] + dts.dts_epoch[1]) / 2); +} + +/* clang-format off */ +/* compilation unit's entry point */ +#define TEST(name, func) \ +{ \ + name ": vos_dtx_get_cmt_stat - " #func, func, test_setup, test_teardown \ +} +/* clang-format on */ + +static const struct CMUnitTest vos_dtx_get_cmt_stat_tests_all[] = { + TEST("DTX600", test_errors), + TEST("DTX601", test_cmt_cnt), + TEST("DTX602", test_time_stat), +}; + +int +run_dtx_cmt_stat_tests(void) +{ + const char *test_name = "vos_dtx_get_cmt_stat"; + + return cmocka_run_group_tests_name(test_name, vos_dtx_get_cmt_stat_tests_all, NULL, NULL); +} diff --git a/src/dtx/tests/dts_count.c b/src/dtx/tests/dts_count.c deleted file mode 100644 index 451ab2e4e40..00000000000 --- a/src/dtx/tests/dts_count.c +++ /dev/null @@ -1,133 +0,0 @@ -/** - * (C) Copyright 2025 Hewlett Packard Enterprise Development LP. - * - * SPDX-License-Identifier: BSD-2-Clause-Patent - */ -#define D_LOGFAC DD_FAC(tests) - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "vos_internal.h" - -/* mocks */ - -#define DTX_CMT_BLOB_MAGIC 0x2502191c -#define DBD_COUNT 0x3 -#define DBD_BLOB_DF_CAP 0x8 -#define CELL_SIZE \ - sizeof(struct vos_dtx_blob_df) + DBD_BLOB_DF_CAP * sizeof(struct vos_dtx_cmt_ent_df) - -static struct vos_pool mock_pool; -static struct vos_container mock_cont; -static struct vos_cont_df mock_cont_df; -static struct vos_dtx_blob_df *mock_dbds[DBD_COUNT]; -static umem_off_t mock_dbds_off[DBD_COUNT]; -static daos_handle_t mock_coh; - -/* setup & teardown */ - -static int -test_setup(void **unused) -{ - int i; - - memset(&mock_pool, 0, sizeof(mock_pool)); - memset(&mock_cont, 0, sizeof(mock_cont)); - memset(&mock_cont_df, 0, sizeof(mock_cont_df)); - - for (i = 0; i < DBD_COUNT; i++) { - D_ALLOC(mock_dbds[i], CELL_SIZE); - assert_non_null(mock_dbds[i]); - mock_dbds_off[i] = umem_ptr2off(&mock_pool.vp_umm, mock_dbds[i]); - } - - for (i = 0; i < DBD_COUNT; i++) { - mock_dbds[i]->dbd_magic = DTX_CMT_BLOB_MAGIC; - mock_dbds[i]->dbd_cap = DBD_BLOB_DF_CAP; - mock_dbds[i]->dbd_next = (i == DBD_COUNT - 1) ? UMOFF_NULL : mock_dbds_off[i + 1]; - mock_dbds[i]->dbd_prev = (i == 0) ? UMOFF_NULL : mock_dbds_off[i - 1]; - mock_dbds[i]->dbd_count = (i + 1) * 2; - assert_true(mock_dbds[i]->dbd_count <= DBD_BLOB_DF_CAP); - } - - mock_cont.vc_pool = &mock_pool; - mock_cont.vc_cont_df = &mock_cont_df; - mock_cont_df.cd_dtx_committed_head = mock_dbds_off[0]; - mock_cont_df.cd_dtx_committed_tail = mock_dbds_off[DBD_COUNT - 1]; - mock_coh.cookie = (uint64_t)&mock_cont; - - return 0; -} - -static int -test_teardown(void **unused) -{ - int i; - - for (i = 0; i < DBD_COUNT; i++) - D_FREE(mock_dbds[i]); - - return 0; -} - -/* tests */ - -static void -test_asserts(void **unused) -{ - daos_handle_t hdl_null = {0}; - uint32_t cnt; - int rc; - - /* Invalid arguments. */ - rc = vos_dtx_get_cmt_cnt(hdl_null, &cnt); - assert_rc_equal(rc, -DER_INVAL); - rc = vos_dtx_get_cmt_cnt(mock_coh, NULL); - assert_rc_equal(rc, -DER_INVAL); - - /* Corrupted dbd */ - mock_dbds[1]->dbd_magic = 42; - rc = vos_dtx_get_cmt_cnt(mock_coh, &cnt); - assert_rc_equal(rc, -DER_INVAL); -} - -static void -test_count(void **unused) -{ - uint32_t cnt; - int rc; - - /* Container with several DTX entries table */ - rc = vos_dtx_get_cmt_cnt(mock_coh, &cnt); - assert_rc_equal(rc, 0); - assert_int_equal(cnt, DBD_COUNT * (DBD_COUNT + 1)); -} - -/* compilation unit's entry point */ -#define TEST(name, func) \ - { \ - name ": vos_dtx_get_cmt_cnt - " #func, func, test_setup, test_teardown \ - } - -static const struct CMUnitTest vos_dtx_count_tests_all[] = { - TEST("DTX600", test_asserts), - TEST("DTX601", test_count), -}; - -int -run_dtx_count_tests(void) -{ - const char *test_name = "vos_dtx_get_cmt_cnt"; - - return cmocka_run_group_tests_name(test_name, vos_dtx_count_tests_all, NULL, NULL); -} diff --git a/src/dtx/tests/dtx_ut.c b/src/dtx/tests/dtx_ut.c index 630ba53de42..c4fc0ca8e96 100644 --- a/src/dtx/tests/dtx_ut.c +++ b/src/dtx/tests/dtx_ut.c @@ -22,7 +22,7 @@ run_structs_tests(void); int run_discard_invalid_tests(void); int -run_dtx_count_tests(void); +run_dtx_cmt_stat_tests(void); int run_dtx_aggregate_tests(void); @@ -44,7 +44,7 @@ run_all_tests(int keys) failed += run_structs_tests(); failed += run_discard_invalid_tests(); - failed += run_dtx_count_tests(); + failed += run_dtx_cmt_stat_tests(); failed += run_dtx_aggregate_tests(); return failed; diff --git a/src/include/daos_srv/vos.h b/src/include/daos_srv/vos.h index b7d9d604769..d26b8e54683 100644 --- a/src/include/daos_srv/vos.h +++ b/src/include/daos_srv/vos.h @@ -238,10 +238,12 @@ vos_dtx_set_flags(daos_handle_t coh, struct dtx_id dtis[], int count, uint32_t f * \param coh [IN] Container open handle. * \param ep_max [IN] The upper epoch to aggregate. * - * \return Zero on success, negative value if error. + * \return Negative value if error, zero if all DTX entries with an epoch lower + * or equal to ep_max have been aggregated, one if all the targeted DTX + * entries may not have been aggregated. */ int -vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max, umem_off_t *blob); +vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max); /** * Query the container's DTXs statistics information. @@ -1778,15 +1780,22 @@ vos_pin_objects(daos_handle_t coh, daos_unit_oid_t oids[], int count, struct vos bool vos_oi_exist(daos_handle_t coh, daos_unit_oid_t oid); +/* Timing statistic of DTX entries */ +struct dtx_time_stat { + daos_epoch_t dts_epoch[3]; + uint64_t dts_cmt_time[3]; +}; + /** - * Return the number of DTX committed entries of a container. + * Return statistics on the DTX committed entries of a container. * * \param[in] coh container open handle. - * \param[out] cnt number of DTX committed entries. + * \param[out] cmt_cnt number of DTX committed entries. + * \param[out] dts Timing statistics on the DTX committed entries. * * \return 0 on success, error otherwise. */ int -vos_dtx_get_cmt_cnt(daos_handle_t coh, uint32_t *cnt); +vos_dtx_get_cmt_stat(daos_handle_t coh, uint64_t *cmt_cnt, struct dtx_time_stat *dts); #endif /* __VOS_API_H */ diff --git a/src/utils/ddb/ddb.c b/src/utils/ddb/ddb.c index d94ac2d1504..79612c95677 100644 --- a/src/utils/ddb/ddb.c +++ b/src/utils/ddb/ddb.c @@ -922,11 +922,11 @@ dtx_aggr_option_parse(struct ddb_ctx *ctx, struct dtx_aggr_options *cmd_args, ui char **argv) { int opt; - char *options_short = "e:d:"; + char *options_short = "t:d:"; int index = 0; char *endptr; - struct option options_long[] = {{"epoch", required_argument, NULL, 'e'}, - {"date", required_argument, NULL, 'd'}, + struct option options_long[] = {{"cmt_time", required_argument, NULL, 't'}, + {"cmt_date", required_argument, NULL, 'd'}, {NULL}}; memset(cmd_args, 0, sizeof(*cmd_args)); @@ -936,36 +936,38 @@ dtx_aggr_option_parse(struct ddb_ctx *ctx, struct dtx_aggr_options *cmd_args, ui cmd_args->format = DDB_DTX_AGGR_NOW; while ((opt = getopt_long(argc, argv, options_short, options_long, &index)) != -1) { switch (opt) { - case 'e': - if (cmd_args->format == DDB_DTX_AGGR_DATE) { - ddb_error( - ctx, "'--epoch' and '--date' options are mutually exclusive\n"); + case 't': + if (cmd_args->format == DDB_DTX_AGGR_CMT_DATE) { + ddb_error(ctx, "'--cmt_time' and '--cmt_date' options are mutually " + "exclusive\n"); return -DER_INVAL; } - if (cmd_args->format == DDB_DTX_AGGR_EPOCH) { - ddb_error(ctx, "'--epoch' option can not be used multiple time\n"); + if (cmd_args->format == DDB_DTX_AGGR_CMT_TIME) { + ddb_error(ctx, + "'--cmt_time' option can not be used multiple time\n"); return -DER_INVAL; } errno = 0; - cmd_args->epoch = strtoull(optarg, &endptr, 10); + cmd_args->cmt_time = strtoull(optarg, &endptr, 10); if (errno != 0 || endptr == optarg || *endptr != '\0') { - ddb_error(ctx, "'--epoch' option arg epoch format is invalid\n"); + ddb_error(ctx, "'--cmt_time' option arg format is invalid\n"); return -DER_INVAL; } - cmd_args->format = DDB_DTX_AGGR_EPOCH; + cmd_args->format = DDB_DTX_AGGR_CMT_TIME; break; case 'd': - if (cmd_args->format == DDB_DTX_AGGR_EPOCH) { - ddb_error( - ctx, "'--epoch' and '--date' options are mutually exclusive\n"); + if (cmd_args->format == DDB_DTX_AGGR_CMT_TIME) { + ddb_error(ctx, "'--cmt_time' and '--cmt_date' options are mutually " + "exclusive\n"); return -DER_INVAL; } - if (cmd_args->format == DDB_DTX_AGGR_DATE) { - ddb_error(ctx, "'--date' option can not be used multiple time\n"); + if (cmd_args->format == DDB_DTX_AGGR_CMT_DATE) { + ddb_error(ctx, + "'--cmt_date' option can not be used multiple time\n"); return -DER_INVAL; } - cmd_args->date = optarg; - cmd_args->format = DDB_DTX_AGGR_DATE; + cmd_args->cmt_date = optarg; + cmd_args->format = DDB_DTX_AGGR_CMT_DATE; break; case '?': ddb_printf(ctx, "Unknown option: '%c'\n", optopt); @@ -975,7 +977,7 @@ dtx_aggr_option_parse(struct ddb_ctx *ctx, struct dtx_aggr_options *cmd_args, ui } } if (cmd_args->format == DDB_DTX_AGGR_NOW) { - ddb_error(ctx, "'--epoch' or '--date' option has to be defined\n"); + ddb_error(ctx, "'--cmt_time' or '--cmt_date' option has to be defined\n"); return -DER_INVAL; } @@ -1582,10 +1584,10 @@ ddb_commands_help(struct ddb_ctx *ctx) ddb_print(ctx, " [path]\n"); ddb_print(ctx, "\tOptional, VOS tree path of a container to aggregate.\n"); ddb_print(ctx, "Options:\n"); - ddb_print(ctx, " -e, --epoch\n"); - ddb_print(ctx, "\tMax aggregation epoch\n"); - ddb_print(ctx, " -d, --date\n"); - ddb_print(ctx, "\tMax aggregation date (format '1970-01-01 00:00:00')\n"); + ddb_print(ctx, " -t, --cmt_time\n"); + ddb_print(ctx, "\tMax aggregation commit time\n"); + ddb_print(ctx, " -d, --cmt_date\n"); + ddb_print(ctx, "\tMax aggregation commit date (format '1970-01-01 00:00:00')\n"); ddb_print(ctx, "\n"); } diff --git a/src/utils/ddb/ddb.h b/src/utils/ddb/ddb.h index 9497d2cc04d..3bc63c8f40f 100644 --- a/src/utils/ddb/ddb.h +++ b/src/utils/ddb/ddb.h @@ -218,6 +218,7 @@ struct dev_replace_options { struct dtx_stat_options { char *path; + bool details; }; struct prov_mem_options { @@ -226,13 +227,13 @@ struct prov_mem_options { unsigned int tmpfs_mount_size; }; -enum dtx_aggr_format { DDB_DTX_AGGR_NOW = 0, DDB_DTX_AGGR_EPOCH = 1, DDB_DTX_AGGR_DATE = 2 }; +enum dtx_aggr_format { DDB_DTX_AGGR_NOW = 0, DDB_DTX_AGGR_CMT_TIME = 1, DDB_DTX_AGGR_CMT_DATE = 2 }; struct dtx_aggr_options { char *path; enum dtx_aggr_format format; - uint64_t epoch; - char *date; + uint64_t cmt_time; + char *cmt_date; }; struct ddb_cmd_info { diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index 1f4d1b82b69..33db066bdd4 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -1288,33 +1288,26 @@ ddb_run_dev_replace(struct ddb_ctx *ctx, struct dev_replace_options *opt) return rc; } -enum { TIMESPEC_FMT_SIZE = sizeof("1970-01-01 00:00:00.000000000") }; +enum { TIMESPEC_FMT_SIZE = sizeof("1970-01-01 00:00:00") }; static int timespec2str(struct timespec *tspec, char *buf, size_t buf_size) { struct tm date; size_t buf_len; - int rc; if (localtime_r(&(tspec->tv_sec), &date) == NULL) return d_errno2der(errno); buf_len = strftime(buf, buf_size, "%F %T", &date); - if (buf_len == 0) + if (buf_len != buf_size - 1) return -DER_INVAL; - rc = snprintf(&buf[buf_len], buf_size - buf_len, ".%09ld", tspec->tv_nsec); - if (rc < 0) - return -DER_INVAL; - if (rc >= buf_size - buf_len) - return -DER_TRUNC; - return 0; } static int -dtx_print_time_stat(struct ddb_ctx *ctx, char *stat_name, uint64_t epoch, char *align) +dtx_print_epoch(struct ddb_ctx *ctx, const daos_epoch_t epoch) { struct timespec tspec; char buf[TIMESPEC_FMT_SIZE]; @@ -1322,55 +1315,162 @@ dtx_print_time_stat(struct ddb_ctx *ctx, char *stat_name, uint64_t epoch, char * rc = d_hlc2timespec(epoch, &tspec); if (!SUCCESS(rc)) { - ddb_errorf(ctx, "Conversion error of the DTX stat %s: " DF_RC "\n", stat_name, - DP_RC(rc)); - return rc; + ddb_errorf(ctx, "Failed to convert DTX epoch %" PRIu64 " to timespec: " DF_RC "\n", + epoch, DP_RC(rc)); + goto out; } + rc = timespec2str(&tspec, buf, TIMESPEC_FMT_SIZE); - if (!SUCCESS(rc)) { - ddb_errorf(ctx, "Conversion error of the DTX stat %s: " DF_RC "\n", stat_name, - DP_RC(rc)); - return rc; + + ddb_printf(ctx, "%s (%" PRIu64 ")", buf, epoch); +out: + return rc; +} + +static const char *stat_names[] = {"min", "max", "mean"}; + +static int +dtx_print_epoch_stat(struct ddb_ctx *ctx, const char *prefix, const daos_epoch_t epoch_stats[], + const char *align) +{ + int i; + int rc; + + ddb_printf(ctx, "\t- %s%s", prefix, align); + + rc = 0; + for (i = 0; i < 3; i++) { + if (epoch_stats[i] == 0 || epoch_stats[i] == DAOS_EPOCH_MAX) { + ddb_printf(ctx, "%s=NA (NA)", stat_names[i]); + } else { + ddb_printf(ctx, "%s=", stat_names[i]); + rc = dtx_print_epoch(ctx, epoch_stats[i]); + if (!SUCCESS(rc)) + goto out; + } + if (i < 2) + ddb_print(ctx, ", "); } - ddb_printf(ctx, "\t- %s time:%s%s, %" PRIu64 "\n", stat_name, align, buf, epoch); + ddb_print(ctx, "\n"); - return 0; +out: + return rc; +} + +static int +dtx_print_time_stat(struct ddb_ctx *ctx, const char *prefix, const uint64_t cmt_time_stats[], + const char *align) +{ + int i; + int rc; + + ddb_printf(ctx, "\t- %s%s", prefix, align); + + rc = 0; + for (i = 0; i < 3; i++) { + if (cmt_time_stats[i] == 0 || cmt_time_stats[i] == UINT64_MAX) { + ddb_printf(ctx, "%s=NA (NA)", stat_names[i]); + } else { + struct timespec tspec = {0}; + char buf[TIMESPEC_FMT_SIZE]; + + tspec.tv_sec = cmt_time_stats[i]; + rc = timespec2str(&tspec, buf, TIMESPEC_FMT_SIZE); + if (!SUCCESS(rc)) { + ddb_errorf(ctx, + "Failed to convert DTX commit time %" PRIu64 + " to timespec: " DF_RC "\n", + cmt_time_stats[i], DP_RC(rc)); + goto out; + } + ddb_printf(ctx, "%s=%s (%" PRIu64 ")", stat_names[i], buf, + cmt_time_stats[i]); + } + if (i < 2) + ddb_print(ctx, ", "); + } + ddb_print(ctx, "\n"); + +out: + return rc; } struct dtx_stat_args { struct ddb_ctx *ctx; struct dtx_stat_options *opt; uint32_t cont_seen; - uint32_t cmt_cnt; + uint64_t cmt_cnt; + struct dtx_time_stat time_stat; + daos_epoch_t aggr_epoch; }; static int -dtx_stat_cont(struct ddb_ctx *ctx, struct dv_indexed_tree_path *itp, uint32_t *cnt) +dtx_stat_print(struct ddb_ctx *ctx, uint64_t cmt_cnt, const struct dtx_time_stat *dts, + daos_epoch_t aggr_epoch) +{ + int rc = 0; + + ddb_printf(ctx, "\t- Committed DTX count:\t%" PRIu64 "\n", cmt_cnt); + if (dts != NULL) { + rc = dtx_print_time_stat(ctx, "Committed DTX time:", dts->dts_cmt_time, "\t"); + if (!SUCCESS(rc)) + goto done; + rc = dtx_print_epoch_stat(ctx, "Committed DTX epoch:", dts->dts_epoch, "\t"); + if (!SUCCESS(rc)) + goto done; + } + ddb_print(ctx, "\t- DTX aggregated epoch:\t"); + if (aggr_epoch == 0 || aggr_epoch == DAOS_EPOCH_MAX) + ddb_print(ctx, "NA (NA)"); + else { + rc = dtx_print_epoch(ctx, aggr_epoch); + if (!SUCCESS(rc)) + goto done; + } + ddb_print(ctx, "\n"); + +done: + + return rc; +} + +static int +dtx_stat_cont(struct ddb_ctx *ctx, struct dv_indexed_tree_path *itp, struct dtx_stat_args *args) { - struct dtx_stat stat; daos_handle_t coh = {0}; - uint32_t cnt_tmp; + uint64_t cmt_cnt_tmp; + struct dtx_time_stat dts_tmp; + struct dtx_stat aggr_stat_tmp; int rc; rc = dv_cont_open(ctx->dc_poh, itp_cont(itp), &coh); if (!SUCCESS(rc)) goto done; - rc = vos_dtx_get_cmt_cnt(coh, &cnt_tmp); + if (args->opt->details) + rc = vos_dtx_get_cmt_stat(coh, &cmt_cnt_tmp, &dts_tmp); + else + rc = vos_dtx_get_cmt_stat(coh, &cmt_cnt_tmp, NULL); if (!SUCCESS(rc)) goto done; - vos_dtx_stat(coh, &stat, 0); + vos_dtx_stat(coh, &aggr_stat_tmp, 0); - ddb_print(ctx, "DTX entries statistics of "); + ddb_print(ctx, "DTX entries statistics of container "); itp_print_full(ctx, itp); ddb_print(ctx, "\n"); - ddb_printf(ctx, "\t- Number of committed DTX of the container:\t%" PRIu32 "\n", cnt_tmp); - rc = - dtx_print_time_stat(ctx, "DTX newest aggregated", stat.dtx_newest_aggregated, "\t\t\t"); + if (args->opt->details) + rc = + dtx_stat_print(ctx, cmt_cnt_tmp, &dts_tmp, aggr_stat_tmp.dtx_newest_aggregated); + else + rc = dtx_stat_print(ctx, cmt_cnt_tmp, NULL, aggr_stat_tmp.dtx_newest_aggregated); if (!SUCCESS(rc)) goto done; - if (cnt != NULL) - *cnt = cnt_tmp; + + args->cmt_cnt = cmt_cnt_tmp; + args->aggr_epoch = aggr_stat_tmp.dtx_newest_aggregated; + if (args->opt->details) + memcpy(&args->time_stat, &dts_tmp, sizeof(struct dtx_time_stat)); + done: dv_cont_close(&coh); @@ -1384,7 +1484,7 @@ dtx_stat_cont_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type struct ddb_ctx *ctx; struct dv_indexed_tree_path itp = {0}; struct dtx_stat_args *args; - uint32_t cnt; + struct dtx_stat_args args_tmp; int rc; if (type != VOS_ITER_COUUID) { @@ -1393,15 +1493,52 @@ dtx_stat_cont_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type } args = (struct dtx_stat_args *)cb_arg; + memcpy(&args_tmp, args, sizeof(struct dtx_stat_args)); ctx = args->ctx; itp_set_cont(&itp, entry->ie_couuid, args->cont_seen); ++(args->cont_seen); - rc = dtx_stat_cont(ctx, &itp, &cnt); + rc = dtx_stat_cont(ctx, &itp, &args_tmp); if (!SUCCESS(rc)) goto done; - args->cmt_cnt += cnt; + + args->cmt_cnt += args_tmp.cmt_cnt; + + if (!args->opt->details) + goto done; + if (args->aggr_epoch < args_tmp.aggr_epoch) + args->aggr_epoch = args_tmp.aggr_epoch; + if (args->time_stat.dts_cmt_time[0] > args_tmp.time_stat.dts_cmt_time[0]) + args->time_stat.dts_cmt_time[0] = args_tmp.time_stat.dts_cmt_time[0]; + if (args->time_stat.dts_cmt_time[1] < args_tmp.time_stat.dts_cmt_time[1]) + args->time_stat.dts_cmt_time[1] = args_tmp.time_stat.dts_cmt_time[1]; + if (args->time_stat.dts_cmt_time[2] == 0) + args->time_stat.dts_cmt_time[2] = args_tmp.time_stat.dts_cmt_time[2]; + else { + long double tmp_mean; + + tmp_mean = args->time_stat.dts_cmt_time[2] * (long double)args->cmt_cnt; + tmp_mean += + (long double)args_tmp.time_stat.dts_cmt_time[2] * (long double)args_tmp.cmt_cnt; + tmp_mean /= (long double)(args->cmt_cnt + args_tmp.cmt_cnt); + args->time_stat.dts_cmt_time[2] = tmp_mean; + } + if (args->time_stat.dts_epoch[0] > args_tmp.time_stat.dts_epoch[0]) + args->time_stat.dts_epoch[0] = args_tmp.time_stat.dts_epoch[0]; + if (args->time_stat.dts_epoch[1] < args_tmp.time_stat.dts_epoch[1]) + args->time_stat.dts_epoch[1] = args_tmp.time_stat.dts_epoch[1]; + if (args->time_stat.dts_epoch[2] == 0) + args->time_stat.dts_epoch[2] = args_tmp.time_stat.dts_epoch[2]; + else { + long double tmp_mean; + + tmp_mean = args->time_stat.dts_epoch[2] * (long double)args->cmt_cnt; + tmp_mean += + (long double)args_tmp.time_stat.dts_epoch[2] * (long double)args_tmp.cmt_cnt; + tmp_mean /= (long double)(args->cmt_cnt + args_tmp.cmt_cnt); + args->time_stat.dts_epoch[2] = tmp_mean; + } done: itp_free(&itp); @@ -1410,22 +1547,22 @@ dtx_stat_cont_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type } static int -dtx_stat_cont_warp(struct ddb_ctx *ctx, char *path) +dtx_stat_cont_warp(struct ddb_ctx *ctx, struct dtx_stat_args *args) { struct dv_indexed_tree_path itp = {0}; int rc; - rc = init_path(ctx, path, &itp); + rc = init_path(ctx, args->opt->path, &itp); if (!SUCCESS(rc)) goto done; if (!itp_has_cont(&itp)) { - ddb_errorf(ctx, "Invalid container path " DF_PATH ".\n", DP_PATH(path)); + ddb_errorf(ctx, "Invalid container path " DF_PATH ".\n", DP_PATH(args->opt->path)); rc = -DER_INVAL; goto done; } - rc = dtx_stat_cont(ctx, &itp, NULL); + rc = dtx_stat_cont(ctx, &itp, args); done: itp_free(&itp); return rc; @@ -1445,22 +1582,28 @@ ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt) goto done; } + args.ctx = ctx; + args.opt = opt; if (opt->path != NULL && opt->path[0] != '\0') { - rc = dtx_stat_cont_warp(ctx, opt->path); + rc = dtx_stat_cont_warp(ctx, &args); goto done; } - args.ctx = ctx; - args.opt = opt; + args.cmt_cnt = 0; + args.time_stat.dts_cmt_time[0] = UINT64_MAX; + args.time_stat.dts_epoch[0] = DAOS_EPOCH_MAX; + args.aggr_epoch = DAOS_EPOCH_MAX; param.ip_hdl = ctx->dc_poh; param.ip_epr.epr_hi = DAOS_EPOCH_MAX; do { rc = vos_iterate(¶m, VOS_ITER_COUUID, false, &anchors, NULL, dtx_stat_cont_cb, &args, NULL); } while (rc > 0); - if (rc == 0) - ddb_printf(ctx, "Number of committed DTX of the pool:\t\t\t%" PRIu32 "\n", - args.cmt_cnt); + ddb_printf(ctx, "DTX entries statistics of the pool %s\n", ctx->dc_pool_path); + if (opt->details) + rc = dtx_stat_print(ctx, args.cmt_cnt, &args.time_stat, args.aggr_epoch); + else + rc = dtx_stat_print(ctx, args.cmt_cnt, NULL, args.aggr_epoch); done: return rc; @@ -1469,35 +1612,42 @@ ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt) struct dtx_aggr_args { struct ddb_ctx *ctx; uint32_t cont_seen; - uint64_t *epoch; + uint64_t *cmt_time; }; static int -dtx_aggr_cont(struct ddb_ctx *ctx, struct dv_indexed_tree_path *itp, uint64_t *epoch) +dtx_aggr_cont(struct ddb_ctx *ctx, struct dv_indexed_tree_path *itp, uint64_t *cmt_time) { daos_handle_t coh = {0}; - umem_off_t dbd_off; int rc; rc = dv_cont_open(ctx->dc_poh, itp_cont(itp), &coh); - if (!SUCCESS(rc)) - goto done; + if (!SUCCESS(rc)) { + ddb_error(ctx, "Failed to open container "); + itp_print_full(ctx, itp); + ddb_errorf(ctx, ": " DF_RC "\n", DP_RC(rc)); + return rc; + } ddb_print(ctx, "Aggregating DTX entries of container "); itp_print_full(ctx, itp); - ddb_print(ctx, "\n"); - dbd_off = UMOFF_NULL; + ddb_print(ctx, "...\n"); do { - rc = vos_dtx_aggregate(coh, epoch, &dbd_off); - if (!SUCCESS(rc)) { - ddb_errorf(ctx, "Aggregation of DTX entries failed: " DF_RC "\n", - DP_RC(rc)); - goto done; - } - } while (!UMOFF_IS_NULL(dbd_off)); + rc = vos_dtx_aggregate(coh, cmt_time); + } while (rc > 0); + if (rc < 0) { + ddb_error(ctx, "Failed to aggregate DTX committed entries of container "); + itp_print_full(ctx, itp); + ddb_errorf(ctx, ": " DF_RC "\n", DP_RC(rc)); + } -done: - dv_cont_close(&coh); + rc = dv_cont_close(&coh); + if (!SUCCESS(rc)) { + ddb_error(ctx, "Failed to close container "); + itp_print_full(ctx, itp); + ddb_errorf(ctx, ": " DF_RC "\n", DP_RC(rc)); + return rc; + } return rc; } @@ -1520,7 +1670,7 @@ dtx_aggr_cont_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type itp_set_cont(&itp, entry->ie_couuid, args->cont_seen); ++(args->cont_seen); - rc = dtx_aggr_cont(args->ctx, &itp, args->epoch); + rc = dtx_aggr_cont(args->ctx, &itp, args->cmt_time); done: itp_free(&itp); @@ -1558,7 +1708,6 @@ ddb_run_dtx_aggr(struct ddb_ctx *ctx, struct dtx_aggr_options *opt) vos_iter_param_t param = {0}; struct dtx_aggr_args args = {0}; struct vos_iter_anchors anchors = {0}; - uint64_t epoch; int rc; if (!ctx->dc_write_mode) { @@ -1575,25 +1724,23 @@ ddb_run_dtx_aggr(struct ddb_ctx *ctx, struct dtx_aggr_options *opt) switch (opt->format) { case DDB_DTX_AGGR_NOW: - args.epoch = NULL; + args.cmt_time = NULL; break; - case DDB_DTX_AGGR_DATE: - rc = ddb_date2epoch(optarg, &epoch); + case DDB_DTX_AGGR_CMT_DATE: + rc = ddb_date2cmt_time(opt->cmt_date, &opt->cmt_time); if (!SUCCESS(rc)) { ddb_error(ctx, "'--date' option arg date format is invalid\n"); goto done; } - args.epoch = &epoch; - break; - case DDB_DTX_AGGR_EPOCH: - args.epoch = &opt->epoch; + case DDB_DTX_AGGR_CMT_TIME: + args.cmt_time = &opt->cmt_time; break; default: D_ASSERT(false && "Invalid dtx_aggr options"); } if (opt->path != NULL && opt->path[0] != '\0') { - rc = dtx_aggr_cont_warp(ctx, opt->path, args.epoch); + rc = dtx_aggr_cont_warp(ctx, opt->path, args.cmt_time); goto done; } diff --git a/src/utils/ddb/ddb_parse.c b/src/utils/ddb/ddb_parse.c index 3ec673dc385..e1bf64272a7 100644 --- a/src/utils/ddb/ddb_parse.c +++ b/src/utils/ddb/ddb_parse.c @@ -481,22 +481,23 @@ ddb_parse_key(const char *input, daos_key_t *key) } int -ddb_date2epoch(const char *date, uint64_t *epoch) +ddb_date2cmt_time(const char *date, uint64_t *cmt_time) { - struct tm date_tm = {0}; - struct timespec date_tspec = {0}; + struct tm date_tm = {0}; char *endptr; + time_t cmt_time_tmp; - if (date == NULL || epoch == NULL) + if (date == NULL || cmt_time == NULL) return -DER_INVAL; endptr = strptime(date, "%Y-%m-%d %H:%M:%S", &date_tm); if (endptr == NULL || *endptr != '\0') return -DER_INVAL; - date_tspec.tv_sec = mktime(&date_tm); - if (date_tspec.tv_sec == (time_t)-1) + cmt_time_tmp = mktime(&date_tm); + if (cmt_time_tmp == (time_t)-1) return daos_errno2der(errno); + *cmt_time = cmt_time_tmp; - return d_timespec2hlc(date_tspec, epoch); + return 0; } diff --git a/src/utils/ddb/ddb_parse.h b/src/utils/ddb/ddb_parse.h index 6fc0e672e14..6eaab9eab57 100644 --- a/src/utils/ddb/ddb_parse.h +++ b/src/utils/ddb/ddb_parse.h @@ -54,6 +54,6 @@ int ddb_parse_dtx_id(const char *dtx_id_str, struct dtx_id *dtx_id); /* Parse a string representing a date into an epoch */ int -ddb_date2epoch(const char *date, uint64_t *epoch); +ddb_date2cmt_time(const char *date, uint64_t *cmt_time); #endif /** __DAOS_DDB_PARSE_H */ diff --git a/src/utils/ddb/tests/ddb_cmd_options_tests.c b/src/utils/ddb/tests/ddb_cmd_options_tests.c index 524873780e2..82562870db0 100644 --- a/src/utils/ddb/tests/ddb_cmd_options_tests.c +++ b/src/utils/ddb/tests/ddb_cmd_options_tests.c @@ -336,23 +336,23 @@ dtx_aggr_parsing(void **state) /* test all options and arguments */ test_run_cmd(&info, "dtx_aggr", "-e", "42"); assert_null(options->path); - assert_non_null(options->epoch); - assert_null(options->date); + assert_non_null(options->cmt_time); + assert_null(options->cmt_date); test_run_cmd(&info, "dtx_aggr", "-d", "1970-01-01 00:00:00"); assert_null(options->path); - assert_null(options->epoch); - assert_non_null(options->date); + assert_null(options->cmt_time); + assert_non_null(options->cmt_date); test_run_cmd(&info, "dtx_aggr", "-e", "42", "path"); assert_non_null(options->path); - assert_non_null(options->epoch); - assert_null(options->date); + assert_non_null(options->cmt_time); + assert_null(options->cmt_date); test_run_cmd(&info, "dtx_aggr", "-d", "1970-01-01 00:00:00", "path"); assert_non_null(options->path); - assert_null(options->epoch); - assert_non_null(options->date); + assert_null(options->cmt_time); + assert_non_null(options->cmt_date); } /* diff --git a/src/utils/ddb/tests/ddb_cmocka.h b/src/utils/ddb/tests/ddb_cmocka.h index a5948b6a048..567de80f501 100644 --- a/src/utils/ddb/tests/ddb_cmocka.h +++ b/src/utils/ddb/tests/ddb_cmocka.h @@ -120,4 +120,16 @@ fail_msg("'%s' regex not matched in '%s'", regex, str); \ } while (0) +#define assert_nl_equal(str, cnt) \ + do { \ + int _i; \ + int _nl_cnt; \ + _nl_cnt = 0; \ + for (_i = 0; _i <= strlen(str); _i++) { \ + if (str[_i] == '\n') \ + _nl_cnt++; \ + } \ + assert_int_equal(_nl_cnt++, cnt); \ + } while (0) + #endif /* DAOS_DDB_CMOCKA_H */ diff --git a/src/utils/ddb/tests/ddb_commands_tests.c b/src/utils/ddb/tests/ddb_commands_tests.c index 1e4b389d17b..1b89a95594a 100644 --- a/src/utils/ddb/tests/ddb_commands_tests.c +++ b/src/utils/ddb/tests/ddb_commands_tests.c @@ -411,6 +411,8 @@ dtx_stat_tests(void **state) struct dt_vos_pool_ctx *tctx = *state; struct ddb_ctx ctx = {0}; struct dtx_stat_options opt = {0}; + int i; + int cont_cnt; ctx.dc_poh = tctx->dvt_poh; ctx.dc_io_ft.ddb_print_message = dvt_fake_print; @@ -419,127 +421,65 @@ dtx_stat_tests(void **state) opt.path = "[0]"; dvt_fake_print_reset(); assert_success(ddb_run_dtx_stat(&ctx, &opt)); - assert_regex_match( - dvt_fake_print_buffer, - "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+1$"); assert_regex_match(dvt_fake_print_buffer, - "^[[:blank:]]+- DTX newest aggregated time:.+, 0$"); + "^DTX entries statistics of container " + "CONT:[[:blank:]]+\\(/\\[0\\]\\)[[:blank:]]+/[[:digit:]-]+$"); + assert_regex_match(dvt_fake_print_buffer, + "^[[:blank:]]+- Committed DTX count:[[:blank:]]+1$"); + assert_regex_match(dvt_fake_print_buffer, + "^[[:blank:]]+- DTX aggregated epoch:[[:blank:]]+NA \\(NA\\)$"); + assert_nl_equal(dvt_fake_print_buffer, 3); - opt.path = ""; + opt.details = true; dvt_fake_print_reset(); assert_success(ddb_run_dtx_stat(&ctx, &opt)); + assert_regex_match(dvt_fake_print_buffer, + "^DTX entries statistics of container " + "CONT:[[:blank:]]+\\(/\\[0\\]\\)[[:blank:]]+/[[:digit:]-]+$"); assert_regex_match( dvt_fake_print_buffer, - "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+1$"); - assert_regex_match(dvt_fake_print_buffer, - "^[[:blank:]]+- DTX newest aggregated time:.+, 0$"); + "^[[:blank:]]+- Committed DTX time:[[:blank:]]+min=20.+, max=20.+, mean=20.+$"); + assert_regex_match( + dvt_fake_print_buffer, + "^[[:blank:]]+- Committed DTX epoch:[[:blank:]]+min=20.+, max=20.+, mean=20.+$"); assert_regex_match(dvt_fake_print_buffer, - "^Number of committed DTX of the pool:[[:blank:]]+1$"); -} + "^[[:blank:]]+- DTX aggregated epoch:[[:blank:]]+NA \\(NA\\)$"); + assert_nl_equal(dvt_fake_print_buffer, 5); -static uint64_t -get_epoch(const char *buf) -{ - size_t buf_len; - int idx; - uint64_t exp; - uint64_t epoch; - - buf_len = strlen(buf); - idx = buf_len - 1; - while (idx > 0 && (buf[idx] < '0' || buf[idx] > '9')) - --idx; - assert_true(idx > 0); - - exp = 1; - epoch = 0; - while (idx > 0 && buf[idx] >= '0' && buf[idx] <= '9') { - epoch += (buf[idx] - '0') * exp; - exp *= 10; - --idx; - } - assert_true(idx > 0); - assert_true(buf[idx] == ' '); + opt.path = ""; + dvt_fake_print_reset(); + assert_success(ddb_run_dtx_stat(&ctx, &opt)); + cont_cnt = (DAOS_ON_VALGRIND) ? 8 : 10; + for (i = 0; i < cont_cnt; i++) { + char buf[] = "^DTX entries statistics of container " + "CONT:[[:blank:]]+\\(/\\[0\\]\\)[[:blank:]]+/[[:digit:]-]+$"; - return epoch; + buf[59] += i; + assert_regex_match(dvt_fake_print_buffer, buf); + } + assert_regex_match(dvt_fake_print_buffer, + "^DTX entries statistics of the pool \\(null\\)$"); } -static void -print_regerror(int rc, regex_t *preg, const char *regex_str) +static uint64_t +dtx_get_cmt_time(char *buf) { - char *buf; - size_t buf_size; + uint64_t cmt_time; - buf_size = regerror(rc, preg, NULL, 0); - D_ALLOC_ARRAY(buf, buf_size); + buf = strstr(buf, "- Committed DTX time:"); + assert_non_null(buf); + buf = strstr(buf, "("); assert_non_null(buf); - regerror(rc, preg, buf, buf_size); - /* XXX Needs of testing buf to avoid false positive error from gcc : '%s' directive argument - * is null [-Werror=format-overflow=] */ - if (buf) - print_error("ERROR: invalid regex '%s': %s\n", regex_str, buf); - D_FREE(buf); - if (preg != NULL) - regfree(preg); -} - -struct ddb_epochs { - size_t de_size; - uint64_t de_epochs[0]; -}; - -static int -cmp_epoch(const void *ep1, const void *ep2) -{ - return *(uint64_t *)ep1 - *(uint64_t *)ep2; -} -static struct ddb_epochs * -get_committed_epochs(char *buf) -{ - const char *regex_str = "^[[:blank:]]+Epoch: [[:digit:]]+$"; - char *token; - regex_t preg; - struct ddb_epochs *epochs; - int rc; - - D_ALLOC_PTR(epochs); - assert_non_null(epochs); - memset(epochs, 0, sizeof(*epochs)); - - rc = regcomp(&preg, regex_str, REG_NOSUB | REG_EXTENDED); - if (rc != 0) { - print_regerror(rc, NULL, regex_str); - fail(); - } - token = strtok(buf, "\n"); - while (token != NULL) { - struct ddb_epochs *epochs_prev; - size_t epochs_size; - - rc = regexec(&preg, token, 0, NULL, 0); - if (rc == REG_NOMATCH) { - token = strtok(NULL, "\n"); - continue; - } - if (rc != 0) { - print_regerror(rc, &preg, regex_str); - fail(); - } - - ++epochs->de_size; - epochs_prev = epochs; - epochs_size = sizeof(*epochs) + sizeof(epochs->de_epochs[0]) * epochs->de_size; - D_REALLOC_NZ(epochs, epochs_prev, epochs_size); - epochs->de_epochs[epochs->de_size - 1] = get_epoch(token); - - token = strtok(NULL, "\n"); + buf++; + cmt_time = 0; + while (*buf >= '0' && *buf <= '9') { + cmt_time *= 10; + cmt_time += *buf - '0'; + buf++; } - regfree(&preg); - qsort(&epochs->de_epochs[0], epochs->de_size, sizeof(epochs->de_epochs[0]), cmp_epoch); - - return epochs; + return cmt_time; } static void @@ -548,13 +488,10 @@ dtx_aggr_tests(void **state) uuid_t *p_uuid = &g_uuids[3]; struct dt_vos_pool_ctx *tctx = *state; struct ddb_ctx ctx = {0}; - struct dtx_dump_options opt_dump = {0}; struct dtx_stat_options opt_stat = {0}; struct dtx_aggr_options opt_aggr = {0}; daos_handle_t coh; char buf[256]; - uint64_t epoch_last; - struct ddb_epochs *epochs; ctx.dc_poh = tctx->dvt_poh; ctx.dc_io_ft.ddb_print_error = dvt_fake_print; @@ -572,10 +509,8 @@ dtx_aggr_tests(void **state) opt_stat.path = buf; dvt_fake_print_reset(); assert_success(ddb_run_dtx_stat(&ctx, &opt_stat)); - assert_regex_match( - dvt_fake_print_buffer, - "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+8$"); - assert_int_equal(get_epoch(dvt_fake_print_buffer), 0); + assert_regex_match(dvt_fake_print_buffer, + "^[[:blank:]]+- Committed DTX count:[[:blank:]]+8$"); /* Test aggregation without epoch (i.e. all 8 DTX entries) */ opt_aggr.path = buf; @@ -584,67 +519,44 @@ dtx_aggr_tests(void **state) dvt_fake_print_reset(); assert_success(ddb_run_dtx_stat(&ctx, &opt_stat)); + assert_regex_match(dvt_fake_print_buffer, + "^[[:blank:]]+- Committed DTX count:[[:blank:]]+0$"); assert_regex_match( dvt_fake_print_buffer, - "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+0$"); - epoch_last = get_epoch(dvt_fake_print_buffer); - assert_true(epoch_last > 0); + "^[[:blank:]]+- DTX aggregated epoch:[[:blank:]]+.+ \\([[:digit:]]+\\)$"); /* Insert 10 mocked DTX entries */ assert_success(vos_cont_open(tctx->dvt_poh, *p_uuid, &coh)); - dvt_vos_insert_dtx_records(coh, 10, 10); + dvt_vos_insert_dtx_records(coh, 10, 3); + sleep(2); + dvt_vos_insert_dtx_records(coh, 10, 7); assert_success(vos_cont_close(coh)); + opt_stat.details = true; dvt_fake_print_reset(); assert_success(ddb_run_dtx_stat(&ctx, &opt_stat)); - assert_regex_match( - dvt_fake_print_buffer, - "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+10$"); - assert_int_equal(get_epoch(dvt_fake_print_buffer), epoch_last); - - opt_dump.path = buf; - opt_dump.active = false; - opt_dump.committed = true; - dvt_fake_print_reset(); - assert_success(ddb_run_dtx_dump(&ctx, &opt_dump)); - epochs = get_committed_epochs(dvt_fake_print_buffer); - assert_int_equal(epochs->de_size, 10); + assert_regex_match(dvt_fake_print_buffer, + "^[[:blank:]]+- Committed DTX count:[[:blank:]]+10$"); + opt_aggr.cmt_time = dtx_get_cmt_time(dvt_fake_print_buffer); - /* Test aggregation with an epoch (i.e. aggregate 7 first DTX entries) */ - opt_aggr.format = DDB_DTX_AGGR_EPOCH; - opt_aggr.epoch = epochs->de_epochs[6]; + /* Test aggregation with an epoch (i.e. aggregate 3 first DTX entries) */ + opt_aggr.format = DDB_DTX_AGGR_CMT_TIME; + opt_aggr.cmt_time += 1; assert_success(ddb_run_dtx_aggr(&ctx, &opt_aggr)); dvt_fake_print_reset(); assert_success(ddb_run_dtx_stat(&ctx, &opt_stat)); - assert_regex_match( - dvt_fake_print_buffer, - "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+3$"); - assert_int_equal(get_epoch(dvt_fake_print_buffer), epochs->de_epochs[6]); - assert_true(get_epoch(dvt_fake_print_buffer) > epoch_last); + assert_regex_match(dvt_fake_print_buffer, + "^[[:blank:]]+- Committed DTX count:[[:blank:]]+7$"); - /* Test aggregation without epoch (i.e. aggregate last 3 DTX entries) */ + /* Test aggregation without epoch (i.e. aggregate last 7 DTX entries) */ opt_aggr.format = DDB_DTX_AGGR_NOW; assert_success(ddb_run_dtx_aggr(&ctx, &opt_aggr)); - dvt_fake_print_reset(); - assert_success(ddb_run_dtx_stat(&ctx, &opt_stat)); - assert_regex_match( - dvt_fake_print_buffer, - "^[[:blank:]]+- Number of committed DTX of the container:[[:blank:]]+0$"); - assert_int_equal(get_epoch(dvt_fake_print_buffer), epochs->de_epochs[9]); - - /* Test aggregaton of all the containers */ - opt_aggr.path = NULL; - assert_success(ddb_run_dtx_aggr(&ctx, &opt_aggr)); - - opt_stat.path = NULL; dvt_fake_print_reset(); assert_success(ddb_run_dtx_stat(&ctx, &opt_stat)); assert_regex_match(dvt_fake_print_buffer, - "^Number of committed DTX of the pool:[[:blank:]]+0$"); - - D_FREE(epochs); + "^[[:blank:]]+- Committed DTX count:[[:blank:]]+0$"); } /* diff --git a/src/utils/ddb/tests/ddb_parse_tests.c b/src/utils/ddb/tests/ddb_parse_tests.c index fdc1b025927..bcb3efe9179 100644 --- a/src/utils/ddb/tests/ddb_parse_tests.c +++ b/src/utils/ddb/tests/ddb_parse_tests.c @@ -351,31 +351,41 @@ pool_flags_tests(void **state) } static void -date2epoch_tests(void **state) +date2cmt_time_tests(void **state) { - uint64_t epoch = -1; + uint64_t cmt_time; int rc; - rc = ddb_date2epoch(NULL, &epoch); + cmt_time = -1; + rc = ddb_date2cmt_time(NULL, &cmt_time); assert_rc_equal(rc, -DER_INVAL); - assert_int_equal(epoch, -1); - rc = ddb_date2epoch("1970-01-01 00:00:00", NULL); + assert_int_equal(cmt_time, -1); + + rc = ddb_date2cmt_time("1970-01-01 00:00:00", NULL); assert_rc_equal(rc, -DER_INVAL); - assert_int_equal(epoch, -1); - rc = ddb_date2epoch(NULL, NULL); + assert_int_equal(cmt_time, -1); + + rc = ddb_date2cmt_time(NULL, NULL); assert_rc_equal(rc, -DER_INVAL); - assert_int_equal(epoch, -1); - rc = ddb_date2epoch("foo", NULL); + assert_int_equal(cmt_time, -1); + + rc = ddb_date2cmt_time("foo", NULL); assert_rc_equal(rc, -DER_INVAL); - assert_int_equal(epoch, -1); - rc = ddb_date2epoch("0000-00-00 00:00:00", &epoch); + assert_int_equal(cmt_time, -1); + + rc = ddb_date2cmt_time("0000-00-00 00:00:00", &cmt_time); assert_rc_equal(rc, -DER_INVAL); - assert_int_equal(epoch, -1); + assert_int_equal(cmt_time, -1); + + rc = ddb_date2cmt_time("1970-01-01 00:00:00", &cmt_time); + assert_success(rc); + assert_int_equal(rc, 0); + assert_int_equal(cmt_time, 0ull); - rc = ddb_date2epoch("1970-01-01 00:00:00", &epoch); + rc = ddb_date2cmt_time("1970-01-01 00:01:00", &cmt_time); assert_success(rc); assert_int_equal(rc, 0); - assert_int_equal(epoch, 0x9aa0d62878400000ull); + assert_int_equal(cmt_time, 60ull); } /* @@ -390,7 +400,7 @@ ddb_parse_tests_run() static const struct CMUnitTest tests[] = { TEST(vos_file_parts_tests), TEST(string_to_argv_tests), TEST(parse_args_tests), TEST(parse_dtx_id_tests), TEST(keys_are_parsed_correctly), TEST(pool_flags_tests), - TEST(date2epoch_tests), + TEST(date2cmt_time_tests), }; return cmocka_run_group_tests_name("DDB helper parsing function tests", tests, NULL, NULL); diff --git a/src/utils/ddb/tests/ddb_test_driver.h b/src/utils/ddb/tests/ddb_test_driver.h index 4573a395e82..270f90d3a4c 100644 --- a/src/utils/ddb/tests/ddb_test_driver.h +++ b/src/utils/ddb/tests/ddb_test_driver.h @@ -78,7 +78,7 @@ int ddb_test_pool_setup(struct dt_vos_pool_ctx *tctx); extern uint32_t dvt_fake_print_called; extern bool dvt_fake_print_just_count; -#define DVT_FAKE_PRINT_BUFFER_SIZE (2048) +#define DVT_FAKE_PRINT_BUFFER_SIZE (0x20000) extern char dvt_fake_print_buffer[DVT_FAKE_PRINT_BUFFER_SIZE]; int dvt_fake_print(const char *fmt, ...); void dvt_fake_print_reset(void); diff --git a/src/vos/tests/vts_dtx.c b/src/vos/tests/vts_dtx.c index d378da8845e..12cd6d72728 100644 --- a/src/vos/tests/vts_dtx.c +++ b/src/vos/tests/vts_dtx.c @@ -800,7 +800,7 @@ dtx_18(void **state) daos_key_t akey; d_iov_t val_iov; uint64_t epoch; - uint32_t cnt; + uint64_t cnt; char dkey_buf[UPDATE_DKEY_SIZE]; char akey_buf[UPDATE_AKEY_SIZE]; char update_buf[UPDATE_BUF_SIZE]; @@ -842,15 +842,15 @@ dtx_18(void **state) sleep(3); - rc = vos_dtx_get_cmt_cnt(args->ctx.tc_co_hdl, &cnt); + rc = vos_dtx_get_cmt_stat(args->ctx.tc_co_hdl, &cnt, NULL); assert_rc_equal(rc, 0); assert_int_equal(cnt, 10); /* Aggregate the DTXs. */ - rc = vos_dtx_aggregate(args->ctx.tc_co_hdl, NULL, NULL); + rc = vos_dtx_aggregate(args->ctx.tc_co_hdl, NULL); assert_rc_equal(rc, 0); - rc = vos_dtx_get_cmt_cnt(args->ctx.tc_co_hdl, &cnt); + rc = vos_dtx_get_cmt_stat(args->ctx.tc_co_hdl, &cnt, NULL); assert_rc_equal(rc, 0); assert_int_equal(cnt, 0); diff --git a/src/vos/vos_dtx.c b/src/vos/vos_dtx.c index f1cfecb05cb..cf10ef498a8 100644 --- a/src/vos/vos_dtx.c +++ b/src/vos/vos_dtx.c @@ -3055,35 +3055,37 @@ vos_dtx_set_flags(daos_handle_t coh, struct dtx_id dtis[], int count, uint32_t f static int dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_container *cont, - struct vos_cont_df *cont_df, umem_off_t dbd_off, const daos_epoch_t *ep_max) + struct vos_cont_df *cont_df, umem_off_t dbd_off, const uint64_t *ts_max) { struct vos_dtx_blob_df *dbd; - umem_off_t dbd_next_off; + umem_off_t dbd_next_off = UMOFF_NULL; uint64_t epoch; - int count; - bool is_freed = false; + int dtx_aggr_count; + bool is_dbd_freed = false; int i; int rc; dbd = umem_off2ptr(umm, dbd_off); - if (dbd == NULL) - return 0; + if (dbd == NULL) { + rc = 0; + goto out; + } rc = umem_tx_begin(umm, NULL); if (unlikely(rc != 0)) { D_ERROR("Failed to TX begin for DTX aggregation " UMOFF_PF ": " DF_RC "\n", UMOFF_P(dbd_off), DP_RC(rc)); - return rc; + goto out; } - count = 0; + dtx_aggr_count = 0; epoch = cont_df->cd_newest_aggregated; for (i = 0; i < dbd->dbd_count; i++) { struct vos_dtx_cmt_ent_df *dce_df; d_iov_t kiov; dce_df = &dbd->dbd_committed_data[i]; - if (ep_max != NULL && *ep_max < dce_df->dce_epoch) + if (ts_max != NULL && *ts_max < dce_df->dce_cmt_time) break; d_iov_set(&kiov, &dce_df->dce_xid, sizeof(dce_df->dce_xid)); @@ -3091,14 +3093,14 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co &kiov, NULL); if (rc == -DER_NONEXIST) rc = 0; - if (rc != 0) { + if (unlikely(rc != 0)) { D_ERROR("Failed to remove entry for DTX aggregation " UMOFF_PF": "DF_RC"\n", UMOFF_P(dbd_off), DP_RC(rc)); - goto out; + goto out_tx_end; } - count++; + dtx_aggr_count++; if (epoch < dce_df->dce_epoch) epoch = dce_df->dce_epoch; } @@ -3110,12 +3112,12 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co D_ERROR("Failed to refresh epoch for DTX aggregation " UMOFF_PF ": " DF_RC "\n", UMOFF_P(dbd_off), DP_RC(rc)); - goto out; + goto out_tx_end; } cont_df->cd_newest_aggregated = epoch; } - if (count < dbd->dbd_count) { + if (dbd->dbd_count - dtx_aggr_count > 0) { size_t buf_len; rc = umem_tx_add_ptr(umm, &dbd->dbd_committed_data[0], @@ -3123,27 +3125,38 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co if (unlikely(rc != 0)) { D_ERROR("Failed update committed DTX blob " UMOFF_PF ": " DF_RC "\n", UMOFF_P(dbd_off), DP_RC(rc)); - goto out; + goto out_tx_end; } - buf_len = (dbd->dbd_count - count) * sizeof(dbd->dbd_committed_data[0]); - memmove(&dbd->dbd_committed_data[0], &dbd->dbd_committed_data[count], buf_len); + buf_len = (dbd->dbd_count - dtx_aggr_count) * sizeof(dbd->dbd_committed_data[0]); + memmove(&dbd->dbd_committed_data[0], &dbd->dbd_committed_data[dtx_aggr_count], + buf_len); rc = umem_tx_add_ptr(umm, &dbd->dbd_count, sizeof(dbd->dbd_count)); if (unlikely(rc != 0)) { D_ERROR("Failed update committed DTX count " UMOFF_PF ": " DF_RC "\n", UMOFF_P(dbd_off), DP_RC(rc)); - goto out; + goto out_tx_end; } - dbd->dbd_count -= count; + dbd->dbd_count -= dtx_aggr_count; } else { struct vos_dtx_blob_df *dbd_next; - struct vos_dtx_blob_df *dbd_prev; umem_off_t dbd_prev_off; dbd_next_off = dbd->dbd_next; dbd_next = umem_off2ptr(umm, dbd_next_off); dbd_prev_off = dbd->dbd_prev; - dbd_prev = umem_off2ptr(umm, dbd_prev_off); + + D_ASSERT(UMOFF_IS_NULL(dbd_prev_off)); + D_ASSERT(dbd_off == cont_df->cd_dtx_committed_head); + rc = umem_tx_add_ptr(umm, &cont_df->cd_dtx_committed_head, + sizeof(cont_df->cd_dtx_committed_head)); + if (unlikely(rc != 0)) { + D_ERROR("Failed to update head for DTX aggregation " UMOFF_PF ": " DF_RC + "\n", + UMOFF_P(dbd_off), DP_RC(rc)); + goto out_tx_end; + } + cont_df->cd_dtx_committed_head = dbd_next_off; if (dbd_next == NULL) { D_ASSERT(dbd_off == cont_df->cd_dtx_committed_tail); @@ -3153,7 +3166,7 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co D_ERROR("Failed to update tail for DTX aggregation " UMOFF_PF ": " DF_RC "\n", UMOFF_P(dbd_off), DP_RC(rc)); - goto out; + goto out_tx_end; } cont_df->cd_dtx_committed_tail = dbd_prev_off; } else { @@ -3162,102 +3175,80 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co D_ERROR("Failed to update previous DTXs blob for DTX " "aggregation " UMOFF_PF ": " DF_RC "\n", UMOFF_P(dbd_off), DP_RC(rc)); - goto out; + goto out_tx_end; } dbd_next->dbd_prev = dbd_prev_off; } - if (dbd_prev == NULL) { - D_ASSERT(dbd_off == cont_df->cd_dtx_committed_head); - rc = umem_tx_add_ptr(umm, &cont_df->cd_dtx_committed_head, - sizeof(cont_df->cd_dtx_committed_head)); - if (unlikely(rc != 0)) { - D_ERROR("Failed to update head for DTX aggregation " UMOFF_PF - ": " DF_RC "\n", - UMOFF_P(dbd_off), DP_RC(rc)); - goto out; - } - cont_df->cd_dtx_committed_head = dbd_next_off; - } else { - rc = umem_tx_add_ptr(umm, &dbd_prev->dbd_next, sizeof(dbd_prev->dbd_next)); - if (unlikely(rc != 0)) { - D_ERROR( - "Failed to update next DTXs blob for DTX aggregation " UMOFF_PF - ": " DF_RC "\n", - UMOFF_P(dbd_off), DP_RC(rc)); - goto out; - } - dbd_prev->dbd_next = dbd_next_off; - } - rc = umem_free(umm, dbd_off); if (unlikely(rc != 0)) { D_ERROR("Failed to free DTXs blob " UMOFF_PF ": " DF_RC "\n", UMOFF_P(dbd_off), DP_RC(rc)); - goto out; + goto out_tx_end; } - is_freed = true; + is_dbd_freed = true; } -out: +out_tx_end: rc = umem_tx_end(umm, rc); - if (likely(rc == 0)) { - if (is_freed && cont->vc_cmt_dtx_reindex_pos == dbd_off) - cont->vc_cmt_dtx_reindex_pos = dbd_next_off; - cont->vc_dtx_committed_count -= count; - cont->vc_pool->vp_dtx_committed_count -= count; - d_tm_dec_gauge(tls->vtl_committed, count); + if (likely(rc != 0)) { + DL_ERROR(rc, + "Failed to commit DTX commit aggregation of blob %p (" UMOFF_PF + ") of cont " DF_UUID, + dbd, UMOFF_P(dbd_off), DP_UUID(cont->vc_id)); + goto out; } - DL_CDEBUG(rc != 0, DLOG_ERR, DB_IO, rc, - "Release DTX committed blob %p (" UMOFF_PF ") for cont " DF_UUID, dbd, - UMOFF_P(dbd_off), DP_UUID(cont->vc_id)); + if (dtx_aggr_count > 0) { + cont->vc_dtx_committed_count -= dtx_aggr_count; + cont->vc_pool->vp_dtx_committed_count -= dtx_aggr_count; + d_tm_dec_gauge(tls->vtl_committed, dtx_aggr_count); + D_DEBUG(DB_IO, + "Release %i DTX committed entries of blob %p (" UMOFF_PF + ") of cont " DF_UUID, + dtx_aggr_count, dbd, UMOFF_P(dbd_off), DP_UUID(cont->vc_id)); + } + + if (is_dbd_freed) { + cont->vc_cmt_dtx_reindex_pos = dbd_next_off; + D_DEBUG(DB_IO, + "Removed blob of DTX committed entries %p (" UMOFF_PF ") of cont " DF_UUID, + dbd, UMOFF_P(dbd_off), DP_UUID(cont->vc_id)); + rc = 1; + } + +out: return rc; } int -vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max, umem_off_t *blob) +vos_dtx_aggregate(daos_handle_t coh, const uint64_t *ts_max) { struct vos_container *cont; struct vos_tls *tls; struct umem_instance *umm; struct vos_cont_df *cont_df; umem_off_t dbd_off; - umem_off_t dbd_next_off; int rc; - cont = vos_hdl2cont(coh); - D_ASSERT(cont != NULL); - tls = vos_tls_get(false); D_ASSERT(tls != NULL); + cont = vos_hdl2cont(coh); + D_ASSERT(cont != NULL); + D_ASSERT(cont->vc_pool->vp_sysdb == false); + umm = vos_cont2umm(cont); cont_df = cont->vc_cont_df; dbd_off = cont_df->cd_dtx_committed_head; - if (UMOFF_IS_NULL(dbd_off)) { - rc = 0; - goto done; - } - - if (blob != NULL) { - struct vos_dtx_blob_df *dbd; - - if (!UMOFF_IS_NULL(*blob)) - dbd_off = *blob; - dbd = umem_off2ptr(umm, dbd_off); - dbd_next_off = dbd->dbd_next; - } - D_ASSERT(cont->vc_pool->vp_sysdb == false); /* Take the opportunity to free some memory if we can */ lrua_array_aggregate(cont->vc_dtx_array); - rc = dtx_blob_aggregate(umm, tls, cont, cont_df, dbd_off, ep_max); - if (rc == 0 && blob != NULL) - *blob = dbd_next_off; + rc = dtx_blob_aggregate(umm, tls, cont, cont_df, dbd_off, ts_max); + if (rc == 1 && UMOFF_IS_NULL(cont_df->cd_dtx_committed_head)) + rc = 0; -done: return rc; } @@ -4087,13 +4078,20 @@ vos_dtx_local_end(struct dtx_handle *dth, int result) return result; } +struct dtx_time_stat_priv { + struct dtx_time_stat dts_pub; + /* DAOS-17322: Use of floating point to avoid integer overflow issue */ + long double dts_mean[2]; +}; + int -vos_dtx_get_cmt_cnt(daos_handle_t coh, uint32_t *cnt) +vos_dtx_get_cmt_stat(daos_handle_t coh, uint64_t *cmt_cnt, struct dtx_time_stat *dts) { struct umem_instance *umm; struct vos_container *cont; struct vos_dtx_blob_df *dbd; - uint32_t tmp; + uint64_t cmt_cnt_tmp; + struct dtx_time_stat_priv dts_tmp = {0}; int rc; cont = vos_hdl2cont(coh); @@ -4101,14 +4099,16 @@ vos_dtx_get_cmt_cnt(daos_handle_t coh, uint32_t *cnt) rc = -DER_INVAL; goto out; } - if (cnt == NULL) { + if (cmt_cnt == NULL) { rc = -DER_INVAL; goto out; } - tmp = 0; + cmt_cnt_tmp = 0; umm = vos_cont2umm(cont); dbd = umem_off2ptr(umm, cont->vc_cont_df->cd_dtx_committed_head); + dts_tmp.dts_pub.dts_epoch[0] = DAOS_EPOCH_MAX; + dts_tmp.dts_pub.dts_cmt_time[0] = UINT64_MAX; while (dbd != NULL) { if (dbd->dbd_magic != DTX_CMT_BLOB_MAGIC) { D_ERROR("Committed DTX blob with bad magic: container=" DF_UUID @@ -4117,12 +4117,47 @@ vos_dtx_get_cmt_cnt(daos_handle_t coh, uint32_t *cnt) rc = -DER_INVAL; goto out; } - tmp += dbd->dbd_count; + + cmt_cnt_tmp += dbd->dbd_count; + if (dts != NULL) { + int i; + + for (i = 0; i < dbd->dbd_count; i++) { + struct vos_dtx_cmt_ent_df *dce_df; + + dce_df = &dbd->dbd_committed_data[i]; + + if (dts_tmp.dts_pub.dts_epoch[0] > dce_df->dce_epoch) + dts_tmp.dts_pub.dts_epoch[0] = dce_df->dce_epoch; + if (dts_tmp.dts_pub.dts_epoch[1] < dce_df->dce_epoch) + dts_tmp.dts_pub.dts_epoch[1] = dce_df->dce_epoch; + dts_tmp.dts_mean[0] += dce_df->dce_epoch; + + if (dts_tmp.dts_pub.dts_cmt_time[0] > dce_df->dce_cmt_time) + dts_tmp.dts_pub.dts_cmt_time[0] = dce_df->dce_cmt_time; + if (dts_tmp.dts_pub.dts_cmt_time[1] < dce_df->dce_cmt_time) + dts_tmp.dts_pub.dts_cmt_time[1] = dce_df->dce_cmt_time; + dts_tmp.dts_mean[1] += dce_df->dce_cmt_time; + } + } dbd = umem_off2ptr(umm, dbd->dbd_next); } - *cnt = tmp; + *cmt_cnt = cmt_cnt_tmp; + + if (dts != NULL) { + if (cmt_cnt_tmp != 0) { + dts_tmp.dts_mean[0] /= (long double)cmt_cnt_tmp; + dts_tmp.dts_pub.dts_epoch[2] = (daos_epoch_t)dts_tmp.dts_mean[0]; + + dts_tmp.dts_mean[1] /= (long double)cmt_cnt_tmp; + dts_tmp.dts_pub.dts_cmt_time[2] = (uint64_t)dts_tmp.dts_mean[1]; + } + + memcpy(dts, &dts_tmp, sizeof(struct dtx_time_stat)); + } + rc = 0; out: From 9ed953298fb38452958333254eeaa76ca1319ac1 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Wed, 15 Oct 2025 19:35:03 +0000 Subject: [PATCH 13/20] DAOS-17322 ddb: DTX aggregation with ddb Fix clang format issues. Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/dtx/tests/dts_aggregate.c | 40 +++++++++++++++++------------------ src/utils/ddb/ddb.c | 2 +- src/utils/ddb/ddb_commands.c | 4 ++-- src/vos/vos_dtx.c | 14 ++++++------ 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/dtx/tests/dts_aggregate.c b/src/dtx/tests/dts_aggregate.c index 0bc06e1c3b8..0f341cf9f5b 100644 --- a/src/dtx/tests/dts_aggregate.c +++ b/src/dtx/tests/dts_aggregate.c @@ -169,8 +169,8 @@ tx_free(struct umem_instance *umm, umem_off_t umoff) static void prep_dtx_entries(void) { - int i; - uint64_t cmt_time; + int i; + uint64_t cmt_time; cmt_time = CMT_TIME_START; for (i = 0; i < DBD_BLOBS_CAP; i++) { @@ -179,7 +179,7 @@ prep_dtx_entries(void) for (j = 0; j < DBD_BLOB_DF_CAP; j++) { struct vos_dtx_cmt_ent_df *dce_df; - dce_df = &mock_dbds[i]->dbd_committed_data[j]; + dce_df = &mock_dbds[i]->dbd_committed_data[j]; dce_df->dce_cmt_time = cmt_time; dce_df->dce_epoch = cmt_time + EPOCH_OFFSET; cmt_time += CMT_TIME_STEP; @@ -193,8 +193,8 @@ prep_dtx_entries(void) static void check_rollback(void) { - int i; - uint64_t cmt_time; + int i; + uint64_t cmt_time; cmt_time = CMT_TIME_START; for (i = 0; i < DBD_BLOBS_CAP; i++) { @@ -485,10 +485,10 @@ test_umm_free_error(void **unused) static void test_committed_data_error(void **unused) { - uint64_t cmt_time; - int i; - int rc; - const int dtx_count = 3; + uint64_t cmt_time; + int i; + int rc; + const int dtx_count = 3; will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); @@ -514,10 +514,10 @@ test_committed_data_error(void **unused) static void test_dbd_count_error(void **unused) { - uint64_t cmt_time; - int i; - int rc; - const int dtx_count = 3; + uint64_t cmt_time; + int i; + int rc; + const int dtx_count = 3; will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); @@ -546,10 +546,10 @@ test_dbd_count_error(void **unused) static void test_umm_commit_error(void **unused) { - uint64_t cmt_time; - int i; - int rc; - const int dtx_count = 3; + uint64_t cmt_time; + int i; + int rc; + const int dtx_count = 3; will_return(__wrap_vos_tls_get, &mock_tls); will_return(tx_begin, 0); @@ -671,9 +671,9 @@ test_one_blob(void **unused) static void test_10_entries(void **unused) { - uint64_t cmt_time; - int i; - int rc; + uint64_t cmt_time; + int i; + int rc; prep_dtx_entries(); diff --git a/src/utils/ddb/ddb.c b/src/utils/ddb/ddb.c index 79612c95677..a0e975ad97d 100644 --- a/src/utils/ddb/ddb.c +++ b/src/utils/ddb/ddb.c @@ -947,7 +947,7 @@ dtx_aggr_option_parse(struct ddb_ctx *ctx, struct dtx_aggr_options *cmd_args, ui "'--cmt_time' option can not be used multiple time\n"); return -DER_INVAL; } - errno = 0; + errno = 0; cmd_args->cmt_time = strtoull(optarg, &endptr, 10); if (errno != 0 || endptr == optarg || *endptr != '\0') { ddb_error(ctx, "'--cmt_time' option arg format is invalid\n"); diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index 33db066bdd4..45ee27404e1 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -1593,8 +1593,8 @@ ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt) args.time_stat.dts_cmt_time[0] = UINT64_MAX; args.time_stat.dts_epoch[0] = DAOS_EPOCH_MAX; args.aggr_epoch = DAOS_EPOCH_MAX; - param.ip_hdl = ctx->dc_poh; - param.ip_epr.epr_hi = DAOS_EPOCH_MAX; + param.ip_hdl = ctx->dc_poh; + param.ip_epr.epr_hi = DAOS_EPOCH_MAX; do { rc = vos_iterate(¶m, VOS_ITER_COUUID, false, &anchors, NULL, dtx_stat_cont_cb, &args, NULL); diff --git a/src/vos/vos_dtx.c b/src/vos/vos_dtx.c index cf10ef498a8..fe29c1a6d59 100644 --- a/src/vos/vos_dtx.c +++ b/src/vos/vos_dtx.c @@ -3079,7 +3079,7 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co } dtx_aggr_count = 0; - epoch = cont_df->cd_newest_aggregated; + epoch = cont_df->cd_newest_aggregated; for (i = 0; i < dbd->dbd_count; i++) { struct vos_dtx_cmt_ent_df *dce_df; d_iov_t kiov; @@ -3225,12 +3225,12 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co int vos_dtx_aggregate(daos_handle_t coh, const uint64_t *ts_max) { - struct vos_container *cont; - struct vos_tls *tls; - struct umem_instance *umm; - struct vos_cont_df *cont_df; - umem_off_t dbd_off; - int rc; + struct vos_container *cont; + struct vos_tls *tls; + struct umem_instance *umm; + struct vos_cont_df *cont_df; + umem_off_t dbd_off; + int rc; tls = vos_tls_get(false); D_ASSERT(tls != NULL); From a7bbda1fd70ea987d2bda763a539e97acc963fc8 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Thu, 16 Oct 2025 06:31:10 +0000 Subject: [PATCH 14/20] DAOS-17322 ddb: DTX aggregation with ddb Fix unit test. Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/utils/ddb/tests/ddb_cmd_options_tests.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils/ddb/tests/ddb_cmd_options_tests.c b/src/utils/ddb/tests/ddb_cmd_options_tests.c index 82562870db0..ee106a8cd5f 100644 --- a/src/utils/ddb/tests/ddb_cmd_options_tests.c +++ b/src/utils/ddb/tests/ddb_cmd_options_tests.c @@ -327,14 +327,14 @@ dtx_aggr_parsing(void **state) test_run_inval_cmd("dtx_aggr", "path"); test_run_inval_cmd("dtx_aggr", "path", "extra"); test_run_inval_cmd("dtx_aggr", "-z"); - test_run_inval_cmd("dtx_aggr", "-e", "foo", "path"); - test_run_inval_cmd("dtx_aggr", "-e", "666", "-e", "999", "path"); + test_run_inval_cmd("dtx_aggr", "-t", "foo", "path"); + test_run_inval_cmd("dtx_aggr", "-t", "666", "-t", "999", "path"); test_run_inval_cmd("dtx_aggr", "-d", "1970-01-01 00:00:00", "-d", "2000-01-01 00:00:00", "path"); - test_run_inval_cmd("dtx_aggr", "-e", "42", "-d", "1970-01-01 00:00:00", "path"); + test_run_inval_cmd("dtx_aggr", "-t", "42", "-d", "1970-01-01 00:00:00", "path"); /* test all options and arguments */ - test_run_cmd(&info, "dtx_aggr", "-e", "42"); + test_run_cmd(&info, "dtx_aggr", "-t", "42"); assert_null(options->path); assert_non_null(options->cmt_time); assert_null(options->cmt_date); @@ -344,7 +344,7 @@ dtx_aggr_parsing(void **state) assert_null(options->cmt_time); assert_non_null(options->cmt_date); - test_run_cmd(&info, "dtx_aggr", "-e", "42", "path"); + test_run_cmd(&info, "dtx_aggr", "-t", "42", "path"); assert_non_null(options->path); assert_non_null(options->cmt_time); assert_null(options->cmt_date); From 2a604ef9cc9455211a6268eb1b5537388a874923 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Thu, 16 Oct 2025 06:44:34 +0000 Subject: [PATCH 15/20] DAOS-17322 ddb: DTX aggregation with ddb Fix function argument name. Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/vos/vos_dtx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vos/vos_dtx.c b/src/vos/vos_dtx.c index fe29c1a6d59..f3cd477239b 100644 --- a/src/vos/vos_dtx.c +++ b/src/vos/vos_dtx.c @@ -3055,7 +3055,7 @@ vos_dtx_set_flags(daos_handle_t coh, struct dtx_id dtis[], int count, uint32_t f static int dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_container *cont, - struct vos_cont_df *cont_df, umem_off_t dbd_off, const uint64_t *ts_max) + struct vos_cont_df *cont_df, umem_off_t dbd_off, const uint64_t *cmt_time) { struct vos_dtx_blob_df *dbd; umem_off_t dbd_next_off = UMOFF_NULL; @@ -3085,7 +3085,7 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co d_iov_t kiov; dce_df = &dbd->dbd_committed_data[i]; - if (ts_max != NULL && *ts_max < dce_df->dce_cmt_time) + if (cmt_time != NULL && *cmt_time < dce_df->dce_cmt_time) break; d_iov_set(&kiov, &dce_df->dce_xid, sizeof(dce_df->dce_xid)); @@ -3223,7 +3223,7 @@ dtx_blob_aggregate(struct umem_instance *umm, struct vos_tls *tls, struct vos_co } int -vos_dtx_aggregate(daos_handle_t coh, const uint64_t *ts_max) +vos_dtx_aggregate(daos_handle_t coh, const uint64_t *cmt_time) { struct vos_container *cont; struct vos_tls *tls; @@ -3245,7 +3245,7 @@ vos_dtx_aggregate(daos_handle_t coh, const uint64_t *ts_max) /* Take the opportunity to free some memory if we can */ lrua_array_aggregate(cont->vc_dtx_array); - rc = dtx_blob_aggregate(umm, tls, cont, cont_df, dbd_off, ts_max); + rc = dtx_blob_aggregate(umm, tls, cont, cont_df, dbd_off, cmt_time); if (rc == 1 && UMOFF_IS_NULL(cont_df->cd_dtx_committed_head)) rc = 0; From 74a85bbfaa395fd8973180a67cf5e0471884c20c Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Thu, 16 Oct 2025 06:46:52 +0000 Subject: [PATCH 16/20] DAOS-17322 ddb: DTX aggregation with ddb Fix function argument comment. Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/include/daos_srv/vos.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/include/daos_srv/vos.h b/src/include/daos_srv/vos.h index d26b8e54683..661026cd915 100644 --- a/src/include/daos_srv/vos.h +++ b/src/include/daos_srv/vos.h @@ -236,14 +236,14 @@ vos_dtx_set_flags(daos_handle_t coh, struct dtx_id dtis[], int count, uint32_t f * Aggregate the committed DTXs. * * \param coh [IN] Container open handle. - * \param ep_max [IN] The upper epoch to aggregate. + * \param cmt_time [IN] The upper commit time to aggregate, or NULL. * - * \return Negative value if error, zero if all DTX entries with an epoch lower - * or equal to ep_max have been aggregated, one if all the targeted DTX - * entries may not have been aggregated. + * \return Negative value if error, zero if all DTX entries with a commit time + * lower or equal to cmt_time have been aggregated, one if all the + * targeted DTX entries may not have been aggregated. */ int -vos_dtx_aggregate(daos_handle_t coh, const daos_epoch_t *ep_max); +vos_dtx_aggregate(daos_handle_t coh, const uint64_t *cmt_time); /** * Query the container's DTXs statistics information. From c6ee6411da11d4533cea7136d2f1c5f9f12057ff Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Fri, 17 Oct 2025 14:39:24 +0000 Subject: [PATCH 17/20] DAOS-17322 ddb: DTX aggregation with ddb Fix reviewers comments: - Fix comments - Use constant instead of hard coded value - Fix invalid mean calculation - Remove useless return Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/include/daos_srv/vos.h | 11 ++--- src/utils/ddb/ddb_commands.c | 79 ++++++++++++++++++------------------ src/utils/ddb/ddb_parse.h | 2 +- 3 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/include/daos_srv/vos.h b/src/include/daos_srv/vos.h index 661026cd915..2e798dc8a9f 100644 --- a/src/include/daos_srv/vos.h +++ b/src/include/daos_srv/vos.h @@ -238,9 +238,9 @@ vos_dtx_set_flags(daos_handle_t coh, struct dtx_id dtis[], int count, uint32_t f * \param coh [IN] Container open handle. * \param cmt_time [IN] The upper commit time to aggregate, or NULL. * - * \return Negative value if error, zero if all DTX entries with a commit time - * lower or equal to cmt_time have been aggregated, one if all the - * targeted DTX entries may not have been aggregated. + * \return Zero if all qualified DTX entries have been removed. + * Positive value if more DTX entries can be eventually aggregated. + * Negative value for error. */ int vos_dtx_aggregate(daos_handle_t coh, const uint64_t *cmt_time); @@ -1781,9 +1781,10 @@ bool vos_oi_exist(daos_handle_t coh, daos_unit_oid_t oid); /* Timing statistic of DTX entries */ +#define DTX_TIME_STAT_COUNT 3 struct dtx_time_stat { - daos_epoch_t dts_epoch[3]; - uint64_t dts_cmt_time[3]; + daos_epoch_t dts_epoch[DTX_TIME_STAT_COUNT]; + uint64_t dts_cmt_time[DTX_TIME_STAT_COUNT]; }; /** diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index 45ee27404e1..3d86705c00c 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -1339,7 +1339,7 @@ dtx_print_epoch_stat(struct ddb_ctx *ctx, const char *prefix, const daos_epoch_t ddb_printf(ctx, "\t- %s%s", prefix, align); rc = 0; - for (i = 0; i < 3; i++) { + for (i = 0; i < DTX_TIME_STAT_COUNT; i++) { if (epoch_stats[i] == 0 || epoch_stats[i] == DAOS_EPOCH_MAX) { ddb_printf(ctx, "%s=NA (NA)", stat_names[i]); } else { @@ -1348,7 +1348,7 @@ dtx_print_epoch_stat(struct ddb_ctx *ctx, const char *prefix, const daos_epoch_t if (!SUCCESS(rc)) goto out; } - if (i < 2) + if (i < DTX_TIME_STAT_COUNT - 1) ddb_print(ctx, ", "); } ddb_print(ctx, "\n"); @@ -1367,7 +1367,7 @@ dtx_print_time_stat(struct ddb_ctx *ctx, const char *prefix, const uint64_t cmt_ ddb_printf(ctx, "\t- %s%s", prefix, align); rc = 0; - for (i = 0; i < 3; i++) { + for (i = 0; i < DTX_TIME_STAT_COUNT; i++) { if (cmt_time_stats[i] == 0 || cmt_time_stats[i] == UINT64_MAX) { ddb_printf(ctx, "%s=NA (NA)", stat_names[i]); } else { @@ -1386,7 +1386,7 @@ dtx_print_time_stat(struct ddb_ctx *ctx, const char *prefix, const uint64_t cmt_ ddb_printf(ctx, "%s=%s (%" PRIu64 ")", stat_names[i], buf, cmt_time_stats[i]); } - if (i < 2) + if (i < DTX_TIME_STAT_COUNT - 1) ddb_print(ctx, ", "); } ddb_print(ctx, "\n"); @@ -1503,43 +1503,43 @@ dtx_stat_cont_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type if (!SUCCESS(rc)) goto done; - args->cmt_cnt += args_tmp.cmt_cnt; - - if (!args->opt->details) - goto done; - if (args->aggr_epoch < args_tmp.aggr_epoch) - args->aggr_epoch = args_tmp.aggr_epoch; - if (args->time_stat.dts_cmt_time[0] > args_tmp.time_stat.dts_cmt_time[0]) - args->time_stat.dts_cmt_time[0] = args_tmp.time_stat.dts_cmt_time[0]; - if (args->time_stat.dts_cmt_time[1] < args_tmp.time_stat.dts_cmt_time[1]) - args->time_stat.dts_cmt_time[1] = args_tmp.time_stat.dts_cmt_time[1]; - if (args->time_stat.dts_cmt_time[2] == 0) - args->time_stat.dts_cmt_time[2] = args_tmp.time_stat.dts_cmt_time[2]; - else { - long double tmp_mean; - - tmp_mean = args->time_stat.dts_cmt_time[2] * (long double)args->cmt_cnt; - tmp_mean += - (long double)args_tmp.time_stat.dts_cmt_time[2] * (long double)args_tmp.cmt_cnt; - tmp_mean /= (long double)(args->cmt_cnt + args_tmp.cmt_cnt); - args->time_stat.dts_cmt_time[2] = tmp_mean; - } - if (args->time_stat.dts_epoch[0] > args_tmp.time_stat.dts_epoch[0]) - args->time_stat.dts_epoch[0] = args_tmp.time_stat.dts_epoch[0]; - if (args->time_stat.dts_epoch[1] < args_tmp.time_stat.dts_epoch[1]) - args->time_stat.dts_epoch[1] = args_tmp.time_stat.dts_epoch[1]; - if (args->time_stat.dts_epoch[2] == 0) - args->time_stat.dts_epoch[2] = args_tmp.time_stat.dts_epoch[2]; - else { - long double tmp_mean; - - tmp_mean = args->time_stat.dts_epoch[2] * (long double)args->cmt_cnt; - tmp_mean += - (long double)args_tmp.time_stat.dts_epoch[2] * (long double)args_tmp.cmt_cnt; - tmp_mean /= (long double)(args->cmt_cnt + args_tmp.cmt_cnt); - args->time_stat.dts_epoch[2] = tmp_mean; + if (args->opt->details) { + if (args->aggr_epoch < args_tmp.aggr_epoch) + args->aggr_epoch = args_tmp.aggr_epoch; + if (args->time_stat.dts_cmt_time[0] > args_tmp.time_stat.dts_cmt_time[0]) + args->time_stat.dts_cmt_time[0] = args_tmp.time_stat.dts_cmt_time[0]; + if (args->time_stat.dts_cmt_time[1] < args_tmp.time_stat.dts_cmt_time[1]) + args->time_stat.dts_cmt_time[1] = args_tmp.time_stat.dts_cmt_time[1]; + if (args->time_stat.dts_cmt_time[2] == 0) + args->time_stat.dts_cmt_time[2] = args_tmp.time_stat.dts_cmt_time[2]; + else { + long double tmp_mean; + + tmp_mean = args->time_stat.dts_cmt_time[2] * (long double)args->cmt_cnt; + tmp_mean += (long double)args_tmp.time_stat.dts_cmt_time[2] * + (long double)args_tmp.cmt_cnt; + tmp_mean /= (long double)(args->cmt_cnt + args_tmp.cmt_cnt); + args->time_stat.dts_cmt_time[2] = tmp_mean; + } + if (args->time_stat.dts_epoch[0] > args_tmp.time_stat.dts_epoch[0]) + args->time_stat.dts_epoch[0] = args_tmp.time_stat.dts_epoch[0]; + if (args->time_stat.dts_epoch[1] < args_tmp.time_stat.dts_epoch[1]) + args->time_stat.dts_epoch[1] = args_tmp.time_stat.dts_epoch[1]; + if (args->time_stat.dts_epoch[2] == 0) + args->time_stat.dts_epoch[2] = args_tmp.time_stat.dts_epoch[2]; + else { + long double tmp_mean; + + tmp_mean = args->time_stat.dts_epoch[2] * (long double)args->cmt_cnt; + tmp_mean += (long double)args_tmp.time_stat.dts_epoch[2] * + (long double)args_tmp.cmt_cnt; + tmp_mean /= (long double)(args->cmt_cnt + args_tmp.cmt_cnt); + args->time_stat.dts_epoch[2] = tmp_mean; + } } + args->cmt_cnt += args_tmp.cmt_cnt; + done: itp_free(&itp); @@ -1646,7 +1646,6 @@ dtx_aggr_cont(struct ddb_ctx *ctx, struct dv_indexed_tree_path *itp, uint64_t *c ddb_error(ctx, "Failed to close container "); itp_print_full(ctx, itp); ddb_errorf(ctx, ": " DF_RC "\n", DP_RC(rc)); - return rc; } return rc; diff --git a/src/utils/ddb/ddb_parse.h b/src/utils/ddb/ddb_parse.h index 6eaab9eab57..df5d43771db 100644 --- a/src/utils/ddb/ddb_parse.h +++ b/src/utils/ddb/ddb_parse.h @@ -52,7 +52,7 @@ int ddb_parse_key(const char *input, daos_key_t *key); */ int ddb_parse_dtx_id(const char *dtx_id_str, struct dtx_id *dtx_id); -/* Parse a string representing a date into an epoch */ +/* Parse a string representing a date into a DTX commit time */ int ddb_date2cmt_time(const char *date, uint64_t *cmt_time); From 12c808454be7a22963cd8242581ff1d17c0964f2 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Fri, 17 Oct 2025 17:26:55 +0000 Subject: [PATCH 18/20] DAOS-17322 ddb: DTX aggregation with ddb Fix invalid last epoch aggregation computation. Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/utils/ddb/ddb_commands.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/ddb/ddb_commands.c b/src/utils/ddb/ddb_commands.c index 3d86705c00c..0e26686a24c 100644 --- a/src/utils/ddb/ddb_commands.c +++ b/src/utils/ddb/ddb_commands.c @@ -1539,6 +1539,8 @@ dtx_stat_cont_cb(daos_handle_t ih, vos_iter_entry_t *entry, vos_iter_type_t type } args->cmt_cnt += args_tmp.cmt_cnt; + if (args->aggr_epoch < args_tmp.aggr_epoch) + args->aggr_epoch = args_tmp.aggr_epoch; done: itp_free(&itp); @@ -1592,7 +1594,6 @@ ddb_run_dtx_stat(struct ddb_ctx *ctx, struct dtx_stat_options *opt) args.cmt_cnt = 0; args.time_stat.dts_cmt_time[0] = UINT64_MAX; args.time_stat.dts_epoch[0] = DAOS_EPOCH_MAX; - args.aggr_epoch = DAOS_EPOCH_MAX; param.ip_hdl = ctx->dc_poh; param.ip_epr.epr_hi = DAOS_EPOCH_MAX; do { From 643bef156a42cadbd72d79ce4545a0d5fbd170e7 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Fri, 17 Oct 2025 17:33:41 +0000 Subject: [PATCH 19/20] DAOS-17322 ddb: DTX aggregation with ddb Fix minor coding sytle. Features: aggregation Signed-off-by: Cedri,c Koch-Hofer --- src/utils/ddb/tests/ddb_commands_tests.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/ddb/tests/ddb_commands_tests.c b/src/utils/ddb/tests/ddb_commands_tests.c index 1b89a95594a..e638dbbd231 100644 --- a/src/utils/ddb/tests/ddb_commands_tests.c +++ b/src/utils/ddb/tests/ddb_commands_tests.c @@ -537,10 +537,10 @@ dtx_aggr_tests(void **state) assert_success(ddb_run_dtx_stat(&ctx, &opt_stat)); assert_regex_match(dvt_fake_print_buffer, "^[[:blank:]]+- Committed DTX count:[[:blank:]]+10$"); - opt_aggr.cmt_time = dtx_get_cmt_time(dvt_fake_print_buffer); /* Test aggregation with an epoch (i.e. aggregate 3 first DTX entries) */ opt_aggr.format = DDB_DTX_AGGR_CMT_TIME; + opt_aggr.cmt_time = dtx_get_cmt_time(dvt_fake_print_buffer); opt_aggr.cmt_time += 1; assert_success(ddb_run_dtx_aggr(&ctx, &opt_aggr)); From ec9038cb7eea91eb7d360b794862f5a27a581ed3 Mon Sep 17 00:00:00 2001 From: Cedric Koch-Hofer Date: Fri, 17 Oct 2025 18:38:36 +0000 Subject: [PATCH 20/20] DAOS-17322 ddb: DTX aggregation with ddb Fix clang format issues. Features: aggregation Signed-off-by: Cedric Koch-Hofer --- src/utils/ddb/tests/ddb_commands_tests.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/utils/ddb/tests/ddb_commands_tests.c b/src/utils/ddb/tests/ddb_commands_tests.c index e638dbbd231..acb920d9fce 100644 --- a/src/utils/ddb/tests/ddb_commands_tests.c +++ b/src/utils/ddb/tests/ddb_commands_tests.c @@ -539,9 +539,8 @@ dtx_aggr_tests(void **state) "^[[:blank:]]+- Committed DTX count:[[:blank:]]+10$"); /* Test aggregation with an epoch (i.e. aggregate 3 first DTX entries) */ - opt_aggr.format = DDB_DTX_AGGR_CMT_TIME; - opt_aggr.cmt_time = dtx_get_cmt_time(dvt_fake_print_buffer); - opt_aggr.cmt_time += 1; + opt_aggr.format = DDB_DTX_AGGR_CMT_TIME; + opt_aggr.cmt_time = dtx_get_cmt_time(dvt_fake_print_buffer) + 1; assert_success(ddb_run_dtx_aggr(&ctx, &opt_aggr)); dvt_fake_print_reset();