diff --git a/pkg/local_object_storage/blobstor/fstree/fstree.go b/pkg/local_object_storage/blobstor/fstree/fstree.go index bf6ba51e5..1c60ec340 100644 --- a/pkg/local_object_storage/blobstor/fstree/fstree.go +++ b/pkg/local_object_storage/blobstor/fstree/fstree.go @@ -222,6 +222,81 @@ func (t *FSTree) iterate(ctx context.Context, depth uint64, curPath []string, pr return nil } +type ObjectInfo struct { + Address oid.Address + DataSize uint64 +} +type IterateInfoHandler func(ObjectInfo) error + +func (t *FSTree) IterateInfo(ctx context.Context, handler IterateInfoHandler) error { + var ( + err error + startedAt = time.Now() + ) + defer func() { + t.metrics.IterateInfo(time.Since(startedAt), err == nil) + }() + _, span := tracing.StartSpanFromContext(ctx, "FSTree.IterateInfo") + defer span.End() + + return t.iterateInfo(ctx, 0, []string{t.RootPath}, handler) +} + +func (t *FSTree) iterateInfo(ctx context.Context, depth uint64, curPath []string, handler IterateInfoHandler) error { + curName := strings.Join(curPath[1:], "") + dirPath := filepath.Join(curPath...) + entries, err := os.ReadDir(dirPath) + if err != nil { + return fmt.Errorf("read fstree dir '%s': %w", dirPath, err) + } + + isLast := depth >= t.Depth + l := len(curPath) + curPath = append(curPath, "") + + for i := range entries { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + curPath[l] = entries[i].Name() + + if !isLast && entries[i].IsDir() { + err := t.iterateInfo(ctx, depth+1, curPath, handler) + if err != nil { + return err + } + } + + if depth != t.Depth { + continue + } + + addr, err := addressFromString(curName + entries[i].Name()) + if err != nil { + continue + } + info, err := entries[i].Info() + if err != nil { + if os.IsNotExist(err) { + continue + } + return err + } + + err = handler(ObjectInfo{ + Address: addr, + DataSize: uint64(info.Size()), + }) + if err != nil { + return err + } + } + + return nil +} + func (t *FSTree) treePath(addr oid.Address) string { sAddr := stringifyAddress(addr) diff --git a/pkg/local_object_storage/blobstor/fstree/metrics.go b/pkg/local_object_storage/blobstor/fstree/metrics.go index 10de935eb..4241beec9 100644 --- a/pkg/local_object_storage/blobstor/fstree/metrics.go +++ b/pkg/local_object_storage/blobstor/fstree/metrics.go @@ -13,6 +13,7 @@ type Metrics interface { Close() Iterate(d time.Duration, success bool) + IterateInfo(d time.Duration, success bool) Delete(d time.Duration, success bool) Exists(d time.Duration, success bool) Put(d time.Duration, size int, success bool) @@ -27,6 +28,7 @@ func (m *noopMetrics) SetParentID(string) {} func (m *noopMetrics) SetMode(mode.ComponentMode) {} func (m *noopMetrics) Close() {} func (m *noopMetrics) Iterate(time.Duration, bool) {} +func (m *noopMetrics) IterateInfo(time.Duration, bool) {} func (m *noopMetrics) Delete(time.Duration, bool) {} func (m *noopMetrics) Exists(time.Duration, bool) {} func (m *noopMetrics) Put(time.Duration, int, bool) {} diff --git a/pkg/local_object_storage/metrics/fstree.go b/pkg/local_object_storage/metrics/fstree.go index 76822ac2c..d93363fa3 100644 --- a/pkg/local_object_storage/metrics/fstree.go +++ b/pkg/local_object_storage/metrics/fstree.go @@ -38,6 +38,10 @@ func (m *fstreeMetrics) Iterate(d time.Duration, success bool) { m.m.MethodDuration(m.shardID, m.path, "Iterate", d, success) } +func (m *fstreeMetrics) IterateInfo(d time.Duration, success bool) { + m.m.MethodDuration(m.shardID, m.path, "IterateInfo", d, success) +} + func (m *fstreeMetrics) Delete(d time.Duration, success bool) { m.m.MethodDuration(m.shardID, m.path, "Delete", d, success) }