public void DisposeShouldNotTriggerCancellationForCancellableToken(bool cancellableToken) { var context = new Context(TestGlobal.Logger); // There is very important distinction between "cancellable" and "non-cancellable" tokens. // The old implementation was making the distinction when cts.Token.CanBeCanceled was true or not. // So we want to cover both cases here. // CancellationToken.None is not cancellable. var contextToken = cancellableToken ? new CancellationTokenSource().Token : CancellationToken.None; var operationContext = new OperationContext(context, contextToken); var secondaryTokenSource = new CancellationTokenSource(); bool scopeCancellationWasCalled = false; using (var scope = new CancellableOperationContext(operationContext, secondaryTokenSource.Token)) { using var registration = scope.Context.Token.Register( () => { scopeCancellationWasCalled = true; }); } scopeCancellationWasCalled.Should().BeFalse("The cancellation should not be triggered by CancellableOperationContext.Dispose"); }
/// <inheritdoc /> public async Task <Result <SerializedMetadataEntry> > GetContentHashListAsync( OperationContext context, StrongFingerprint strongFingerprint) { var key = GetKey(strongFingerprint.WeakFingerprint); byte[] selectorBytes = SerializeSelector(strongFingerprint.Selector, isReplacementToken: false); byte[] replacementTokenFieldName = SerializeSelector(strongFingerprint.Selector, isReplacementToken: true); var(primaryResult, secondaryResult) = await _redis.ExecuteRaidedAsync <SerializedMetadataEntry>( context, async (redis, cancellationToken) => { using var nestedContext = new CancellableOperationContext(context, cancellationToken); return(await redis.ExecuteBatchAsync( context, async batch => { var metadataBytesTask = batch.AddOperation(key, b => b.HashGetAsync(key, selectorBytes)); var replacementTokenTask = batch.AddOperation(key, b => b.HashGetAsync(key, replacementTokenFieldName)); return new SerializedMetadataEntry() { Data = await metadataBytesTask, ReplacementToken = await replacementTokenTask }; }, RedisOperation.GetContentHashList)); }, retryWindow : Configuration.SlowOperationCancellationTimeout); Contract.Assert(primaryResult != null || secondaryResult != null); return(primaryResult ?? secondaryResult); }
/// <inheritdoc /> protected override async Task <ContentHashListResult> GetContentHashListCoreAsync( OperationContext context, StrongFingerprint strongFingerprint, bool preferShared) { var key = GetKey(strongFingerprint.WeakFingerprint); using var selectorBytes = SerializeSelector(strongFingerprint.Selector, isReplacementToken: false); using var replacementTokenFieldName = SerializeSelector(strongFingerprint.Selector, isReplacementToken: true); var(primaryResult, secondaryResult) = await _redis.ExecuteRaidedAsync <(byte[], string)>( context, async (redis, cancellationToken) => { using var nestedContext = new CancellableOperationContext(context, cancellationToken); return(await redis.ExecuteBatchAsync( context, async batch => { var metadataBytesTask = batch.AddOperation(key, b => b.HashGetAsync(key, (ReadOnlyMemory <byte>)selectorBytes)); var replacementTokenTask = batch.AddOperation(key, b => b.HashGetAsync(key, (ReadOnlyMemory <byte>)replacementTokenFieldName)); return ((byte[])await metadataBytesTask, (string)await replacementTokenTask); }, RedisOperation.GetContentHashList)); }, retryWindow : Configuration.SlowOperationCancellationTimeout); Contract.Assert(primaryResult != null || secondaryResult != null); MetadataEntry?metadata = null; string replacementToken = null; if (primaryResult != null && primaryResult.Succeeded) { var(primaryMetadataBytes, primaryReplacementToken) = primaryResult.Value; if (primaryMetadataBytes != null) { metadata = DeserializeMetadataEntry(primaryMetadataBytes); replacementToken = primaryReplacementToken; } } if (secondaryResult != null && secondaryResult.Succeeded) { var(secondaryMetadataBytes, secondaryReplacementToken) = secondaryResult.Value; if (secondaryMetadataBytes != null) { var secondaryMetadata = DeserializeMetadataEntry(secondaryMetadataBytes); if ((metadata?.LastAccessTimeUtc ?? DateTime.MinValue) < secondaryMetadata.LastAccessTimeUtc) { metadata = secondaryMetadata; replacementToken = secondaryReplacementToken; } } } if (metadata == null) { return(new ContentHashListResult(contentHashListInfo: default, string.Empty));
private async Task <Result <bool> > CompareExchangeInternalAsync( OperationContext context, StrongFingerprint strongFingerprint, string expectedReplacementToken, ContentHashListWithDeterminism replacement, string newReplacementToken) { var key = GetKey(strongFingerprint.WeakFingerprint); var replacementMetadata = new MetadataEntry(replacement, DateTime.UtcNow); using var replacementBytes = SerializeMetadataEntry(replacementMetadata); using var selectorBytes = SerializeSelector(strongFingerprint.Selector, isReplacementToken: false); using var tokenFieldNameBytes = SerializeSelector(strongFingerprint.Selector, isReplacementToken: true); var(primaryResult, secondaryResult) = await _redis.ExecuteRaidedAsync <bool>( context, async (redis, cancellationToken) => { using var nestedContext = new CancellableOperationContext(context, cancellationToken); return(await redis.ExecuteBatchAsync( nestedContext, batch => { var task = batch.CompareExchangeAsync( key, (ReadOnlyMemory <byte>)selectorBytes, (ReadOnlyMemory <byte>)tokenFieldNameBytes, expectedReplacementToken, (ReadOnlyMemory <byte>)replacementBytes, newReplacementToken); batch.KeyExpireAsync(key, Configuration.ExpiryTime).FireAndForget(nestedContext); return task; }, RedisOperation.CompareExchange)); }, retryWindow : Configuration.SlowOperationCancellationTimeout); Contract.Assert(primaryResult != null || secondaryResult != null); if (primaryResult?.Succeeded == true || secondaryResult?.Succeeded == true) { // One of the operations is successful. return((primaryResult?.Value ?? false) || (secondaryResult?.Value ?? false)); } // All operations failed, propagating the error back to the caller. var failure = primaryResult ?? secondaryResult; Contract.Assert(!failure.Succeeded); return(new Result <bool>(failure)); }
/// <summary> /// Run the cache service verb. /// /// NOTE: Currently, this is highly reliant on being launched by the launcher. /// TODO: Add command line args with HostParameters and ServiceLifetime args so that /// this can be used standalone. /// </summary> public static async Task RunCacheServiceAsync( OperationContext context, string configurationPath, Func <HostParameters, DistributedCacheServiceConfiguration, CancellationToken, IDistributedCacheServiceHost> createHost, HostParameters hostParameters = null, bool requireServiceInterruptable = true) { try { hostParameters ??= HostParameters.FromEnvironment(); using var cancellableContext = new CancellableOperationContext(context, default(CancellationToken)); context = cancellableContext; var config = LoadAndWatchPreprocessedConfig <DistributedCacheServiceConfiguration, DistributedCacheServiceConfiguration>( context, configurationPath, hostParameters, out var configHash, c => c); await ServiceLifetimeManager.RunDeployedInterruptableServiceAsync(context, async token => { var hostInfo = new HostInfo(hostParameters.Stamp, hostParameters.Ring, new List <string>()); var host = createHost(hostParameters, config, token); await DistributedCacheServiceFacade.RunWithConfigurationAsync( logger: context.TracingContext.Logger, host: host, hostInfo: hostInfo, telemetryFieldsProvider: new HostTelemetryFieldsProvider(hostParameters) { ConfigurationId = configHash }, config, token: token);; return(BoolResult.Success); }, requireServiceInterruptionEnabled : requireServiceInterruptable).ThrowIfFailure(); } catch (Exception e) { Console.WriteLine(e); throw; } }
/// <inheritdoc /> public async Task <Result <LevelSelectors> > GetLevelSelectorsAsync(OperationContext context, Fingerprint weakFingerprint, int level) { var weakFingerprintKey = GetKey(weakFingerprint); var(primaryResult, secondaryResult) = await _redis.ExecuteRaidedAsync <IEnumerable <Selector> >(context, async (redis, cancellationToken) => { using var nestedContext = new CancellableOperationContext(context, cancellationToken); var keys = await redis.GetHashKeysAsync(nestedContext, weakFingerprintKey, nestedContext.Context.Token); return(keys.Select(r => DeserializeSelector(r)).Where(t => !t.isReplacementToken).Select(t => t.selector).ToList()); }, retryWindow : null); Contract.Assert(primaryResult != null || secondaryResult != null); var result = (primaryResult?.GetValueOrDefault() ?? EmptySelectors).Concat(secondaryResult?.GetValueOrDefault() ?? EmptySelectors).Distinct(); return(LevelSelectors.Single <List <Selector> >(result.ToList())); }
public void ScopeCancellationTriggersCancellationWhenSecondaryCancellationIsSet() { var context = new Context(TestGlobal.Logger); var contextTokenSource = new CancellationTokenSource(); var operationContext = new OperationContext(context, contextTokenSource.Token); var secondaryTokenSource = new CancellationTokenSource(); bool scopeCancellationWasCalled = false; using (var scope = new CancellableOperationContext(operationContext, secondaryTokenSource.Token)) { using var registration = scope.Context.Token.Register( () => { scopeCancellationWasCalled = true; }); secondaryTokenSource.Cancel(); } scopeCancellationWasCalled.Should().BeTrue(); }