diff --git a/cmd/frostfs-node/config/tree/config.go b/cmd/frostfs-node/config/tree/config.go index 632785873f..7007bedf18 100644 --- a/cmd/frostfs-node/config/tree/config.go +++ b/cmd/frostfs-node/config/tree/config.go @@ -63,3 +63,11 @@ func (c TreeConfig) ReplicationChannelCapacity() int { func (c TreeConfig) ReplicationWorkerCount() int { return int(config.IntSafe(c.cfg, "replication_worker_count")) } + +// SyncInterval returns the value of "sync_interval" +// config parameter from the "tree" section. +// +// Returns 0 if config value is not specified. +func (c TreeConfig) SyncInterval() time.Duration { + return config.DurationSafe(c.cfg, "sync_interval") +} diff --git a/cmd/frostfs-node/config/tree/config_test.go b/cmd/frostfs-node/config/tree/config_test.go index 4270213c47..670158438c 100644 --- a/cmd/frostfs-node/config/tree/config_test.go +++ b/cmd/frostfs-node/config/tree/config_test.go @@ -33,6 +33,7 @@ func TestTreeSection(t *testing.T) { require.Equal(t, 32, treeSec.ReplicationChannelCapacity()) require.Equal(t, 32, treeSec.ReplicationWorkerCount()) require.Equal(t, 5*time.Second, treeSec.ReplicationTimeout()) + require.Equal(t, time.Hour, treeSec.SyncInterval()) } configtest.ForEachFileType(path, fileConfigTest) diff --git a/cmd/frostfs-node/tree.go b/cmd/frostfs-node/tree.go index fce00fd3af..a271fc7017 100644 --- a/cmd/frostfs-node/tree.go +++ b/cmd/frostfs-node/tree.go @@ -3,6 +3,7 @@ package main import ( "context" "errors" + "time" treeconfig "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/tree" "github.com/TrueCloudLab/frostfs-node/pkg/core/container" @@ -63,12 +64,29 @@ func initTreeService(c *cfg) { c.treeService.Start(ctx) })) - addNewEpochNotificationHandler(c, func(_ event.Event) { - err := c.treeService.SynchronizeAll() - if err != nil { - c.log.Error("could not synchronize Tree Service", zap.Error(err)) - } - }) + if d := treeConfig.SyncInterval(); d == 0 { + addNewEpochNotificationHandler(c, func(_ event.Event) { + err := c.treeService.SynchronizeAll() + if err != nil { + c.log.Error("could not synchronize Tree Service", zap.Error(err)) + } + }) + } else { + go func() { + tick := time.NewTicker(d) + defer tick.Stop() + + for range tick.C { + err := c.treeService.SynchronizeAll() + if err != nil { + c.log.Error("could not synchronize Tree Service", zap.Error(err)) + if errors.Is(err, tree.ErrShuttingDown) { + return + } + } + } + }() + } subscribeToContainerRemoval(c, func(e event.Event) { ev := e.(containerEvent.DeleteSuccess) diff --git a/config/example/node.env b/config/example/node.env index 98186ef9e2..dc58d3d63b 100644 --- a/config/example/node.env +++ b/config/example/node.env @@ -35,6 +35,7 @@ NEOFS_TREE_CACHE_SIZE=15 NEOFS_TREE_REPLICATION_CHANNEL_CAPACITY=32 NEOFS_TREE_REPLICATION_WORKER_COUNT=32 NEOFS_TREE_REPLICATION_TIMEOUT=5s +NEOFS_TREE_SYNC_INTERVAL=1h # gRPC section ## 0 server diff --git a/config/example/node.json b/config/example/node.json index e0fcc8eaf6..bd85e34bf8 100644 --- a/config/example/node.json +++ b/config/example/node.json @@ -80,7 +80,8 @@ "cache_size": 15, "replication_channel_capacity": 32, "replication_worker_count": 32, - "replication_timeout": "5s" + "replication_timeout": "5s", + "sync_interval": "1h" }, "control": { "authorized_keys": [ diff --git a/config/example/node.yaml b/config/example/node.yaml index 89f24144d8..55f8bd4ffa 100644 --- a/config/example/node.yaml +++ b/config/example/node.yaml @@ -65,6 +65,7 @@ tree: replication_worker_count: 32 replication_channel_capacity: 32 replication_timeout: 5s + sync_interval: 1h control: authorized_keys: # list of hex-encoded public keys that have rights to use the Control Service