public DistributedContentStoreFactory(DistributedCacheServiceArguments arguments)
        {
            _logger              = arguments.Logger;
            _arguments           = arguments;
            _distributedSettings = arguments.Configuration.DistributedContentSettings;
            _keySpace            = string.IsNullOrWhiteSpace(_arguments.Keyspace) ? ContentLocationStoreFactory.DefaultKeySpace : _arguments.Keyspace;
            _fileSystem          = new PassThroughFileSystem(_logger);
            _secretRetriever     = new DistributedCacheSecretRetriever(arguments);
            var bandwidthCheckedCopier = new BandwidthCheckedCopier(_arguments.Copier, BandwidthChecker.Configuration.FromDistributedContentSettings(_distributedSettings));

            _orderedResolvedCacheSettings = ResolveCacheSettingsInPrecedenceOrder(arguments);
            Contract.Assert(_orderedResolvedCacheSettings.Count != 0);

            RedisContentLocationStoreConfiguration = CreateRedisConfiguration();
            _distributedContentStoreSettings       = CreateDistributedStoreSettings(_arguments, RedisContentLocationStoreConfiguration);

            _copier = new DistributedContentCopier <AbsolutePath>(
                _distributedContentStoreSettings,
                _fileSystem,
                fileCopier: bandwidthCheckedCopier,
                fileExistenceChecker: _arguments.Copier,
                _arguments.CopyRequester,
                _arguments.PathTransformer,
                _arguments.Overrides.Clock
                );

            _redisMemoizationStoreFactory = new Lazy <RedisMemoizationStoreFactory>(() => CreateRedisCacheFactory());
        }
Beispiel #2
0
        public DistributedContentStoreFactory(DistributedCacheServiceArguments arguments)
        {
            _logger              = arguments.Logger;
            _arguments           = arguments;
            _distributedSettings = arguments.Configuration.DistributedContentSettings;
            _keySpace            = string.IsNullOrWhiteSpace(_arguments.Keyspace) ? ContentLocationStoreFactory.DefaultKeySpace : _arguments.Keyspace;
            _fileSystem          = arguments.FileSystem;
            _secretRetriever     = new DistributedCacheSecretRetriever(arguments);

            _orderedResolvedCacheSettings = ResolveCacheSettingsInPrecedenceOrder(arguments);
            Contract.Assert(_orderedResolvedCacheSettings.Count != 0);

            RedisContentLocationStoreConfiguration = CreateRedisConfiguration();
            _distributedContentStoreSettings       = CreateDistributedStoreSettings(_arguments, RedisContentLocationStoreConfiguration);

            _copier = new DistributedContentCopier(
                _distributedContentStoreSettings,
                _fileSystem,
                fileCopier: _arguments.Copier,
                copyRequester: _arguments.CopyRequester,
                _arguments.Overrides.Clock,
                _logger
                );

            _redisMemoizationStoreFactory = new Lazy <RedisMemoizationStoreFactory>(() => CreateRedisCacheFactory());
        }
Beispiel #3
0
        protected override IContentStore CreateFromArguments(DistributedCacheServiceArguments arguments)
        {
            var factory       = new DistributedContentStoreFactory(arguments);
            var topLevelStore = factory.CreateTopLevelStore().topLevelStore;

            return(topLevelStore);
        }
Beispiel #4
0
        private static DistributedContentStoreSettings CreateDistributedStoreSettings(
            DistributedCacheServiceArguments arguments,
            RedisContentLocationStoreConfiguration redisContentLocationStoreConfiguration)
        {
            var distributedSettings = arguments.Configuration.DistributedContentSettings;

            PinConfiguration pinConfiguration = new PinConfiguration();

            if (distributedSettings.IsPinBetterEnabled)
            {
                ApplyIfNotNull(distributedSettings.PinMinUnverifiedCount, v => pinConfiguration.PinMinUnverifiedCount = v);
                ApplyIfNotNull(distributedSettings.StartCopyWhenPinMinUnverifiedCountThreshold, v => pinConfiguration.StartCopyWhenPinMinUnverifiedCountThreshold = v);
                ApplyIfNotNull(distributedSettings.MaxIOOperations, v => pinConfiguration.MaxIOOperations = v);
            }

            var distributedContentStoreSettings = new DistributedContentStoreSettings()
            {
                TrustedHashFileSizeBoundary     = distributedSettings.TrustedHashFileSizeBoundary,
                ParallelHashingFileSizeBoundary = distributedSettings.ParallelHashingFileSizeBoundary,
                MaxConcurrentCopyOperations     = distributedSettings.MaxConcurrentCopyOperations,
                PinConfiguration                     = pinConfiguration,
                RetryIntervalForCopies               = distributedSettings.RetryIntervalForCopies,
                MaxRetryCount                        = distributedSettings.MaxRetryCount,
                TimeoutForProactiveCopies            = TimeSpan.FromMinutes(distributedSettings.TimeoutForProactiveCopiesMinutes),
                ProactiveCopyMode                    = (ProactiveCopyMode)Enum.Parse(typeof(ProactiveCopyMode), distributedSettings.ProactiveCopyMode),
                PushProactiveCopies                  = distributedSettings.PushProactiveCopies,
                ProactiveCopyOnPut                   = distributedSettings.ProactiveCopyOnPut,
                ProactiveCopyOnPin                   = distributedSettings.ProactiveCopyOnPin,
                ProactiveCopyUsePreferredLocations   = distributedSettings.ProactiveCopyUsePreferredLocations,
                MaxConcurrentProactiveCopyOperations = distributedSettings.MaxConcurrentProactiveCopyOperations,
                ProactiveCopyLocationsThreshold      = distributedSettings.ProactiveCopyLocationsThreshold,
                ProactiveCopyRejectOldContent        = distributedSettings.ProactiveCopyRejectOldContent,
                ReplicaCreditInMinutes               = distributedSettings.IsDistributedEvictionEnabled ? distributedSettings.ReplicaCreditInMinutes : null,
                EnableRepairHandling                 = distributedSettings.IsRepairHandlingEnabled,
                LocationStoreBatchSize               = distributedSettings.RedisBatchPageSize,
                RestrictedCopyReplicaCount           = distributedSettings.RestrictedCopyReplicaCount,
                CopyAttemptsWithRestrictedReplicas   = distributedSettings.CopyAttemptsWithRestrictedReplicas,
                AreBlobsSupported                    = redisContentLocationStoreConfiguration.AreBlobsSupported,
                MaxBlobSize = redisContentLocationStoreConfiguration.MaxBlobSize,
                DelayForProactiveReplication  = TimeSpan.FromSeconds(distributedSettings.ProactiveReplicationDelaySeconds),
                ProactiveReplicationCopyLimit = distributedSettings.ProactiveReplicationCopyLimit,
                EnableProactiveReplication    = distributedSettings.EnableProactiveReplication,
                TraceProactiveCopy            = distributedSettings.TraceProactiveCopy,
                ProactiveCopyGetBulkBatchSize = distributedSettings.ProactiveCopyGetBulkBatchSize,
                ProactiveCopyGetBulkInterval  = TimeSpan.FromSeconds(distributedSettings.ProactiveCopyGetBulkIntervalSeconds)
            };

            if (distributedSettings.EnableProactiveReplication && redisContentLocationStoreConfiguration.Checkpoint != null)
            {
                distributedContentStoreSettings.ProactiveReplicationInterval = redisContentLocationStoreConfiguration.Checkpoint.RestoreCheckpointInterval;
            }

            ApplyIfNotNull(distributedSettings.MaximumConcurrentPutAndPlaceFileOperations, v => distributedContentStoreSettings.MaximumConcurrentPutAndPlaceFileOperations = v);

            arguments.Overrides.Override(distributedContentStoreSettings);

            ConfigurationPrinter.TraceConfiguration(distributedContentStoreSettings, arguments.Logger);

            return(distributedContentStoreSettings);
        }
        internal static IContentStore CreateLocalContentStore(
            DistributedContentSettings settings,
            DistributedCacheServiceArguments arguments,
            ResolvedNamedCacheSettings resolvedSettings,
            IDistributedLocationStore distributedStore = null)
        {
            settings = settings ?? DistributedContentSettings.CreateDisabled();
            var contentStoreSettings = FromDistributedSettings(settings);

            ConfigurationModel configurationModel
                = new ConfigurationModel(new ContentStoreConfiguration(new MaxSizeQuota(resolvedSettings.Settings.CacheSizeQuotaString)));

            var localStore = ContentStoreFactory.CreateContentStore(arguments.FileSystem, resolvedSettings.ResolvedCacheRootPath,
                                                                    contentStoreSettings: contentStoreSettings, distributedStore: distributedStore, configurationModel: configurationModel);

            if (settings.BackingGrpcPort != null)
            {
                var backingStore = new ServiceClientContentStore(
                    arguments.Logger,
                    arguments.FileSystem,
                    resolvedSettings.Name,
                    new ServiceClientRpcConfiguration(settings.BackingGrpcPort.Value),
                    arguments.Configuration.LocalCasSettings.CasClientSettings.RetryIntervalSecondsOnFailServiceCalls,
                    arguments.Configuration.LocalCasSettings.CasClientSettings.RetryCountOnFailServiceCalls,
                    scenario: settings.BackingScenario);

                return(new MultiLevelContentStore(localStore, backingStore));
            }

            return(localStore);
        }
        protected override DistributedCacheServiceArguments ModifyArguments(DistributedCacheServiceArguments arguments)
        {
            var dcs = arguments.Configuration.DistributedContentSettings;

            dcs.EnableDistributedCache = true;

            return(base.ModifyArguments(arguments));
        }
        public DistributedCacheSecretRetriever(DistributedCacheServiceArguments arguments)
        {
            _distributedSettings  = arguments.Configuration.DistributedContentSettings;
            _loggingConfiguration = arguments.LoggingSettings?.Configuration;
            _logger = arguments.Logger;
            _host   = arguments.Host;

            _secrets = new Lazy <Task <Result <RetrievedSecrets> > >(TryGetSecretsAsync);
        }
Beispiel #8
0
        public CacheServerFactory(DistributedCacheServiceArguments arguments)
        {
            _arguments = arguments;
            _logger    = arguments.Logger;

            // Enable POSIX delete to ensure that files are removed even when there are open handles
            PassThroughFileSystem.EnablePosixDelete();
            _fileSystem = new PassThroughFileSystem(_logger);
        }
 public DistributedContentStoreFactory(DistributedCacheServiceArguments arguments)
 {
     _logger    = arguments.Logger;
     _arguments = arguments;
     _redisContentSecretNames = arguments.Configuration.DistributedContentSettings.GetRedisConnectionSecretNames(arguments.HostInfo.StampId);
     _distributedSettings     = arguments.Configuration.DistributedContentSettings;
     _keySpace        = string.IsNullOrWhiteSpace(_arguments.Keyspace) ? RedisContentLocationStoreFactory.DefaultKeySpace : _arguments.Keyspace;
     _fileSystem      = new PassThroughFileSystem(_logger);
     _secretRetriever = new DistributedCacheSecretRetriever(arguments);
 }
 public DistributedContentStoreFactory(
     DistributedCacheServiceArguments arguments,
     RedisContentSecretNames redisContentSecretNames)
 {
     _logger    = arguments.Logger;
     _arguments = arguments;
     _redisContentSecretNames = redisContentSecretNames;
     _distributedSettings     = arguments.Configuration.DistributedContentSettings;
     _keySpace   = string.IsNullOrWhiteSpace(_arguments.Keyspace) ? RedisContentLocationStoreFactory.DefaultKeySpace : _arguments.Keyspace;
     _fileSystem = new PassThroughFileSystem(_logger);
 }
        protected override ICache CreateFromArguments(DistributedCacheServiceArguments arguments)
        {
            var factory = new DistributedContentStoreFactory(arguments);
            var store   = factory.CreateTopLevelStore().topLevelStore;

            return(new DistributedOneLevelCache(
                       store,
                       factory.Services.ContentLocationStoreServices.Instance,
                       Guid.NewGuid(),
                       passContentToMemoization: false));
        }
        protected override TestServerProvider CreateStore(Context context, DistributedCacheServiceArguments arguments)
        {
            // Need to enable ASP.Net Core GRPC
            arguments.Configuration.DistributedContentSettings.EnableAspNetCoreGrpc = true;

            var server = new TestCacheServerWrapper(Host, arguments);

            return(new TestServerProvider(server, new Func <IContentStore>(() =>
            {
                return server.Host.Store.Task.Result;
            })));
        }
Beispiel #13
0
            static Result <CacheServiceWrapperConfiguration> tryCreateConfiguration(DistributedCacheServiceArguments configuration)
            {
                var outOfProcSettings = configuration.Configuration.DistributedContentSettings.OutOfProcCacheSettings;

                if (outOfProcSettings is null)
                {
                    return(Result.FromErrorMessage <CacheServiceWrapperConfiguration>($"{nameof(configuration.Configuration.DistributedContentSettings.OutOfProcCacheSettings)} should not be null."));
                }

                if (outOfProcSettings.Executable is null)
                {
                    return(Result.FromErrorMessage <CacheServiceWrapperConfiguration>($"{nameof(outOfProcSettings.Executable)} is null."));
                }

                if (!File.Exists(outOfProcSettings.Executable))
                {
                    // This is not a bullet proof check, but if the executable is not found we should not even trying to create an out of proc cache service.
                    return(Result.FromErrorMessage <CacheServiceWrapperConfiguration>($"the executable is not found at '{outOfProcSettings.Executable}'."));
                }

                if (outOfProcSettings.CacheConfigPath is null)
                {
                    return(Result.FromErrorMessage <CacheServiceWrapperConfiguration>($"{nameof(outOfProcSettings.CacheConfigPath)} is null."));
                }

                if (!File.Exists(outOfProcSettings.CacheConfigPath))
                {
                    // This is not a bullet proof check, but if the executable is not found we should not even trying to create an out of proc cache service.
                    return(Result.FromErrorMessage <CacheServiceWrapperConfiguration>($"the cache configuration is not found at '{outOfProcSettings.CacheConfigPath}'."));
                }

                // The next layout should be in sync with CloudBuild.
                AbsolutePath executable       = getExecutingPath() / outOfProcSettings.Executable;
                var          workingDirectory = getRootPath(configuration.Configuration);

                var hostParameters = HostParameters.FromTelemetryProvider(configuration.TelemetryFieldsProvider);

                var resultingConfiguration = new CacheServiceWrapperConfiguration(
                    serviceId: "OutOfProcCache",
                    executable: executable,
                    workingDirectory: workingDirectory,
                    hostParameters: hostParameters,
                    cacheConfigPath: new AbsolutePath(outOfProcSettings.CacheConfigPath),
                    // DataRootPath is set in CloudBuild and we need to propagate this configuration to the launched process.
                    dataRootPath: new AbsolutePath(configuration.Configuration.DataRootPath),
                    useInterProcSecretsCommunication: outOfProcSettings.UseInterProcSecretsCommunication);

                outOfProcSettings.ServiceLifetimePollingIntervalSeconds.ApplyIfNotNull(v => resultingConfiguration.ServiceLifetimePollingInterval = TimeSpan.FromSeconds(v));
                outOfProcSettings.ShutdownTimeoutSeconds.ApplyIfNotNull(v => resultingConfiguration.ShutdownTimeout = TimeSpan.FromSeconds(v));

                return(resultingConfiguration);
            }
            private string[] CreateCommandLine(DistributedCacheServiceArguments arguments)
            {
                var configurationText = DeploymentUtilities.JsonSerialize(arguments.Configuration);
                var configurationPath = Path.Combine(arguments.DataRootPath, "ServerCacheConfiguration.json");

                Directory.CreateDirectory(arguments.DataRootPath);
                File.WriteAllText(configurationPath, configurationText);

                return(new[]
                {
                    "--cacheconfigurationPath", configurationPath
                });
            }
            public TestCacheServerWrapper(TestHost testHost, DistributedCacheServiceArguments arguments)
            {
                string[] commandLineArguments = CreateCommandLine(arguments);

                var hostParameters = HostParameters.FromEnvironment();
                var hostInfo       = new HostInfo(hostParameters.Stamp, hostParameters.Ring, new List <string>());

                var serviceHost = new CacheServiceStartup.ServiceHost(
                    new string[0],
                    arguments.Configuration,
                    hostParameters,
                    new Context(TestGlobal.Logger));

                Host = new TestServiceHost(testHost, serviceHost);

                var _ = arguments.Cancellation.Register(() => Host.StartupCompletedSignal.TrySetCanceled());

                RunInBackground("RunCacheService", async context =>
                {
                    using (var cts = CancellationTokenSource.CreateLinkedTokenSource(context.Token, arguments.Cancellation))
                    {
                        arguments = arguments with
                        {
                            Host         = Host,
                            Cancellation = cts.Token
                        };

                        await DistributedCacheServiceFacade.RunAsync(arguments);

                        Assert.True(cts.IsCancellationRequested, "Cache service task shutdown prematurely");
                    }

                    return(BoolResult.Success);
                },
                                fireAndForget: false);
            }
Beispiel #16
0
        public static async Task <Result <CacheServiceWrapper> > CreateAsync(DistributedCacheServiceArguments configuration)
        {
            // Validating the cache configuration

            var wrapperConfiguration = tryCreateConfiguration(configuration);

            if (!wrapperConfiguration.Succeeded)
            {
                const string BaseError = "Can't start cache service as a separate process because";
                return(Result.FromErrorMessage <CacheServiceWrapper>($"{BaseError} {wrapperConfiguration.ErrorMessage}"));
            }

            // Obtaining the secrets and creating a wrapper.
            var serviceLifetimeManager = new ServiceLifetimeManager(wrapperConfiguration.Value.WorkingDirectory, wrapperConfiguration.Value.ServiceLifetimePollingInterval);
            var secretsRetriever       = new DistributedCacheSecretRetriever(configuration);

            var secrets = await secretsRetriever.TryRetrieveSecretsAsync();

            if (!secrets.Succeeded)
            {
                return(new Result <CacheServiceWrapper>(secrets));
            }

            return(Result.Success(new CacheServiceWrapper(wrapperConfiguration.Value, serviceLifetimeManager, secrets.Value)));
 protected abstract TStore CreateFromArguments(DistributedCacheServiceArguments arguments);
Beispiel #18
0
 public ContentServerFactory(DistributedCacheServiceArguments arguments)
 {
     _arguments  = arguments;
     _logger     = arguments.Logger;
     _fileSystem = new PassThroughFileSystem(_logger);
 }
Beispiel #19
0
        internal void Service
        (
            [Description("Cache names")] string[] names,
            [Description("Cache root paths")] string[] paths,
            [DefaultValue(DefaultMaxConnections), Description(MaxConnectionsDescription)] uint maxConnections,
            [DefaultValue(DefaultGracefulShutdownSeconds), Description(GracefulShutdownSecondsDescription)] uint gracefulShutdownSeconds,
            [DefaultValue(ServiceConfiguration.GrpcDisabledPort), Description(GrpcPortDescription)] int grpcPort,
            [Description("Name of the memory mapped file used to share GRPC port. 'CASaaS GRPC port' if not specified.")] string grpcPortFileName,
            [DefaultValue(null), Description("Writable directory for service operations (use CWD if null)")] string dataRootPath,
            [DefaultValue(null), Description("Duration of inactivity after which a session will be timed out.")] double?unusedSessionTimeoutSeconds,
            [DefaultValue(null), Description("Duration of inactivity after which a session with a heartbeat will be timed out.")] double?unusedSessionHeartbeatTimeoutSeconds,
            [DefaultValue(false), Description("Stop running service")] bool stop,
            [DefaultValue(Constants.OneMB), Description("Max size quota in MB")] int maxSizeQuotaMB
        )
        {
            Initialize();

            if (stop)
            {
                IpcUtilities.SetShutdown(_scenario);

                return;
            }

            if (names == null || paths == null)
            {
                throw new CacheException("At least one cache name/path is required.");
            }

            if (names.Length != paths.Length)
            {
                throw new CacheException("Mismatching lengths of names/paths arguments.");
            }

            var caches = new Dictionary <string, string>();

            for (var i = 0; i < names.Length; i++)
            {
                caches.Add(names[i], paths[i]);
            }

            var serverDataRootPath = !string.IsNullOrWhiteSpace(dataRootPath)
                ? new AbsolutePath(dataRootPath)
                : new AbsolutePath(Environment.CurrentDirectory);

            var cancellationTokenSource = new CancellationTokenSource();

#if !FEATURE_CORECLR
            var configuration = new ServiceConfiguration(caches, serverDataRootPath, maxConnections, gracefulShutdownSeconds, grpcPort, grpcPortFileName);
            if (!configuration.IsValid)
            {
                throw new CacheException($"Invalid service configuration, error=[{configuration.Error}]");
            }

            var localContentServerConfiguration = new LocalServerConfiguration(configuration);
            if (unusedSessionTimeoutSeconds != null)
            {
                localContentServerConfiguration.UnusedSessionTimeout = TimeSpan.FromSeconds(unusedSessionTimeoutSeconds.Value);
            }

            if (unusedSessionHeartbeatTimeoutSeconds != null)
            {
                localContentServerConfiguration.UnusedSessionHeartbeatTimeout = TimeSpan.FromSeconds(unusedSessionHeartbeatTimeoutSeconds.Value);
            }

            if (_scenario != null)
            {
                _logger.Debug($"scenario=[{_scenario}]");
            }

            var exitSignal = new ManualResetEvent(false);
            Console.CancelKeyPress += (sender, args) =>
            {
                exitSignal.Set();
                args.Cancel = true;
            };

            using (var exitEvent = IpcUtilities.GetShutdownWaitHandle(_scenario))
            {
                var server = new LocalContentServer(
                    _fileSystem,
                    _logger,
                    _scenario,
                    path =>
                    new FileSystemContentStore(
                        _fileSystem,
                        SystemClock.Instance,
                        path,
                        new ConfigurationModel(inProcessConfiguration: ContentStoreConfiguration.CreateWithMaxSizeQuotaMB((uint)maxSizeQuotaMB))),
                    localContentServerConfiguration);

                using (server)
                {
                    var context = new Context(_logger);
                    try
                    {
                        var result = server.StartupAsync(context).Result;
                        if (!result.Succeeded)
                        {
                            throw new CacheException(result.ErrorMessage);
                        }

                        int completedIndex = WaitHandle.WaitAny(new WaitHandle[] { exitSignal, exitEvent });
                        var source         = completedIndex == 0 ? "control-C" : "exit event";
                        _tracer.Always(context, $"Shutdown by {source}.");
                    }
                    finally
                    {
                        var result = server.ShutdownAsync(context).Result;
                        if (!result.Succeeded)
                        {
                            _tracer.Warning(context, $"Failed to shutdown store: {result.ErrorMessage}");
                        }
                    }
                }
            }
#else
            Console.CancelKeyPress += (sender, args) =>
            {
                cancellationTokenSource.Cancel();
                args.Cancel = true;
            };

            var localCasSettings = LocalCasSettings.Default(maxSizeQuotaMB, serverDataRootPath.Path, names[0], (uint)grpcPort);

            var distributedContentSettings = DistributedContentSettings.CreateDisabled();

            var distributedCacheServiceConfiguration = new DistributedCacheServiceConfiguration(localCasSettings, distributedContentSettings);

            // Ensure the computed keyspace is computed based on the hostInfo's StampId
            distributedCacheServiceConfiguration.UseStampBasedIsolation = false;

            var distributedCacheServiceArguments = new DistributedCacheServiceArguments(
                logger: _logger,
                copier: null,
                pathTransformer: null,
                host: new EnvironmentVariableHost(),
                hostInfo: new HostInfo(null, null, new List <string>()),
                cancellation: cancellationTokenSource.Token,
                dataRootPath: serverDataRootPath.Path,
                configuration: distributedCacheServiceConfiguration,
                keyspace: null);

            DistributedCacheServiceFacade.RunAsync(distributedCacheServiceArguments).GetAwaiter().GetResult();

            // Because the facade completes immediately and named wait handles don't exist in CORECLR,
            // completion here is gated on Control+C. In the future, this can be redone with another option,
            // such as a MemoryMappedFile or GRPC heartbeat. This is just intended to be functional.
            cancellationTokenSource.Token.WaitHandle.WaitOne();
#endif
        }
Beispiel #20
0
        internal void Service
        (
            [Description("Cache names")] string[] names,
            [Description("Cache root paths")] string[] paths,
            [DefaultValue(DefaultMaxConnections), Description(MaxConnectionsDescription)] uint maxConnections,
            [DefaultValue(DefaultGracefulShutdownSeconds), Description(GracefulShutdownSecondsDescription)] uint gracefulShutdownSeconds,
            [DefaultValue(ServiceConfiguration.GrpcDisabledPort), Description(GrpcPortDescription)] uint grpcPort,
            [Description("Name of the memory mapped file used to share GRPC port. 'CASaaS GRPC port' if not specified.")] string grpcPortFileName,
            [DefaultValue(null), Description("Writable directory for service operations (use CWD if null)")] string dataRootPath,
            [DefaultValue(null), Description("Duration of inactivity after which a session will be timed out.")] double?unusedSessionTimeoutSeconds,
            [DefaultValue(null), Description("Duration of inactivity after which a session with a heartbeat will be timed out.")] double?unusedSessionHeartbeatTimeoutSeconds,
            [DefaultValue(false), Description("Stop running service")] bool stop,
            [DefaultValue(Constants.OneGBInMB), Description("Max size quota in MB")] int maxSizeQuotaMB,
            [DefaultValue(ServiceConfiguration.GrpcDisabledPort), Description(RemoteGrpcPortDescription)] uint backingGrpcPort,
            [DefaultValue(null), Description("Name of scenario for backing CAS service")] string backingScenario,
            [DefaultValue("None"), Description("Ring Id. Used only for telemetry.")] string ringId,
            [DefaultValue("None"), Description("Stamp Id. Used only for telemetry.")] string stampId,
            [DefaultValue(null), Description("nLog configuration path. If empty, it is disabled")] string nLogConfigurationPath,
            [DefaultValue(null), Description("Whether to use Azure Blob logging or not")] string nLogToBlobStorageSecretName,
            [DefaultValue(null), Description("If using Azure Blob logging, where to temporarily store logs")] string nLogToBlobStorageWorkspacePath,
            [DefaultValue(false), Description("Enable metadata")] bool enableMetadata
        )
        {
            Initialize();

            if (stop)
            {
                IpcUtilities.SetShutdown(_scenario);
                return;
            }

            if (names == null || paths == null)
            {
                throw new CacheException("At least one cache name/path is required.");
            }

            if (names.Length != paths.Length)
            {
                throw new CacheException("Mismatching lengths of names/paths arguments.");
            }

            var serverDataRootPath = !string.IsNullOrWhiteSpace(dataRootPath)
                ? new AbsolutePath(dataRootPath)
                : new AbsolutePath(Environment.CurrentDirectory);

            var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken);

            if (_scenario != null)
            {
                _logger.Debug($"scenario=[{_scenario}]");
            }

            using var exitEvent = GetExitEvent();

            var localCasSettings = LocalCasSettings.Default(maxSizeQuotaMB, paths[0], names[0], grpcPort, grpcPortFileName);

            for (int i = 1; i < names.Length; i++)
            {
                localCasSettings.AddNamedCache(names[i], paths[i]);
            }

            localCasSettings.ServiceSettings.ScenarioName = _scenario;

            if (unusedSessionTimeoutSeconds != null)
            {
                localCasSettings.ServiceSettings.UnusedSessionTimeoutMinutes = TimeSpan.FromSeconds(unusedSessionTimeoutSeconds.Value).TotalMinutes;
            }

            if (unusedSessionHeartbeatTimeoutSeconds != null)
            {
                localCasSettings.ServiceSettings.UnusedSessionHeartbeatTimeoutMinutes = TimeSpan.FromSeconds(unusedSessionHeartbeatTimeoutSeconds.Value).TotalMinutes;
            }

            var distributedContentSettings = DistributedContentSettings.CreateDisabled();

            if (backingGrpcPort != ServiceConfiguration.GrpcDisabledPort)
            {
                distributedContentSettings.BackingGrpcPort = (int)backingGrpcPort;
                distributedContentSettings.BackingScenario = backingScenario;
            }

            if (enableMetadata)
            {
                distributedContentSettings.EnableMetadataStore = true;
            }

            LoggingSettings loggingSettings = null;

            if (!string.IsNullOrEmpty(nLogConfigurationPath))
            {
                loggingSettings = new LoggingSettings()
                {
                    NLogConfigurationPath = nLogConfigurationPath,
                    Configuration         = new AzureBlobStorageLogPublicConfiguration()
                    {
                        SecretName          = nLogToBlobStorageSecretName,
                        WorkspaceFolderPath = nLogToBlobStorageWorkspacePath,
                    }
                };
            }

            var distributedCacheServiceConfiguration = new DistributedCacheServiceConfiguration(localCasSettings, distributedContentSettings, loggingSettings);

            // Ensure the computed keyspace is computed based on the hostInfo's StampId
            distributedCacheServiceConfiguration.UseStampBasedIsolation = false;

            var distributedCacheServiceArguments = new DistributedCacheServiceArguments(
                logger: _logger,
                copier: new DistributedCopier(),
                copyRequester: null,
                host: new EnvironmentVariableHost(),
                hostInfo: new HostInfo(null, null, new List <string>()),
                cancellation: cancellationTokenSource.Token,
                dataRootPath: serverDataRootPath.Path,
                configuration: distributedCacheServiceConfiguration,
                keyspace: null)
            {
                TelemetryFieldsProvider = new TelemetryFieldsProvider(ringId, stampId, serviceName: "Service"),
            };

            var runTask = Task.Run(() => DistributedCacheServiceFacade.RunAsync(distributedCacheServiceArguments));

            // Because the facade completes immediately and named wait handles don't exist in CORECLR,
            // completion here is gated on Control+C. In the future, this can be redone with another option,
            // such as a MemoryMappedFile or GRPC heartbeat. This is just intended to be functional.
            int completedIndex = WaitHandle.WaitAny(new WaitHandle[] { cancellationTokenSource.Token.WaitHandle, exitEvent });

            var source = completedIndex == 0 ? "control-C" : "exit event";

            _logger.Always($"Shutdown by {source}.");

            if (completedIndex == 1)
            {
                cancellationTokenSource.Cancel();
            }

            runTask.GetAwaiter().GetResult();
        }
        private static DistributedContentStoreSettings CreateDistributedStoreSettings(
            DistributedCacheServiceArguments arguments,
            RedisContentLocationStoreConfiguration redisContentLocationStoreConfiguration)
        {
            var distributedSettings = arguments.Configuration.DistributedContentSettings;

            ContentAvailabilityGuarantee contentAvailabilityGuarantee;

            if (string.IsNullOrEmpty(distributedSettings.ContentAvailabilityGuarantee))
            {
                contentAvailabilityGuarantee = ContentAvailabilityGuarantee.FileRecordsExist;
            }
            else if (!Enum.TryParse(distributedSettings.ContentAvailabilityGuarantee, true, out contentAvailabilityGuarantee))
            {
                throw new ArgumentException($"Unable to parse {nameof(distributedSettings.ContentAvailabilityGuarantee)}: [{distributedSettings.ContentAvailabilityGuarantee}]");
            }

            PinConfiguration pinConfiguration = null;

            if (distributedSettings.IsPinBetterEnabled)
            {
                pinConfiguration = new PinConfiguration();
                ApplyIfNotNull(distributedSettings.PinRisk, v => pinConfiguration.PinRisk = v);
                ApplyIfNotNull(distributedSettings.PinMinUnverifiedCount, v => pinConfiguration.PinMinUnverifiedCount = v);
                ApplyIfNotNull(distributedSettings.MachineRisk, v => pinConfiguration.MachineRisk         = v);
                ApplyIfNotNull(distributedSettings.FileRisk, v => pinConfiguration.FileRisk               = v);
                ApplyIfNotNull(distributedSettings.MaxIOOperations, v => pinConfiguration.MaxIOOperations = v);

                pinConfiguration.IsPinCachingEnabled = distributedSettings.IsPinCachingEnabled;

                ApplyIfNotNull(distributedSettings.PinCacheReplicaCreditRetentionMinutes, v => pinConfiguration.PinCachePerReplicaRetentionCreditMinutes = v);
                ApplyIfNotNull(distributedSettings.PinCacheReplicaCreditRetentionDecay, v => pinConfiguration.PinCacheReplicaCreditRetentionFactor       = v);
            }

            var contentHashBumpTime          = TimeSpan.FromMinutes(distributedSettings.ContentHashBumpTimeMinutes);
            var lazyTouchContentHashBumpTime = distributedSettings.IsTouchEnabled ? (TimeSpan?)contentHashBumpTime : null;

            if (redisContentLocationStoreConfiguration.ReadMode == ContentLocationMode.LocalLocationStore)
            {
                // LocalLocationStore has its own internal notion of lazy touch/registration. We disable the lazy touch in distributed content store
                // because it can conflict with behavior of the local location store.
                lazyTouchContentHashBumpTime = null;
            }

            var distributedContentStoreSettings = new DistributedContentStoreSettings()
            {
                TrustedHashFileSizeBoundary     = distributedSettings.TrustedHashFileSizeBoundary,
                ParallelHashingFileSizeBoundary = distributedSettings.ParallelHashingFileSizeBoundary,
                MaxConcurrentCopyOperations     = distributedSettings.MaxConcurrentCopyOperations,
                PinConfiguration                      = pinConfiguration,
                RetryIntervalForCopies                = distributedSettings.RetryIntervalForCopies,
                MaxRetryCount                         = distributedSettings.MaxRetryCount,
                TimeoutForProactiveCopies             = TimeSpan.FromMinutes(distributedSettings.TimeoutForProactiveCopiesMinutes),
                ProactiveCopyMode                     = (ProactiveCopyMode)Enum.Parse(typeof(ProactiveCopyMode), distributedSettings.ProactiveCopyMode),
                PushProactiveCopies                   = distributedSettings.PushProactiveCopies,
                ProactiveCopyOnPut                    = distributedSettings.ProactiveCopyOnPut,
                ProactiveCopyOnPin                    = distributedSettings.ProactiveCopyOnPin,
                ProactiveCopyUsePreferredLocations    = distributedSettings.ProactiveCopyUsePreferredLocations,
                MaxConcurrentProactiveCopyOperations  = distributedSettings.MaxConcurrentProactiveCopyOperations,
                ProactiveCopyLocationsThreshold       = distributedSettings.ProactiveCopyLocationsThreshold,
                ProactiveCopyRejectOldContent         = distributedSettings.ProactiveCopyRejectOldContent,
                ReplicaCreditInMinutes                = distributedSettings.IsDistributedEvictionEnabled ? distributedSettings.ReplicaCreditInMinutes : null,
                EnableRepairHandling                  = distributedSettings.IsRepairHandlingEnabled,
                ContentHashBumpTime                   = lazyTouchContentHashBumpTime,
                LocationStoreBatchSize                = distributedSettings.RedisBatchPageSize,
                ContentAvailabilityGuarantee          = contentAvailabilityGuarantee,
                PrioritizeDesignatedLocationsOnCopies = distributedSettings.PrioritizeDesignatedLocationsOnCopies,
                RestrictedCopyReplicaCount            = distributedSettings.RestrictedCopyReplicaCount,
                CopyAttemptsWithRestrictedReplicas    = distributedSettings.CopyAttemptsWithRestrictedReplicas,
                AreBlobsSupported                     = redisContentLocationStoreConfiguration.AreBlobsSupported,
                MaxBlobSize = redisContentLocationStoreConfiguration.MaxBlobSize,
                DelayForProactiveReplication  = TimeSpan.FromSeconds(distributedSettings.ProactiveReplicationDelaySeconds),
                ProactiveReplicationCopyLimit = distributedSettings.ProactiveReplicationCopyLimit,
                EnableProactiveReplication    = distributedSettings.EnableProactiveReplication,
                TraceProactiveCopy            = distributedSettings.TraceProactiveCopy,
            };

            if (distributedSettings.EnableProactiveReplication && redisContentLocationStoreConfiguration.Checkpoint != null)
            {
                distributedContentStoreSettings.ProactiveReplicationInterval = redisContentLocationStoreConfiguration.Checkpoint.RestoreCheckpointInterval;
            }

            ApplyIfNotNull(distributedSettings.MaximumConcurrentPutAndPlaceFileOperations, v => distributedContentStoreSettings.MaximumConcurrentPutAndPlaceFileOperations = v);

            arguments.Overrides.Override(distributedContentStoreSettings);

            ConfigurationPrinter.TraceConfiguration(distributedContentStoreSettings, arguments.Logger);

            return(distributedContentStoreSettings);
        }
        internal static List <ResolvedNamedCacheSettings> ResolveCacheSettingsInPrecedenceOrder(DistributedCacheServiceArguments arguments)
        {
            var localCasSettings    = arguments.Configuration.LocalCasSettings;
            var cacheSettingsByName = localCasSettings.CacheSettingsByCacheName;

            Dictionary <string, string> cacheNamesByDrive =
                cacheSettingsByName.ToDictionary(x => Path.GetPathRoot(x.Value.CacheRootPath), x => x.Key, StringComparer.OrdinalIgnoreCase);

            var result = new List <ResolvedNamedCacheSettings>();

            void addCacheByName(string cacheName)
            {
                var cacheSettings         = cacheSettingsByName[cacheName];
                var resolvedCacheRootPath = localCasSettings.GetCacheRootPathWithScenario(cacheName);

                result.Add(
                    new ResolvedNamedCacheSettings(
                        name: cacheName,
                        settings: cacheSettings,
                        resolvedCacheRootPath: resolvedCacheRootPath,
                        machineLocation: arguments.PathTransformer.GetLocalMachineLocation(resolvedCacheRootPath)));
            }

            // Add caches specified in drive preference order
            foreach (var drive in localCasSettings.DrivePreferenceOrder)
            {
                if (cacheNamesByDrive.TryGetValue(drive, out var cacheName))
                {
                    addCacheByName(cacheName);
                    cacheNamesByDrive.Remove(drive);
                }
            }

            // Add remaining caches
            foreach (var cacheName in cacheNamesByDrive.Values)
            {
                addCacheByName(cacheName);
            }

            return(result);
        }
 protected virtual DistributedCacheServiceArguments ModifyArguments(DistributedCacheServiceArguments arguments) => arguments;