Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const (
flagStartS3Endpoint = "initial-s3-endpoint"
flagStartS3Region = "initial-s3-region"
flagStartS3ForcePathStyle = "initial-s3-force-path-style"

// Filenode configuration.
flagStartFilenodeDefaultLimit = "initial-filenode-default-limit"
)

var log = logger.NewNamed("cli")
Expand Down Expand Up @@ -164,6 +167,16 @@ func buildStartFlags() []cli.Flag {
EnvVars: []string{"ANY_SYNC_BUNDLE_INIT_S3_FORCE_PATH_STYLE"},
Usage: "Use path-style S3 URLs (required for MinIO)",
},

// Filenode Configuration
&cli.Uint64Flag{
Name: flagStartFilenodeDefaultLimit,
EnvVars: []string{"ANY_SYNC_BUNDLE_INIT_FILENODE_DEFAULT_LIMIT"},
Usage: "Storage limit per space in bytes. " +
"Examples: 1 GiB = 1073741824, 10 GiB = 10737418240, " +
"150 GiB = 161061273600, 1 TiB = 1099511627776 (default), " +
"2 TiB = 2199023255552",
},
}
}

Expand Down
3 changes: 3 additions & 0 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ func loadOrCreateConfig(cCtx *cli.Context, log logger.CtxLogger) *bundleConfig.C
S3Endpoint: cCtx.String(flagStartS3Endpoint),
S3Region: cCtx.String(flagStartS3Region),
S3ForcePathStyle: cCtx.Bool(flagStartS3ForcePathStyle),

// Filenode configuration
FilenodeDefaultLimit: cCtx.Uint64(flagStartFilenodeDefaultLimit),
})
}

Expand Down
7 changes: 7 additions & 0 deletions compose.aio.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,10 @@ services:
environment:
# Advertise addresses clients should use. Replace with your server hostname/IP.
ANY_SYNC_BUNDLE_INIT_EXTERNAL_ADDRS: "192.168.100.9"
# Storage limit per space in bytes (default: 1 TiB)
# Examples: 1 GiB = 1073741824
# 10 GiB = 10737418240
# 150 GiB = 161061273600
# 1 TiB = 1099511627776 (default)
# 2 TiB = 2199023255552
# ANY_SYNC_BUNDLE_INIT_FILENODE_DEFAULT_LIMIT: "1099511627776"
7 changes: 7 additions & 0 deletions compose.external.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,10 @@ services:
ANY_SYNC_BUNDLE_INIT_EXTERNAL_ADDRS: "192.168.100.9"
ANY_SYNC_BUNDLE_INIT_MONGO_URI: "mongodb://mongo:27017/?replicaSet=rs0"
ANY_SYNC_BUNDLE_INIT_REDIS_URI: "redis://redis:6379/"
# Storage limit per space in bytes (default: 1 TiB)
# Examples: 1 GiB = 1073741824
# 10 GiB = 10737418240
# 150 GiB = 161061273600
# 1 TiB = 1099511627776 (default)
# 2 TiB = 2199023255552
# ANY_SYNC_BUNDLE_INIT_FILENODE_DEFAULT_LIMIT: "1099511627776"
7 changes: 7 additions & 0 deletions compose.s3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,10 @@ services:
# S3 Credentials (via standard AWS env vars)
AWS_ACCESS_KEY_ID: "minioadmin"
AWS_SECRET_ACCESS_KEY: "minioadmin"
# Storage limit per space in bytes (default: 1 TiB)
# Examples: 1 GiB = 1073741824
# 10 GiB = 10737418240
# 150 GiB = 161061273600
# 1 TiB = 1099511627776 (default)
# 2 TiB = 2199023255552
# ANY_SYNC_BUNDLE_INIT_FILENODE_DEFAULT_LIMIT: "1099511627776"
21 changes: 21 additions & 0 deletions config/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ type ConsensusConfig struct {
type FileNodeConfig struct {
RedisConnect string `yaml:"redisConnect"`
S3 *S3Config `yaml:"s3,omitempty"` // Optional: if present, use S3 storage instead of BadgerDB
// DefaultLimit is the storage limit per space in bytes.
// If not set (0), defaults to 1 TiB for backwards compatibility with old configs.
//
// TODO(bundleFormat:v2): Remove omitempty and runtime default. New configs already
// write 1 TiB explicitly; v2 can require the field to be present in config file.
DefaultLimit uint64 `yaml:"defaultLimit,omitempty"`
}

// S3Config configures S3-compatible storage backend for the filenode.
Expand Down Expand Up @@ -148,6 +154,10 @@ type CreateOptions struct {
S3Endpoint string
S3Region string // Optional, defaults to "us-east-1" if empty
S3ForcePathStyle bool

// Filenode storage limit per space in bytes.
// If 0, defaults to 1 TiB at runtime.
FilenodeDefaultLimit uint64
}

func CreateWrite(cfg *CreateOptions) *Config {
Expand Down Expand Up @@ -217,6 +227,7 @@ func newBundleConfig(cfg *CreateOptions) *Config {
},
FileNode: FileNodeConfig{
RedisConnect: cfg.RedisURI,
DefaultLimit: filenodeDefaultLimit(cfg.FilenodeDefaultLimit),
},
}

Expand All @@ -237,6 +248,16 @@ func newBundleConfig(cfg *CreateOptions) *Config {
return defaultCfg
}

// filenodeDefaultLimit returns the provided limit or 1 TiB if not set.
// This ensures new configs always have an explicit limit written to the file.
func filenodeDefaultLimit(limit uint64) uint64 {
const oneTiB = 1024 * 1024 * 1024 * 1024
if limit == 0 {
return oneTiB
}
return limit
}

func newAcc(netKey crypto.PrivKey) accountservice.Config {
signKey, _, err := crypto.GenerateRandomEd25519KeyPair()
if err != nil {
Expand Down
81 changes: 81 additions & 0 deletions config/bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,3 +420,84 @@ filenode:
assert.Equal(t, "https://s3.amazonaws.com", cfg.FileNode.S3.Endpoint)
assert.True(t, cfg.FileNode.S3.ForcePathStyle)
}

// Filenode Default Limit Tests

func TestCreateWrite_WithFilenodeDefaultLimit(t *testing.T) {
tmpDir := t.TempDir()
cfgPath := filepath.Join(tmpDir, "limit-config.yml")

const tenGiB = 10 * 1024 * 1024 * 1024 // 10 GiB

opts := &CreateOptions{
CfgPath: cfgPath,
StorePath: filepath.Join(tmpDir, "storage"),
MongoURI: "mongodb://localhost:27017/",
RedisURI: "redis://localhost:6379/",
ExternalAddrs: []string{"192.168.1.100"},
FilenodeDefaultLimit: tenGiB,
}

cfg := CreateWrite(opts)
assert.Equal(t, uint64(tenGiB), cfg.FileNode.DefaultLimit)

// Verify it persists and loads back correctly
loadedCfg := Load(cfgPath)
assert.Equal(t, uint64(tenGiB), loadedCfg.FileNode.DefaultLimit)
}

func TestCreateWrite_WithoutFilenodeDefaultLimit(t *testing.T) {
tmpDir := t.TempDir()
cfgPath := filepath.Join(tmpDir, "no-limit-config.yml")

const oneTiB = 1024 * 1024 * 1024 * 1024 // 1 TiB

opts := &CreateOptions{
CfgPath: cfgPath,
StorePath: filepath.Join(tmpDir, "storage"),
MongoURI: "mongodb://localhost:27017/",
RedisURI: "redis://localhost:6379/",
ExternalAddrs: []string{"192.168.1.100"},
// FilenodeDefaultLimit not set (zero value)
}

cfg := CreateWrite(opts)
assert.Equal(t, uint64(oneTiB), cfg.FileNode.DefaultLimit,
"DefaultLimit should be 1 TiB when not configured (written to config file)")
}

func TestLoad_WithFilenodeDefaultLimit(t *testing.T) {
tmpDir := t.TempDir()
cfgPath := filepath.Join(tmpDir, "limit-config.yml")

configWithLimit := `bundleVersion: "1.0.0"
bundleFormat: 1
externalAddr:
- "192.168.1.100"
configId: "test-config-id"
networkId: "test-network-id"
storagePath: "./data/storage"
account:
peerId: "test-peer-id"
peerKey: "test-peer-key"
signingKey: "test-signing-key"
network:
listenTCPAddr: "0.0.0.0:33010"
listenUDPAddr: "0.0.0.0:33020"
coordinator:
mongoConnect: "mongodb://localhost:27017/"
mongoDatabase: "coordinator"
consensus:
mongoConnect: "mongodb://localhost:27017/?w=majority"
mongoDatabase: "consensus"
filenode:
redisConnect: "redis://localhost:6379/"
defaultLimit: 10737418240
`

err := os.WriteFile(cfgPath, []byte(configWithLimit), 0o600)
require.NoError(t, err)

cfg := Load(cfgPath)
assert.Equal(t, uint64(10737418240), cfg.FileNode.DefaultLimit)
}
9 changes: 8 additions & 1 deletion config/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ func (bc *Config) consensusConfig(opts *nodeConfigOpts) *consensusconfig.Config
func (bc *Config) filenodeConfig(opts *nodeConfigOpts) *filenodeconfig.Config {
const oneTerabyte = 1024 * 1024 * 1024 * 1024 // 1 TiB in bytes

// Use configured limit or default to 1 TiB for backwards compatibility.
// TODO(bundleFormat:v2): Remove default, require explicit limit.
defaultLimit := bc.FileNode.DefaultLimit
if defaultLimit == 0 {
defaultLimit = oneTerabyte
}

cfg := &filenodeconfig.Config{
Account: bc.Account,
Drpc: rpc.Config{
Expand All @@ -161,7 +168,7 @@ func (bc *Config) filenodeConfig(opts *nodeConfigOpts) *filenodeconfig.Config {
Network: opts.networkCfg,
NetworkStorePath: opts.pathNetworkStoreFilenode,
NetworkUpdateIntervalSec: 0,
DefaultLimit: oneTerabyte,
DefaultLimit: defaultLimit,
}

// Configure S3 storage if S3 config is present
Expand Down
27 changes: 27 additions & 0 deletions config/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,30 @@ func TestFilenodeConfig_WithoutS3(t *testing.T) {
// S3Store should be empty (zero value)
assert.Empty(t, nodeCfgs.Filenode.S3Store.Bucket)
}

// Filenode Default Limit Tests

func TestFilenodeConfig_DefaultLimit_CustomValue(t *testing.T) {
const tenGiB = 10 * 1024 * 1024 * 1024 // 10 GiB

cfg := newTestConfig()
cfg.FileNode.DefaultLimit = tenGiB

nodeCfgs := cfg.NodeConfigs()

require.NotNil(t, nodeCfgs.Filenode)
assert.Equal(t, uint64(tenGiB), nodeCfgs.Filenode.DefaultLimit)
}

func TestFilenodeConfig_DefaultLimit_ZeroDefaultsTo1TiB(t *testing.T) {
const oneTiB = 1024 * 1024 * 1024 * 1024 // 1 TiB

cfg := newTestConfig()
cfg.FileNode.DefaultLimit = 0 // Not set

nodeCfgs := cfg.NodeConfigs()

require.NotNil(t, nodeCfgs.Filenode)
assert.Equal(t, uint64(oneTiB), nodeCfgs.Filenode.DefaultLimit,
"Zero DefaultLimit should fallback to 1 TiB")
}
Loading