Skip to content

Commit 51a1b38

Browse files
committed
feat: native bucket inventory (extend metadata; update cli; refactor)
* extend inventory meta - add ntotal, smap version, chunks, (active) targets - add apc.NBIMeta to further embed (xattr, info) and assign * fs: refactor fs/nbi * cli: show object count in default view; chunks, targets, smap in verbose ---- * part thirty-two, prev. commit: 846ee81 Signed-off-by: Alex Aizman <alex.aizman@gmail.com>
1 parent 626ea31 commit 51a1b38

8 files changed

Lines changed: 108 additions & 57 deletions

File tree

ais/kalive.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ type (
7575

7676
hbTracker interface {
7777
HeardFrom(id string, now int64) int64 // callback for 'id' to respond
78-
TimedOut(id string) bool // true if 'id` didn't keepalive or called (via "heard") within the interval (above)
78+
TimedOut(id string) bool // true if 'id' didn't keepalive or called (via "heard") within the interval (above)
7979

8080
reg(id string)
8181
set(interval time.Duration) bool

api/apc/nbi.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,21 @@ type (
2828
Force bool `json:"force,omitempty"`
2929
}
3030

31+
NBIMeta struct {
32+
Prefix string `json:"prefix,omitempty"` // lsmsg.Prefix
33+
Started int64 `json:"started,omitempty"` // time started creating (ns)
34+
Finished int64 `json:"finished,omitempty"` // finished (ns)
35+
Ntotal int64 `json:"ntotal,omitempty"` // total number of names in the inventory
36+
SmapVer int64 `json:"smap_ver,omitempty"` // cluster map when writing inventory
37+
Chunks int32 `json:"chunks,omitempty"` // number of chunks (manifest.Count())
38+
Nat int32 `json:"nat,omitempty"` // number of active (not in maintenance) targets
39+
}
3140
NBIInfo struct {
32-
Bucket string `json:"bucket"`
33-
Name string `json:"name"`
34-
ObjName string `json:"obj_name"`
35-
Prefix string `json:"prefix,omitempty"`
36-
Size int64 `json:"size"`
37-
Started int64 `json:"started,omitempty"`
38-
Finished int64 `json:"finished,omitempty"`
41+
Bucket string `json:"bucket"`
42+
Name string `json:"name"`
43+
ObjName string `json:"obj_name"`
44+
Size int64 `json:"size"` // inventory size on disk
45+
NBIMeta
3946
}
4047
NBIInfoMap map[string]*NBIInfo // by NBIInfo.Name
4148
)

cmd/cli/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/NVIDIA/aistore/cmd/cli
33
go 1.25
44

55
require (
6-
github.com/NVIDIA/aistore v1.4.3-0.20260317162519-ffefd96db92f
6+
github.com/NVIDIA/aistore v1.4.3-0.20260318163955-808673a27c3b
77
github.com/fatih/color v1.18.0
88
github.com/json-iterator/go v1.1.12
99
github.com/onsi/ginkgo/v2 v2.27.5

cmd/cli/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
22
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
33
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
4-
github.com/NVIDIA/aistore v1.4.3-0.20260317162519-ffefd96db92f h1:SAH+HEj5m/2Dw/HYZ2uYvh+8jHqURDmvJwkNa5fe7U0=
5-
github.com/NVIDIA/aistore v1.4.3-0.20260317162519-ffefd96db92f/go.mod h1:LWOYptrTZyGfxqIg6M7IIO+8UNmHBQZM68CbYVyPVM8=
4+
github.com/NVIDIA/aistore v1.4.3-0.20260318163955-808673a27c3b h1:2xR5PrUQ29IhHUiAV6Lkrr+Rja55OzolYkVUV3Yl8fo=
5+
github.com/NVIDIA/aistore v1.4.3-0.20260318163955-808673a27c3b/go.mod h1:LWOYptrTZyGfxqIg6M7IIO+8UNmHBQZM68CbYVyPVM8=
66
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
77
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
88
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=

cmd/cli/teb/templates.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,22 +374,27 @@ See '--help' and docs/cli for details.`
374374
// `search`
375375
SearchTmpl = "{{ JoinListNL . }}\n"
376376

377-
NBITmpl = "BUCKET\t NAME\t SIZE\t STARTED\t FINISHED\t PREFIX\n" +
377+
NBITmpl = "BUCKET\t NAME\t SIZE\t OBJECTS\t STARTED\t FINISHED\t PREFIX\n" +
378378
"{{range $v := .}}" +
379379
"{{$v.Bucket}}\t " +
380380
"{{$v.Name}}\t " +
381381
"{{FormatBytesSig $v.Size 2}}\t " +
382+
"{{if $v.Ntotal}}{{$v.Ntotal}}{{else}}-{{end}}\t " +
382383
"{{FormatUnixNano $v.Started}}\t " +
383384
"{{FormatUnixNano $v.Finished}}\t " +
384385
"{{if $v.Prefix}}{{$v.Prefix}}{{else}}-{{end}}\n" +
385386
"{{end}}"
386387

387-
NBITmplVerbose = "BUCKET\t NAME\t OBJECT\t SIZE\t STARTED\t FINISHED\t PREFIX\n" +
388+
NBITmplVerbose = "BUCKET\t NAME\t OBJECT\t SIZE\t OBJECTS\t CHUNKS\t TARGETS\t SMAP\t STARTED\t FINISHED\t PREFIX\n" +
388389
"{{range $v := .}}" +
389390
"{{$v.Bucket}}\t " +
390391
"{{$v.Name}}\t " +
391392
"{{$v.ObjName}}\t " +
392393
"{{FormatBytesSig $v.Size 2}}\t " +
394+
"{{if $v.Ntotal}}{{$v.Ntotal}}{{else}}-{{end}}\t " +
395+
"{{if $v.Chunks}}{{$v.Chunks}}{{else}}-{{end}}\t " +
396+
"{{if $v.Nat}}{{$v.Nat}}{{else}}-{{end}}\t " +
397+
"{{if $v.SmapVer}}v{{$v.SmapVer}}{{else}}-{{end}}\t " +
393398
"{{FormatUnixNano $v.Started}}\t " +
394399
"{{FormatUnixNano $v.Finished}}\t " +
395400
"{{if $v.Prefix}}{{$v.Prefix}}{{else}}-{{end}}\n" +

fs/nbi.go

Lines changed: 72 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,43 @@ import (
1717
"github.com/NVIDIA/aistore/core/meta"
1818
)
1919

20-
// (1) NBI xattr v1 (pre-manifest)
21-
// (2) NBI discovery
20+
// In this source:
21+
// (1) NBI xattr v1 (pre-manifest)
22+
// (2) NBI discovery
23+
24+
//
25+
// nbiXattr: native bucket inventory (NBI) on-disk metadata
26+
//
27+
// Versioned binary layout (big-endian via cos.BytePack):
28+
//
29+
// | -- FIXED-SIZE FIELDS ----------------------------------------------- | -- VAR -- |
30+
// | ver | started | finished | ntotal | smapVer | chunks | nat | prefix |
31+
// | u8 | int64 | int64 | int64 | int64 | int32 | int32 | string |
32+
//
33+
// where:
34+
// - ver : metadata version (nbiMetaVersion)
35+
// - started : creation start time (UnixNano)
36+
// - finished : creation completion time (UnixNano)
37+
// - ntotal : total number of object names in the inventory
38+
// - smapVer : cluster Smap version at creation time
39+
// - chunks : total number of inventory chunks (manifest.Count())
40+
// - nat : number of active targets at creation time
41+
// - prefix : lsmsg.Prefix used to generate the inventory
42+
//
43+
// Notes:
44+
// - `prefix` is variable-length stored last.
45+
// - All fixed-size fields must be appended only; do not reorder existing fields.
46+
// - Any post-4.3 format change must bump `nbiMetaVersion` and preserve backward compatibility.
47+
// - Consumers must validate `ver` meta-version before interpreting the payload.
48+
//
2249

2350
const (
2451
nbiMetaVersion = 1
2552
)
2653

2754
type nbiXattr struct {
28-
prefix string
29-
started int64
30-
finished int64
31-
version uint8
55+
apc.NBIMeta
56+
version uint8 // set internally; not exported
3257
}
3358

3459
type nbiJogger struct {
@@ -118,11 +143,11 @@ func (j *nbiJogger) run() {
118143
ObjName: filepath.Join(j.prefix, name),
119144
Size: fi.Size(),
120145
}
146+
121147
if ok {
122-
info.Started = meta.started
123-
info.Finished = meta.finished
124-
info.Prefix = meta.prefix
148+
info.NBIMeta = meta.NBIMeta
125149
} else {
150+
// TODO: indicate "likely in progress"
126151
info.Finished = _nbiMtime(fqn)
127152
}
128153

@@ -141,72 +166,80 @@ func _nbiMtime(fqn string) int64 {
141166
return t.UnixNano()
142167
}
143168

144-
//
145-
// nbiXattr: on-disk metadata format (versioned):
146-
// - version (1 byte)
147-
// - started (int64 unix-nano)
148-
// - finished (int64 unix-nano)
149-
// - prefix (length-prefixed string)
150-
//
151-
152169
// interface guard
153170
var (
154171
_ cos.Packer = (*nbiXattr)(nil)
155172
_ cos.Unpacker = (*nbiXattr)(nil)
156173
)
157174

158175
func (x *nbiXattr) PackedSize() int {
159-
return 1 + cos.SizeofI64 + cos.SizeofI64 + cos.PackedStrLen(x.prefix)
176+
return 1 + 4*cos.SizeofI64 + 2*cos.SizeofI32 + cos.PackedStrLen(x.Prefix)
160177
}
161178

162179
func (x *nbiXattr) Pack(packer *cos.BytePack) {
163180
packer.WriteUint8(x.version)
164-
packer.WriteInt64(x.started)
165-
packer.WriteInt64(x.finished)
166-
packer.WriteString(x.prefix)
181+
packer.WriteInt64(x.Started)
182+
packer.WriteInt64(x.Finished)
183+
packer.WriteInt64(x.Ntotal)
184+
packer.WriteInt64(x.SmapVer)
185+
packer.WriteInt32(x.Chunks)
186+
packer.WriteInt32(x.Nat)
187+
packer.WriteString(x.Prefix)
167188
}
168189

169190
func (x *nbiXattr) Unpack(unpacker *cos.ByteUnpack) (err error) {
170191
if x.version, err = unpacker.ReadByte(); err != nil {
171192
return
172193
}
173-
if x.started, err = unpacker.ReadInt64(); err != nil {
194+
195+
if x.Started, err = unpacker.ReadInt64(); err != nil {
196+
return
197+
}
198+
if x.Finished, err = unpacker.ReadInt64(); err != nil {
174199
return
175200
}
176-
if x.finished, err = unpacker.ReadInt64(); err != nil {
201+
if x.Ntotal, err = unpacker.ReadInt64(); err != nil {
177202
return
178203
}
179-
x.prefix, err = unpacker.ReadString()
204+
if x.SmapVer, err = unpacker.ReadInt64(); err != nil {
205+
return
206+
}
207+
if x.Chunks, err = unpacker.ReadInt32(); err != nil {
208+
return
209+
}
210+
if x.Nat, err = unpacker.ReadInt32(); err != nil {
211+
return
212+
}
213+
214+
x.Prefix, err = unpacker.ReadString()
180215
return
181216
}
182217

183-
func getNBI(fqn string) (meta nbiXattr, ok bool, err error) {
218+
func getNBI(fqn string) (x *nbiXattr, ok bool, err error) {
184219
b, err := GetXattr(fqn, xattrNBI)
185220
if err != nil {
186221
if cos.IsErrXattrNotFound(err) {
187-
return meta, false, nil
222+
return nil, false, nil
188223
}
189-
return meta, false, err
224+
return nil, false, err
190225
}
191226

227+
x = &nbiXattr{}
192228
u := cos.NewUnpacker(b)
193-
if err = meta.Unpack(u); err != nil {
194-
return meta, false, err
229+
if err = x.Unpack(u); err != nil {
230+
return nil, false, err
195231
}
196-
if meta.version != nbiMetaVersion {
197-
return meta, false, fmt.Errorf("unsupported NBI metadata version %d", meta.version)
232+
if x.version != nbiMetaVersion {
233+
return nil, false, fmt.Errorf("unsupported NBI metadata version %d", x.version)
198234
}
199-
return meta, true, nil
235+
return x, true, nil
200236
}
201237

202-
func SetNBI(fqn string, started, finished int64, prefix string, buf []byte) error {
203-
x := &nbiXattr{
204-
version: nbiMetaVersion,
205-
started: started,
206-
finished: finished,
207-
prefix: prefix,
238+
func SetNBI(fqn string, meta *apc.NBIMeta, buf []byte) error {
239+
x := nbiXattr{
240+
NBIMeta: *meta,
241+
version: nbiMetaVersion,
208242
}
209-
210243
packer := cos.NewPacker(buf, x.PackedSize())
211244
x.Pack(packer)
212245

xact/xs/create_nbi.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import (
3030

3131
// XactNBI: create native bucket inventory (NBI)
3232
//
33-
// TODO -- FIXME:
33+
// TODO:
3434
// - progress notif
3535
// - designated-target + filterAddLmeta()
3636
// - stats: internal (-> CtlMsg) and Prometheus
@@ -290,8 +290,16 @@ func (r *XactNBI) Run(wg *sync.WaitGroup) {
290290
return
291291
}
292292

293-
a, b := r.StartTime(), time.Now()
294-
if err := fs.SetNBI(r.lom.FQN, a.UnixNano(), b.UnixNano(), r.msg.Prefix, r.buf); err != nil {
293+
meta := &apc.NBIMeta{
294+
Prefix: r.msg.Prefix,
295+
Started: r.StartTime().UnixNano(),
296+
Finished: time.Now().UnixNano(),
297+
Ntotal: ntotal,
298+
SmapVer: smap.Version,
299+
Chunks: int32(r.ufest.Count()),
300+
Nat: int32(smap.CountActiveTs()),
301+
}
302+
if err := fs.SetNBI(r.lom.FQN, meta, r.buf); err != nil {
295303
nlog.Errorf("%s: ex-post-facto failure to store metadata: [%q, %q, %v]", r.Name(), r.msg.Name, r.lom.Cname(), err)
296304
core.T.FSHC(err, r.lom.Mountpath(), r.lom.FQN)
297305
}

xact/xs/ls_nbi.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,11 @@ const (
4242

4343
// TODO:
4444
// - corner case: very small bucket w/ not every target having inventory LOM
45-
// - test list-objects with empty invName (target must resolve single)
4645
// - feat: support LsCached (semantics? a) per recorded in chunks or b) w/ respect to local data)
4746
// - feat: support listing jobs that currently rely-on/reuse:
4847
// - npgCtx.nextPageR() (bucket summary)
4948
// - npgCtx.nextPageA() (all list-range jobs: prefetch, evict, transform-copy-objs, etc.)
5049
// - returned errors: unify; add details
51-
// - pagesize default set by proxy (10k when zero) - not optimal for NBI
5250

5351
type nbiCtx struct {
5452
lom *core.LOM // .sys_inventory/BUCKET-UNAME/INV-NAME

0 commit comments

Comments
 (0)