Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combined writing for FSTree #2814

Merged
merged 12 commits into from
Aug 28, 2024
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog for NeoFS Node
## [Unreleased]

### Added
- More effective FSTree writer for HDDs, new configuration options for it (#2814)

### Fixed

Expand Down
14 changes: 10 additions & 4 deletions cmd/neofs-lens/internal/storage/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
engineconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine"
shardconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard"
fstreeconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard/blobstor/fstree"
peapodconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard/blobstor/peapod"
"github.com/nspcc-dev/neofs-node/cmd/neofs-node/storage"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/fstree"
Expand Down Expand Up @@ -119,15 +118,18 @@
sCfg.Typ = storagesCfg[i].Type()
sCfg.Path = storagesCfg[i].Path()
sCfg.Perm = storagesCfg[i].Perm()
sCfg.FlushInterval = storagesCfg[i].FlushInterval()

Check warning on line 121 in cmd/neofs-lens/internal/storage/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/root.go#L121

Added line #L121 was not covered by tests

switch storagesCfg[i].Type() {
case fstree.Type:
sub := fstreeconfig.From((*config.Config)(storagesCfg[i]))
sCfg.Depth = sub.Depth()
sCfg.NoSync = sub.NoSync()
sCfg.CombinedCountLimit = sub.CombinedCountLimit()
sCfg.CombinedSizeLimit = sub.CombinedSizeLimit()
sCfg.CombinedSizeThreshold = sub.CombinedSizeThreshold()

Check warning on line 130 in cmd/neofs-lens/internal/storage/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/root.go#L128-L130

Added lines #L128 - L130 were not covered by tests
case peapod.Type:
peapodCfg := peapodconfig.From((*config.Config)(storagesCfg[i]))
sCfg.FlushInterval = peapodCfg.FlushInterval()
// Nothing peapod-specific, but it should work.
default:
return fmt.Errorf("can't initiate storage. invalid storage type: %s", storagesCfg[i].Type())
}
Expand Down Expand Up @@ -193,7 +195,11 @@
fstree.WithPath(sRead.Path),
fstree.WithPerm(sRead.Perm),
fstree.WithDepth(sRead.Depth),
fstree.WithNoSync(sRead.NoSync)),
fstree.WithNoSync(sRead.NoSync),
fstree.WithCombinedCountLimit(sRead.CombinedCountLimit),
fstree.WithCombinedSizeLimit(sRead.CombinedSizeLimit),
fstree.WithCombinedSizeThreshold(sRead.CombinedSizeThreshold),
fstree.WithCombinedWriteInterval(sRead.FlushInterval)),

Check warning on line 202 in cmd/neofs-lens/internal/storage/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/root.go#L198-L202

Added lines #L198 - L202 were not covered by tests
Policy: func(_ *objectSDK.Object, data []byte) bool {
return true
},
Expand Down
4 changes: 1 addition & 3 deletions cmd/neofs-lens/internal/storage/sanity.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
engineconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine"
shardconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard"
fstreeconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard/blobstor/fstree"
peapodconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard/blobstor/peapod"
objectcore "github.com/nspcc-dev/neofs-node/pkg/core/object"
commonb "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/common"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/compression"
Expand Down Expand Up @@ -77,8 +76,7 @@
default:
return fmt.Errorf("unsupported sub-storage type '%s'", subCfg.Type())
case peapod.Type:
peapodCfg := peapodconfig.From((*config.Config)(subCfg))
sh.p = peapod.New(subCfg.Path(), subCfg.Perm(), peapodCfg.FlushInterval())
sh.p = peapod.New(subCfg.Path(), subCfg.Perm(), subCfg.FlushInterval())

Check warning on line 79 in cmd/neofs-lens/internal/storage/sanity.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-lens/internal/storage/sanity.go#L79

Added line #L79 was not covered by tests

var compressCfg compression.Config
err := compressCfg.Init()
Expand Down
8 changes: 5 additions & 3 deletions cmd/neofs-node/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
engineconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine"
shardconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard"
fstreeconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard/blobstor/fstree"
peapodconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard/blobstor/peapod"
loggerconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/logger"
metricsconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/metrics"
morphconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/morph"
Expand Down Expand Up @@ -224,15 +223,18 @@
sCfg.Typ = storagesCfg[i].Type()
sCfg.Path = storagesCfg[i].Path()
sCfg.Perm = storagesCfg[i].Perm()
sCfg.FlushInterval = storagesCfg[i].FlushInterval()

Check warning on line 226 in cmd/neofs-node/config.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/config.go#L226

Added line #L226 was not covered by tests

switch storagesCfg[i].Type() {
case fstree.Type:
sub := fstreeconfig.From((*config.Config)(storagesCfg[i]))
sCfg.Depth = sub.Depth()
sCfg.NoSync = sub.NoSync()
sCfg.CombinedCountLimit = sub.CombinedCountLimit()
sCfg.CombinedSizeLimit = sub.CombinedSizeLimit()
sCfg.CombinedSizeThreshold = sub.CombinedSizeThreshold()

Check warning on line 235 in cmd/neofs-node/config.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/config.go#L233-L235

Added lines #L233 - L235 were not covered by tests
case peapod.Type:
peapodCfg := peapodconfig.From((*config.Config)(storagesCfg[i]))
sCfg.FlushInterval = peapodCfg.FlushInterval()
// No specific configs, but it's a valid storage type.
default:
return fmt.Errorf("invalid storage type: %s", storagesCfg[i].Type())
}
Expand Down
7 changes: 2 additions & 5 deletions cmd/neofs-node/config/engine/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
engineconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine"
shardconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard"
fstreeconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard/blobstor/fstree"
peapodconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard/blobstor/peapod"
piloramaconfig "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/engine/shard/pilorama"
configtest "github.com/nspcc-dev/neofs-node/cmd/neofs-node/config/test"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/peapod"
Expand Down Expand Up @@ -87,11 +86,10 @@ func TestEngineSection(t *testing.T) {
require.EqualValues(t, 102400, sc.SmallSizeLimit())

require.Equal(t, 2, len(ss))
ppd := peapodconfig.From((*config.Config)(ss[0]))
require.Equal(t, "tmp/0/blob/peapod.db", ss[0].Path())
require.EqualValues(t, 0644, ss[0].Perm())
require.EqualValues(t, peapod.Type, ss[0].Type())
require.EqualValues(t, 10*time.Millisecond, ppd.FlushInterval())
require.EqualValues(t, 10*time.Millisecond, ss[0].FlushInterval())

require.Equal(t, "tmp/0/blob", ss[1].Path())
require.EqualValues(t, 0644, ss[1].Perm())
Expand Down Expand Up @@ -131,11 +129,10 @@ func TestEngineSection(t *testing.T) {
require.EqualValues(t, 102400, sc.SmallSizeLimit())

require.Equal(t, 2, len(ss))
ppd := peapodconfig.From((*config.Config)(ss[0]))
require.Equal(t, "tmp/1/blob/peapod.db", ss[0].Path())
require.EqualValues(t, 0644, ss[0].Perm())
require.EqualValues(t, peapod.Type, ss[0].Type())
require.EqualValues(t, 30*time.Millisecond, ppd.FlushInterval())
require.EqualValues(t, 30*time.Millisecond, ss[0].FlushInterval())

require.Equal(t, "tmp/1/blob", ss[1].Path())
require.EqualValues(t, 0644, ss[1].Perm())
Expand Down
53 changes: 51 additions & 2 deletions cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
package fstree

import (
"math"

"github.com/nspcc-dev/neofs-node/cmd/neofs-node/config"
"github.com/nspcc-dev/neofs-node/pkg/local_object_storage/blobstor/fstree"
"github.com/spf13/cast"
)

// Config is a wrapper over the config section
// which provides access to FSTree configurations.
type Config config.Config

// DepthDefault is a default shallow dir depth.
const DepthDefault = 4
const (
// DepthDefault is the default shallow dir depth.
DepthDefault = 4
// CombinedCountLimitDefault is the default for the maximum number of objects to write into a single file.
CombinedCountLimitDefault = 128
// CombinedSizeLimitDefault is the default for the maximum size of the combined object file.
CombinedSizeLimitDefault = 8 * 1024 * 1024
// CombinedSizeThresholdDefault is the default for the minimal size of the object that won't be combined with others for writes.
CombinedSizeThresholdDefault = 128 * 1024
)

// From wraps config section into Config.
func From(c *config.Config) *Config {
Expand Down Expand Up @@ -45,3 +56,41 @@
func (x *Config) NoSync() bool {
return config.BoolSafe((*config.Config)(x), "no_sync")
}

// CombinedCountLimit returns the value of "combined_count_limit" config parameter.
//
// Returns [CombinedCountLimitDefault] if the value is missing or not a positive integer.
func (x *Config) CombinedCountLimit() int {
var v = (*config.Config)(x).Value("combined_count_limit")
if v == nil {
return CombinedCountLimitDefault

Check warning on line 66 in cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go#L63-L66

Added lines #L63 - L66 were not covered by tests
}

i, err := cast.ToIntE(v)
if err != nil {
return CombinedCountLimitDefault

Check warning on line 71 in cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go#L69-L71

Added lines #L69 - L71 were not covered by tests
}
return i

Check warning on line 73 in cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go#L73

Added line #L73 was not covered by tests
}

// CombinedSizeLimit returns the value of "combined_size_limit" config parameter.
//
// Returns [CombinedSizeLimitDefault] if the value is missing, equal to 0 or not a proper size specification.
func (x *Config) CombinedSizeLimit() int {
var s = config.SizeInBytesSafe((*config.Config)(x), "combined_size_limit")
if s == 0 || s > math.MaxInt {
return CombinedSizeLimitDefault

Check warning on line 82 in cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go#L79-L82

Added lines #L79 - L82 were not covered by tests
}
return int(s)

Check warning on line 84 in cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go#L84

Added line #L84 was not covered by tests
}

// CombinedSizeThreshold returns the value of "combined_size_threshold" config parameter.
//
// Returns [CombinedSizeThresholdDefault] if the value is missing, equal to 0 or not a proper size specification.
func (x *Config) CombinedSizeThreshold() int {
var s = config.SizeInBytesSafe((*config.Config)(x), "combined_size_threshold")
if s == 0 || s > math.MaxInt {
return CombinedSizeThresholdDefault

Check warning on line 93 in cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go#L90-L93

Added lines #L90 - L93 were not covered by tests
}
return int(s)

Check warning on line 95 in cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/config/engine/shard/blobstor/fstree/config.go#L95

Added line #L95 was not covered by tests
}
34 changes: 0 additions & 34 deletions cmd/neofs-node/config/engine/shard/blobstor/peapod/config.go

This file was deleted.

23 changes: 21 additions & 2 deletions cmd/neofs-node/config/engine/shard/blobstor/storage/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@ package storage

import (
"io/fs"
"time"

"github.com/nspcc-dev/neofs-node/cmd/neofs-node/config"
)

type Config config.Config

// PermDefault are default permission bits for BlobStor data.
const PermDefault = 0o640
// Various config defaults.
const (
// PermDefault are default permission bits for BlobStor data.
PermDefault = 0o640

// DefaultFlushInterval is the default time interval between Peapod's batch writes
// to disk.
DefaultFlushInterval = 10 * time.Millisecond
)

func From(x *config.Config) *Config {
return (*Config)(x)
Expand Down Expand Up @@ -53,3 +61,14 @@ func (x *Config) Perm() fs.FileMode {

return fs.FileMode(p)
}

// FlushInterval returns the value of "flush_interval" config parameter.
//
// Returns DefaultFlushInterval if the value is not a positive duration.
func (x *Config) FlushInterval() time.Duration {
d := config.DurationSafe((*config.Config)(x), "flush_interval")
if d > 0 {
return d
}
return DefaultFlushInterval
}
6 changes: 5 additions & 1 deletion cmd/neofs-node/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,11 @@
fstree.WithPath(sRead.Path),
fstree.WithPerm(sRead.Perm),
fstree.WithDepth(sRead.Depth),
fstree.WithNoSync(sRead.NoSync)),
fstree.WithNoSync(sRead.NoSync),
fstree.WithCombinedCountLimit(sRead.CombinedCountLimit),
fstree.WithCombinedSizeLimit(sRead.CombinedSizeLimit),
fstree.WithCombinedSizeThreshold(sRead.CombinedSizeThreshold),
fstree.WithCombinedWriteInterval(sRead.FlushInterval)),

Check warning on line 160 in cmd/neofs-node/storage.go

View check run for this annotation

Codecov / codecov/patch

cmd/neofs-node/storage.go#L156-L160

Added lines #L156 - L160 were not covered by tests
Policy: func(_ *objectSDK.Object, data []byte) bool {
return true
},
Expand Down
17 changes: 9 additions & 8 deletions cmd/neofs-node/storage/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,17 @@ type ShardCfg struct {
}
type SubStorageCfg struct {
// common for all storages
Typ string
Path string
Perm fs.FileMode
Typ string
Path string
Perm fs.FileMode
FlushInterval time.Duration

// tree-specific (FS)
Depth uint64
NoSync bool

// Peapod-specific
FlushInterval time.Duration
Depth uint64
NoSync bool
CombinedCountLimit int
CombinedSizeLimit int
CombinedSizeThreshold int
}

// ID returns persistent id of a shard. It is different from the ID used in runtime
Expand Down
6 changes: 5 additions & 1 deletion config/example/node.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,11 @@
"path": "tmp/1/blob",
"no_sync": true,
"perm": "0644",
"depth": 5
"depth": 5,
"flush_interval": "20ms",
"combined_count_limit": 64,
"combined_size_limit": "16M",
"combined_size_threshold": "512K"
}
],
"pilorama": {
Expand Down
4 changes: 4 additions & 0 deletions config/example/node.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ storage:
- type: fstree
path: tmp/1/blob # blobstor path
no_sync: true
flush_interval: 20ms # time interval between combined file writes to disk (defaults to 10ms)
combined_count_limit: 64 # number of small objects to write into a single file (defaults to 128)
combined_size_limit: 16M # limit for the multi-object file size (defaults to 8M)
combined_size_threshold: 512K # threshold for combined object writing (defaults to 128K)

pilorama:
path: tmp/1/blob/pilorama.db
Expand Down
21 changes: 10 additions & 11 deletions docs/storage-node-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,36 +180,35 @@ Currently only 2 types are supported: `fstree` and `peapod`.
blobstor:
- type: peapod
path: /path/to/peapod.db
depth: 1
width: 4
- type: fstree
path: /path/to/blobstor
perm: 0644
size: 4194304
depth: 1
width: 4
opened_cache_capacity: 50
```

#### Common options for sub-storages
| Parameter | Type | Default value | Description |
|-------------------------------------|-----------------------------------------------|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `path` | `string` | | Path to the root of the blobstor. |
| `perm` | file mode | `0640` | Default permission for created files and directories. |
| `flush_interval` | `duration` | `10ms` | Time interval between batch writes to disk. |

#### `fstree` type options
| Parameter | Type | Default value | Description |
|---------------------|-----------|---------------|-------------------------------------------------------|
| `path` | `string` | | Path to the root of the blobstor. |
| `perm` | file mode | `0640` | Default permission for created files and directories. |
| `depth` | `int` | `4` | File-system tree depth. |
| Parameter | Type | Default value | Description |
|---------------------------|-----------|---------------|------------------------------------------------------------------------------------------------------------------------------|
| `path` | `string` | | Path to the root of the blobstor. |
| `perm` | file mode | `0640` | Default permission for created files and directories. |
| `depth` | `int` | `4` | File-system tree depth. |
| `no_sync` | `bool` | `false` | Disable write synchronization, makes writes faster, but can lead to data loss. |
| `combined_count_limit` | `int` | `128` | Maximum number of objects to write into a single file, 0 or 1 disables combined writing (disabling is recommended for SSDs). |
| `combined_size_limit` | `size` | `8M` | Maximum size of a multi-object file. |
| `combined_size_threshold` | `size` | `128K` | Minimum size of object that won't be combined with others when writing to disk. |

#### `peapod` type options
| Parameter | Type | Default value | Description |
|---------------------|-----------|---------------|-------------------------------------------------------|
| `path` | `string` | | Path to the Peapod database file. |
| `perm` | file mode | `0640` | Default permission for created files and directories. |
| `flush_interval` | `duration`| `10ms` | Time interval between batch writes to disk. |

### `gc` subsection

Expand Down
Loading
Loading