cfg/rationalize.go (90 lines of code) (raw):

// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cfg import ( "math" "net/url" "github.com/googlecloudplatform/gcsfuse/v2/internal/util" ) // isSet interface is abstraction over the IsSet() method of viper, specially // added to keep rationalize method simple. IsSet will be used to resolve // conflicting deprecated flags and new configs. type isSet interface { IsSet(string) bool } func decodeURL(u string) (string, error) { // TODO: check if we can replace url.Parse with url.ParseRequestURI. decodedURL, err := url.Parse(u) if err != nil { return "", err } return decodedURL.String(), nil } // resolveMetadataCacheTTL calculates the ttl to be used for stat/type cache based // on the user flags/configs or machine type based optimizations. func resolveMetadataCacheTTL(v isSet, c *MetadataCacheConfig, optimizedFlags []string) { optimizationAppliedToNegativeCacheTTL := isFlagPresent(optimizedFlags, MetadataNegativeCacheTTLConfigKey) if v.IsSet(MetadataNegativeCacheTTLConfigKey) || optimizationAppliedToNegativeCacheTTL { if c.NegativeTtlSecs == -1 { c.NegativeTtlSecs = maxSupportedTTLInSeconds } } // Order of precedence for setting TTL seconds // 1. If metadata-cache:ttl-secs has been set, then it has highest precedence // 2. If metadata-cache:stat-cache-ttl or metadata-cache:type-cache-ttl has been set or no optimization applied, then it has second highest precedence // 3. Optimization is applied (implicit) and take care of special case of -1 which can occur even in defaults optimizationAppliedToMetadataCacheTTL := isFlagPresent(optimizedFlags, MetadataCacheTTLConfigKey) if v.IsSet(MetadataCacheTTLConfigKey) { if c.TtlSecs == -1 { c.TtlSecs = maxSupportedTTLInSeconds } } else if (v.IsSet(MetadataCacheStatCacheTTLConfigKey) || v.IsSet(MetadataCacheTypeCacheTTLConfigKey)) || (!optimizationAppliedToMetadataCacheTTL) { c.TtlSecs = int64(math.Ceil(math.Min(c.DeprecatedStatCacheTtl.Seconds(), c.DeprecatedTypeCacheTtl.Seconds()))) } else if c.TtlSecs == -1 { c.TtlSecs = maxSupportedTTLInSeconds } } // resolveStatCacheMaxSizeMB calculates the stat-cache size in MiBs based on the // machine-type default override, user's old and new flags/configs. func resolveStatCacheMaxSizeMB(v isSet, c *MetadataCacheConfig, optimizedFlags []string) { // Local function to calculate size based on deprecated capacity. calculateSizeFromCapacity := func(capacity int64) int64 { avgTotalStatCacheEntrySize := AverageSizeOfPositiveStatCacheEntry + AverageSizeOfNegativeStatCacheEntry return int64(util.BytesToHigherMiBs(uint64(capacity) * avgTotalStatCacheEntrySize)) } // Order of precedence for setting stat cache size // 1. If metadata-cache:stat-cache-size-mb is set it has the highest precedence // 2. If stat-cache-capacity is set or optimization is not applied then use it to calculate stat cache size // 3. Else handle special case of -1 for both optimized or possible default value optimizationAppliedToStatCacheMaxSize := isFlagPresent(optimizedFlags, StatCacheMaxSizeConfigKey) if v.IsSet(StatCacheMaxSizeConfigKey) { if c.StatCacheMaxSizeMb == -1 { c.StatCacheMaxSizeMb = int64(maxSupportedStatCacheMaxSizeMB) } } else if v.IsSet(MetadataCacheStatCacheCapacityConfigKey) || (!optimizationAppliedToStatCacheMaxSize) { c.StatCacheMaxSizeMb = calculateSizeFromCapacity(c.DeprecatedStatCacheCapacity) } else if c.StatCacheMaxSizeMb == -1 { c.StatCacheMaxSizeMb = int64(maxSupportedStatCacheMaxSizeMB) } } func resolveStreamingWriteConfig(w *WriteConfig) { if w.EnableStreamingWrites { w.CreateEmptyFile = false } w.BlockSizeMb *= util.MiB if w.GlobalMaxBlocks == -1 { w.GlobalMaxBlocks = math.MaxInt64 } if w.MaxBlocksPerFile == -1 { // Setting a reasonable value here because if enough heap space is not // available, make channel results in panic. w.MaxBlocksPerFile = math.MaxInt16 } } func resolveCloudMetricsUploadIntervalSecs(m *MetricsConfig) { if m.CloudMetricsExportIntervalSecs == 0 { m.CloudMetricsExportIntervalSecs = int64(m.StackdriverExportInterval.Seconds()) } } func resolveParallelDownloadsValue(v isSet, fc *FileCacheConfig, c *Config) { // Parallel downloads should be default ON when file cache is enabled, in case // it is explicitly set by the user, use that value. if IsFileCacheEnabled(c) && !v.IsSet(FileCacheParallelDownloadsConfigKey) { fc.EnableParallelDownloads = true } } // Rationalize updates the config fields based on the values of other fields. func Rationalize(v isSet, c *Config, optimizedFlags []string) error { var err error if c.GcsConnection.CustomEndpoint, err = decodeURL(c.GcsConnection.CustomEndpoint); err != nil { return err } if c.GcsAuth.TokenUrl, err = decodeURL(c.GcsAuth.TokenUrl); err != nil { return err } if c.Debug.Fuse || c.Debug.Gcs || c.Debug.LogMutex { c.Logging.Severity = "TRACE" } resolveStreamingWriteConfig(&c.Write) resolveMetadataCacheTTL(v, &c.MetadataCache, optimizedFlags) resolveStatCacheMaxSizeMB(v, &c.MetadataCache, optimizedFlags) resolveCloudMetricsUploadIntervalSecs(&c.Metrics) resolveParallelDownloadsValue(v, &c.FileCache, c) return nil }