Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2e21fba
DAOS-18304 ddb: store VOS path in vos_file_parts and raise path size …
kanard38 Apr 28, 2026
dfc555e
DAOS-18304 ddb: simplify dv_pool_open/destroy API and remove path cac…
kanard38 Apr 28, 2026
03378bb
DAOS-18304 ddb: update C test suites for new dv_pool_open/destroy API
kanard38 Apr 28, 2026
b6f8125
DAOS-18304 ddb: introduce DdbAPI interface and convert DdbContext to …
kanard38 Apr 28, 2026
5ca13e0
DAOS-18304 ddb: propagate DdbAPI through command and CLI layers
kanard38 Apr 28, 2026
7b3c6ac
DAOS-18304 ddb: use MOCKED_VOS_PATH_STR macro in Go unit tests
kanard38 Apr 28, 2026
b0f7207
DAOS-18304 ddb: replace noAutoOpen map with a string slice
kanard38 Apr 28, 2026
425fc48
daos-18304 ddb: capitalize VOS in help strings
kanard38 May 4, 2026
beaeed7
daos-18304 ddb: improve unknown command error message
kanard38 May 4, 2026
c12dcbb
Merge remote-tracking branch 'origin/master' into ckochhof/fix/master…
kanard38 May 5, 2026
b622710
Merge remote-tracking branch 'origin/master' into ckochhof/fix/master…
kanard38 May 7, 2026
232dde6
DAOS-18304 ddb: fix invalid merge conflict resolution in ddb_vos_tests.c
kanard38 May 7, 2026
bdd01e8
DAOS-18304 ddb: clarify why DTX records are inserted in suite setup
kanard38 May 7, 2026
de22289
DAOS-18304 ddb: fix inconsistent help strings for close and rm_pool
kanard38 May 7, 2026
56e0956
DAOS-18304 ddb: add doc comments to all exported Go symbols
kanard38 May 7, 2026
9de3531
DAOS-18304 ddb: decouple dv_pool_open from ddb_ctx
kanard38 May 11, 2026
7310312
DAOS-18304 ddb: replace DdbAPI god interface with build-tag CGo stubs
kanard38 May 11, 2026
d683277
DAOS-18304 ddb: move DtxAggr validation to command layer
kanard38 May 11, 2026
c4a1c1f
DAOS-18304 ddb: use grumbleUnknownCmdErr in unknownCmdError message
kanard38 May 11, 2026
0e95e3a
DAOS-18304 ddb: fix gofmt alignment in libddb Go wrappers
kanard38 May 11, 2026
961d42a
DAOS-18304 ddb: fix and improve dv_pool_open doc comment
kanard38 May 11, 2026
d3e88ed
Merge remote-tracking branch 'origin/master' into ckochhof/fix/master…
kanard38 May 11, 2026
a6cfa13
DAOS-18304 ddb: fix clang-format line length in ddb_vos_tests
kanard38 May 11, 2026
f103aec
DAOS-18304 ddb: fix dv_pool_open Doxygen style to match DAOS convention
kanard38 May 11, 2026
4ff740d
DAOS-18304 ddb: restore user-friendly unknown command error message
kanard38 May 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 91 additions & 67 deletions src/control/cmd/ddb/commands_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,46 +37,67 @@ func freeString(s *C.char) {
C.free(unsafe.Pointer(s))
}

func SetCString(out **C.char, s string) func() {
cstr := C.CString(s)
*out = cstr
type DdbAPI interface {
Comment thread
kjacque marked this conversation as resolved.
Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I understand the intent behind this interface, but giant predefined "god" interfaces is a well-known anti-pattern in Go. Per the community review guidelines, interfaces should be defined at the consumer site. This guidance is codified in the "Accept interfaces, return structs" proverb. While superficially the predefined interface may seem like the easier or more convenient path, it's a trap. It means that every implementation has to define all of those methods, leading to maintenance headaches and friction that results in good tests not being written.

At a minimum, just rearchitecting the tests to define interfaces for the subset of DdbContext methods that will be tested should make this much less of a tech debt trap.

For an alternative approach to doing Go/cgo testing, take a look at src/control/lib/daos/api. It's not perfect, but I think it achieves the goal of exposing as much of the Go surface for unit testing as possible. It also demonstrates a better design (IMO) than a single API object thing that has (at current count) 28 methods. I suggest a smaller, focused fix for the db_path problem and a follow up effort to rework this package along similar lines to the libdaos Go API. It's more work in the short term, but will yield long-term benefits.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I think the argument for going this PR's route is entirely as an intermediate step to increase testability in the short term. It is indeed technical debt that will need a thoughtful redesign in the future either way.

@knard38 knard38 May 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjmac thank you for pointing this out with a concrete alternative to follow.
My apologies to @tanabarr and @kjacque who had already pinpointed this issue — I had not fully
internalized how much the god-interface pattern goes against Go best practices. I have to admit
that at the time I did not see how to solve it without the god-interface pattern: still a Go
padawan, am I 😉

Following @mjmac's suggestion, I preferred fixing it now rather than carrying it as technical debt,
as in my experience it rarely gets easier to address later. I reworked the code to adopt the
build-tag CGo stub pattern used in src/control/lib/daos/api. Commit 7310312 replaces the
DdbAPI god interface with that approach, and PR #18086 shows the stubs being exercised in unit
tests.

Init(log *logging.LeveledLogger) (func(), error)
PoolIsOpen() bool
Ls(path string, recursive bool, details bool) error
Open(path string, dbPath string, writeMode bool) error
Version() error
Close() error
SuperblockDump() error
ValueDump(path string, dst string) error
Rm(path string) error
ValueLoad(src string, dst string) error
IlogDump(path string) error
IlogCommit(path string) error
IlogClear(path string) error
DtxDump(path string, active bool, committed bool) error
DtxCmtClear(path string) error
SmdSync(nvmeConf string, dbPath string) error
VeaDump() error
VeaUpdate(offset string, blkCnt string) error
DtxActCommit(path string, dtxID string) error
DtxActAbort(path string, dtxID string) error
Feature(path, dbPath, enable, disable string, show bool) error
RmPool(path string, dbPath string) error
DtxActDiscardInvalid(path string, dtxID string) error
DevList(dbPath string) error
DevReplace(dbPath string, oldDevid string, newDevid string) error
DtxStat(path string, details bool) error
ProvMem(dbPath string, tmpfsMount string, tmpfsMountSize uint) error
DtxAggr(path string, cmtTime uint64, cmtDate string) error
}

return func() {
C.free(unsafe.Pointer(cstr))
}
// DdbContext structure for wrapping the C code context structure
type DdbContext struct {
ctx C.struct_ddb_ctx
log *logging.LeveledLogger
}

// InitDdb initializes the ddb context and returns a closure to finalize it.
func InitDdb(log *logging.LeveledLogger) (*DdbContext, func(), error) {
// Init initializes the ddb context and returns a closure to finalize it.
func (ctx *DdbContext) Init(log *logging.LeveledLogger) (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()

if err := daosError(C.ddb_init()); err != nil {
runtime.UnlockOSThread()
return nil, nil, err
return nil, err
}

ctx := &DdbContext{}
C.ddb_ctx_init(&ctx.ctx) // Initialize with ctx default values
ctx.log = log

return ctx, func() {
return func() {
C.ddb_fini()
runtime.UnlockOSThread()
}, nil
}

// 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 {
func (ctx *DdbContext) PoolIsOpen() bool {
Comment thread
kjacque marked this conversation as resolved.
return bool(C.ddb_pool_is_open(&ctx.ctx))
}

func ddbLs(ctx *DdbContext, path string, recursive bool, details bool) error {
func (ctx *DdbContext) Ls(path string, recursive bool, details bool) error {
/* Set up the options */
options := C.struct_ls_options{}
options.path = C.CString(path)
Expand All @@ -87,33 +108,34 @@ func ddbLs(ctx *DdbContext, path string, recursive bool, details bool) error {
return daosError(C.ddb_run_ls(&ctx.ctx, &options))
}

func ddbOpen(ctx *DdbContext, path string, write_mode bool) error {
func (ctx *DdbContext) Open(path string, dbPath string, writeMode bool) error {
/* Set up the options */
options := C.struct_open_options{}
options.path = C.CString(path)
defer freeString(options.path)
options.db_path = ctx.ctx.dc_db_path
options.write_mode = C.bool(write_mode)
options.db_path = C.CString(dbPath)
defer freeString(options.db_path)
options.write_mode = C.bool(writeMode)
/* Run the c code command */
return daosError(C.ddb_run_open(&ctx.ctx, &options))
}

func ddbVersion(ctx *DdbContext) error {
func (ctx *DdbContext) Version() error {
/* Run the c code command */
return daosError(C.ddb_run_version(&ctx.ctx))
}

func ddbClose(ctx *DdbContext) error {
func (ctx *DdbContext) Close() error {
/* Run the c code command */
return daosError(C.ddb_run_close(&ctx.ctx))
}

func ddbSuperblockDump(ctx *DdbContext) error {
func (ctx *DdbContext) SuperblockDump() error {
/* Run the c code command */
return daosError(C.ddb_run_superblock_dump(&ctx.ctx))
}

func ddbValueDump(ctx *DdbContext, path string, dst string) error {
func (ctx *DdbContext) ValueDump(path string, dst string) error {
/* Set up the options */
options := C.struct_value_dump_options{}
options.path = C.CString(path)
Expand All @@ -124,7 +146,7 @@ func ddbValueDump(ctx *DdbContext, path string, dst string) error {
return daosError(C.ddb_run_value_dump(&ctx.ctx, &options))
}

func ddbRm(ctx *DdbContext, path string) error {
func (ctx *DdbContext) Rm(path string) error {
/* Set up the options */
options := C.struct_rm_options{}
options.path = C.CString(path)
Expand All @@ -133,7 +155,7 @@ func ddbRm(ctx *DdbContext, path string) error {
return daosError(C.ddb_run_rm(&ctx.ctx, &options))
}

func ddbValueLoad(ctx *DdbContext, src string, dst string) error {
func (ctx *DdbContext) ValueLoad(src string, dst string) error {
/* Set up the options */
options := C.struct_value_load_options{}
options.src = C.CString(src)
Expand All @@ -144,7 +166,7 @@ func ddbValueLoad(ctx *DdbContext, src string, dst string) error {
return daosError(C.ddb_run_value_load(&ctx.ctx, &options))
}

func ddbIlogDump(ctx *DdbContext, path string) error {
func (ctx *DdbContext) IlogDump(path string) error {
/* Set up the options */
options := C.struct_ilog_dump_options{}
options.path = C.CString(path)
Expand All @@ -153,7 +175,7 @@ func ddbIlogDump(ctx *DdbContext, path string) error {
return daosError(C.ddb_run_ilog_dump(&ctx.ctx, &options))
}

func ddbIlogCommit(ctx *DdbContext, path string) error {
func (ctx *DdbContext) IlogCommit(path string) error {
/* Set up the options */
options := C.struct_ilog_commit_options{}
options.path = C.CString(path)
Expand All @@ -162,7 +184,7 @@ func ddbIlogCommit(ctx *DdbContext, path string) error {
return daosError(C.ddb_run_ilog_commit(&ctx.ctx, &options))
}

func ddbIlogClear(ctx *DdbContext, path string) error {
func (ctx *DdbContext) IlogClear(path string) error {
/* Set up the options */
options := C.struct_ilog_clear_options{}
options.path = C.CString(path)
Expand All @@ -171,7 +193,7 @@ func ddbIlogClear(ctx *DdbContext, path string) error {
return daosError(C.ddb_run_ilog_clear(&ctx.ctx, &options))
}

func ddbDtxDump(ctx *DdbContext, path string, active bool, committed bool) error {
func (ctx *DdbContext) DtxDump(path string, active bool, committed bool) error {
/* Set up the options */
options := C.struct_dtx_dump_options{}
options.path = C.CString(path)
Expand All @@ -182,7 +204,7 @@ func ddbDtxDump(ctx *DdbContext, path string, active bool, committed bool) error
return daosError(C.ddb_run_dtx_dump(&ctx.ctx, &options))
}

func ddbDtxCmtClear(ctx *DdbContext, path string) error {
func (ctx *DdbContext) DtxCmtClear(path string) error {
/* Set up the options */
options := C.struct_dtx_cmt_clear_options{}
options.path = C.CString(path)
Expand All @@ -191,61 +213,62 @@ func ddbDtxCmtClear(ctx *DdbContext, path string) error {
return daosError(C.ddb_run_dtx_cmt_clear(&ctx.ctx, &options))
}

func ddbSmdSync(ctx *DdbContext, nvme_conf string, db_path string) error {
func (ctx *DdbContext) SmdSync(nvmeConf string, dbPath string) error {
/* Set up the options */
options := C.struct_smd_sync_options{}
options.nvme_conf = C.CString(nvme_conf)
options.nvme_conf = C.CString(nvmeConf)
defer freeString(options.nvme_conf)
options.db_path = C.CString(db_path)
options.db_path = C.CString(dbPath)
defer freeString(options.db_path)
/* Run the c code command */
return daosError(C.ddb_run_smd_sync(&ctx.ctx, &options))
}

func ddbVeaDump(ctx *DdbContext) error {
func (ctx *DdbContext) VeaDump() error {
/* Run the c code command */
return daosError(C.ddb_run_vea_dump(&ctx.ctx))
}

func ddbVeaUpdate(ctx *DdbContext, offset string, blk_cnt string) error {
func (ctx *DdbContext) VeaUpdate(offset string, blkCnt string) error {
/* Set up the options */
options := C.struct_vea_update_options{}
options.offset = C.CString(offset)
defer freeString(options.offset)
options.blk_cnt = C.CString(blk_cnt)
options.blk_cnt = C.CString(blkCnt)
defer freeString(options.blk_cnt)
/* Run the c code command */
return daosError(C.ddb_run_vea_update(&ctx.ctx, &options))
}

func ddbDtxActCommit(ctx *DdbContext, path string, dtx_id string) error {
func (ctx *DdbContext) DtxActCommit(path string, dtxID string) error {
/* Set up the options */
options := C.struct_dtx_act_options{}
options.path = C.CString(path)
defer freeString(options.path)
options.dtx_id = C.CString(dtx_id)
options.dtx_id = C.CString(dtxID)
defer freeString(options.dtx_id)
/* Run the c code command */
return daosError(C.ddb_run_dtx_act_commit(&ctx.ctx, &options))
}

func ddbDtxActAbort(ctx *DdbContext, path string, dtx_id string) error {
func (ctx *DdbContext) DtxActAbort(path string, dtxID string) error {
/* Set up the options */
options := C.struct_dtx_act_options{}
options.path = C.CString(path)
defer freeString(options.path)
options.dtx_id = C.CString(dtx_id)
options.dtx_id = C.CString(dtxID)
defer freeString(options.dtx_id)
/* Run the c code command */
return daosError(C.ddb_run_dtx_act_abort(&ctx.ctx, &options))
}

func ddbFeature(ctx *DdbContext, path, enable, disable string, show bool) error {
func (ctx *DdbContext) Feature(path, dbPath, enable, disable string, show bool) error {
/* Set up the options */
options := C.struct_feature_options{}
options.path = C.CString(path)
defer freeString(options.path)
options.db_path = ctx.ctx.dc_db_path
options.db_path = C.CString(dbPath)
defer freeString(options.db_path)
if enable != "" {
err := daosError(C.ddb_feature_string2flags(&ctx.ctx, C.CString(enable),
&options.set_compat_flags, &options.set_incompat_flags))
Expand All @@ -265,50 +288,51 @@ func ddbFeature(ctx *DdbContext, path, enable, disable string, show bool) error
return daosError(C.ddb_run_feature(&ctx.ctx, &options))
}

func ddbRmPool(ctx *DdbContext, path string) error {
func (ctx *DdbContext) RmPool(path string, dbPath string) error {
/* Set up the options */
options := C.struct_rm_pool_options{}
options.path = C.CString(path)
defer freeString(options.path)
options.db_path = ctx.ctx.dc_db_path
options.db_path = C.CString(dbPath)
defer freeString(options.db_path)
/* Run the c code command */
return daosError(C.ddb_run_rm_pool(&ctx.ctx, &options))
}

func ddbDtxActDiscardInvalid(ctx *DdbContext, path string, dtx_id string) error {
func (ctx *DdbContext) DtxActDiscardInvalid(path string, dtxID string) error {
/* Set up the options */
options := C.struct_dtx_act_options{}
options.path = C.CString(path)
defer freeString(options.path)
options.dtx_id = C.CString(dtx_id)
options.dtx_id = C.CString(dtxID)
defer freeString(options.dtx_id)
/* Run the c code command */
return daosError(C.ddb_run_dtx_act_discard_invalid(&ctx.ctx, &options))
}

func ddbDevList(ctx *DdbContext, db_path string) error {
func (ctx *DdbContext) DevList(dbPath string) error {
/* Set up the options */
options := C.struct_dev_list_options{}
options.db_path = C.CString(db_path)
options.db_path = C.CString(dbPath)
defer freeString(options.db_path)
/* Run the c code command */
return daosError(C.ddb_run_dev_list(&ctx.ctx, &options))
}

func ddbDevReplace(ctx *DdbContext, db_path string, old_devid string, new_devid string) error {
func (ctx *DdbContext) DevReplace(dbPath string, oldDevID string, newDevID string) error {
/* Set up the options */
options := C.struct_dev_replace_options{}
options.db_path = C.CString(db_path)
options.db_path = C.CString(dbPath)
defer freeString(options.db_path)
options.old_devid = C.CString(old_devid)
options.old_devid = C.CString(oldDevID)
defer freeString(options.old_devid)
options.new_devid = C.CString(new_devid)
options.new_devid = C.CString(newDevID)
defer freeString(options.new_devid)
/* Run the c code command */
return daosError(C.ddb_run_dev_replace(&ctx.ctx, &options))
}

func ddbDtxStat(ctx *DdbContext, path string, details bool) error {
func (ctx *DdbContext) DtxStat(path string, details bool) error {
/* Set up the options */
options := C.struct_dtx_stat_options{}
options.path = C.CString(path)
Expand All @@ -318,25 +342,25 @@ func ddbDtxStat(ctx *DdbContext, path string, details bool) error {
return daosError(C.ddb_run_dtx_stat(&ctx.ctx, &options))
}

func ddbProvMem(ctx *DdbContext, db_path string, tmpfs_mount string, tmpfs_mount_size uint) error {
func (ctx *DdbContext) ProvMem(dbPath string, tmpfsMount string, tmpfsMountSize uint) error {
/* Set up the options */
options := C.struct_prov_mem_options{}
options.db_path = C.CString(db_path)
options.db_path = C.CString(dbPath)
defer freeString(options.db_path)
options.tmpfs_mount = C.CString(tmpfs_mount)
options.tmpfs_mount = C.CString(tmpfsMount)
defer freeString(options.tmpfs_mount)

options.tmpfs_mount_size = C.uint(tmpfs_mount_size)
options.tmpfs_mount_size = C.uint(tmpfsMountSize)
/* Run the c code command */
return daosError(C.ddb_run_prov_mem(&ctx.ctx, &options))
}

func ddbDtxAggr(ctx *DdbContext, path string, cmt_time uint64, cmt_date string) error {
if cmt_time != math.MaxUint64 && cmt_date != "" {
func (ctx *DdbContext) DtxAggr(path string, cmtTime uint64, cmtDate string) error {
if cmtTime != math.MaxUint64 && cmtDate != "" {
ctx.log.Error("'--cmt_time' and '--cmt_date' options are mutually exclusive")
return daosError(-C.DER_INVAL)
}
if cmt_time == math.MaxUint64 && cmt_date == "" {
if cmtTime == math.MaxUint64 && cmtDate == "" {
ctx.log.Error("'--cmt_time' or '--cmt_date' option has to be defined")
return daosError(-C.DER_INVAL)
}
Expand All @@ -345,13 +369,13 @@ func ddbDtxAggr(ctx *DdbContext, path string, cmt_time uint64, cmt_date string)
options := C.struct_dtx_aggr_options{}
options.path = C.CString(path)
defer freeString(options.path)
if cmt_time != math.MaxUint64 {
if cmtTime != math.MaxUint64 {
options.format = C.DDB_DTX_AGGR_CMT_TIME
options.cmt_time = C.uint64_t(cmt_time)
options.cmt_time = C.uint64_t(cmtTime)
}
if cmt_date != "" {
if cmtDate != "" {
options.format = C.DDB_DTX_AGGR_CMT_DATE
options.cmt_date = C.CString(cmt_date)
options.cmt_date = C.CString(cmtDate)
defer freeString(options.cmt_date)
}
/* Run the c code command */
Expand Down
Loading
Loading