Example #1
0
 public void Save(ReactiveEntity entity)
 {
     SerializeExpression(entity.Expression);
     _serializer.Serialize(entity.Uri, _stream);
     _serializer.Serialize(entity.State, _stream);
     entity.Serialize(_serializer, _stream);
 }
                private bool TryGetCandidate(out ReactiveEntity entity, out bool complete)
                {
                    var stopwatch = Stopwatch.StartNew();

                    while (stopwatch.Elapsed < _quantum)
                    {
                        if (!_enumerator.MoveNext())
                        {
                            _traceSource.TemplateMigration_Completed(_queryEngine.Uri);

                            _enumerator.Dispose();

                            entity   = null;
                            complete = true;
                            return(false);
                        }

                        if (!_enumerator.Current.Expression.IsTemplatized())
                        {
                            entity   = _enumerator.Current;
                            complete = false;
                            return(true);
                        }
                    }

                    entity   = null;
                    complete = false;
                    return(false);
                }
                protected override bool OnRemove(ReactiveEntity entity)
                {
                    if (_parent.Parent.Options.GarbageCollectionEnabled && _parent.Parent.Options.GarbageCollectionSweepEnabled)
                    {
                        _parent._garbageCollector.Enqueue(entity);
                    }
                    else
                    {
                        _parent.RemoveEntity(entity);
                    }

                    return(true);
                }
            /// <summary>
            /// Try to invoke the specified <paramref name="action"/> and handle any exception that occurs.
            /// If an exception occurs and <paramref name="shouldMitigate"/> is true, the specified <paramref name="mitigator"/> is used to apply a mitigation.
            /// </summary>
            /// <param name="action">The action to try.</param>
            /// <param name="entity">The entity being loaded/mitigated.</param>
            /// <param name="shouldMitigate">true if errors should be mitigated; otherwise, false.</param>
            /// <param name="mitigator">The mitigator to use if the failed action should be mitigated.</param>
            private void TryMitigate(Action action, ReactiveEntity entity, bool shouldMitigate, ReactiveEntityRecoveryFailureMitigator mitigator)
            {
                try
                {
                    action();
                }
                catch (Exception ex)
                {
                    var error = new EntityLoadFailedException(entity.Uri, entity.Kind, ex);

                    if (shouldMitigate && Parent._engine.OnEntityLoadFailed(entity.Uri, entity, entity.Kind, error, out ReactiveEntityRecoveryFailureMitigation mitigation))
                    {
                        var trace = Parent.TraceSource;

                        trace.Mitigation_Execute(entity.Uri, mitigation);

#pragma warning disable IDE0079 // Remove unnecessary suppression.
#pragma warning disable CA1031  // Do not catch general exception types. (Protecting against engine unavailability; last resort mitigation with tracing of errors.)

                        try
                        {
                            if (!mitigator.DoMitigate(entity, mitigation))
                            {
                                trace.Mitigation_NotAccepted(mitigation, entity.Uri, ex);
                            }
                        }
                        catch (Exception mx)
                        {
                            trace.Mitigation_Failure(mitigation, entity.Uri, mx);
                        }

#pragma warning restore CA1031
#pragma warning restore IDE0079

                        // For this implementation, whenever a mitigation occurs, we will throw a
                        // bail out exception. This is to prevent further processing of the
                        // 'normal' recovery process from throwing additional errors that will
                        // trigger additional mitigations. An unfortunate consequence is that the
                        // mitigations must ensure all steps that would have occurred in the
                        // 'normal' recovery process occur in the mitigation (esp. for things
                        // mitigations like `Regenerate`. Right now, this is not a problem, as the
                        // steps being mitigated are late in the recovery process.
                        throw new MitigationBailOutException();
                    }

                    // This may result in a second call to the `EntityLoadFailed` event.
                    throw;
                }
            }
                /// <summary>
                /// Saves a definition.
                /// </summary>
                /// <param name="entity">The entity to save.</param>
                /// <param name="stream">The stream to save to.</param>
                protected virtual void SaveDefinition(ReactiveEntity entity, Stream stream)
                {
                    Debug.Assert(entity != null, "Entity should not be null.");
                    Debug.Assert(stream != null, "Stream should not be null.");

                    var policy = _engine.Parent._serializationPolicy;

                    using (entity.Measure(EntityMetric.SaveEntity))
                    {
                        using var definitionWriter = new EntityWriter(stream, policy);

                        definitionWriter.WriteHeader();
                        definitionWriter.Save(entity);
                    }
                }
            private void RemoveEntitySafe(ReactiveEntity entity)
            {
#pragma warning disable IDE0079 // Remove unnecessary suppression.
#pragma warning disable CA1031  // Do not catch general exception types. (Purpose of Safe variant.)

                try
                {
                    RemoveEntity(entity);
                }
                catch (Exception ex)
                {
                    Parent.TraceSource.FailSafe_Exception(nameof(RemoveEntity), ex);
                }

#pragma warning restore CA1031
#pragma warning restore IDE0079
            }
            private void RemoveEntity(ReactiveEntity entity)
            {
                var removed = true;
                var uri     = entity.Uri;
                var key     = uri.ToCanonicalString();

                switch (entity.Kind)
                {
                case ReactiveEntityKind.Observable:
                {
                    UndefineObservable(uri);
                    break;
                }

                case ReactiveEntityKind.Observer:
                {
                    UndefineObserver(uri);
                    break;
                }

                case ReactiveEntityKind.Stream:
                {
                    DeleteStream(uri);
                    break;
                }

                case ReactiveEntityKind.StreamFactory:
                {
                    UndefineSubjectFactory(uri);
                    break;
                }

                case ReactiveEntityKind.SubscriptionFactory:
                {
                    UndefineSubscriptionFactory(uri);
                    break;
                }

                case ReactiveEntityKind.Subscription:
                {
                    DeleteSubscription(uri);
                    break;
                }

                case ReactiveEntityKind.ReliableSubscription:
                {
                    DeleteReliableSubscription(uri);
                    break;
                }

                case ReactiveEntityKind.Other:
                {
                    removed = Registry.Other.TryRemove(key, out _);
                    break;
                }

                case ReactiveEntityKind.Template:
                {
                    removed = Registry.Templates.TryRemove(key, out _);
                    break;
                }

                case ReactiveEntityKind.None:
                default:
                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Cannot remove a placeholder ignore '{0}' of type '{1}'.", key, entity.Kind));
                }

                if (removed)
                {
                    Parent.TraceSource.Registry_RemoveEntity(entity.Kind.ToString(), key, Parent.Uri);
                }
            }
 protected override void OnAll(ReactiveEntity entity)
 {
     _parent.AddEntityPlaceholder(entity.Kind, entity.Uri.ToCanonicalString());
 }
 protected override bool OnRegenerate(ReactiveEntity entity)
 {
     _onRegenerate(entity);
     return(true);
 }
                /// <summary>
                /// Loads the definitions.
                /// </summary>
                /// <typeparam name="TEntity">The type of the entity.</typeparam>
                /// <param name="category">The category.</param>
                /// <param name="kind">Reactive entity kind.</param>
                /// <param name="onLoading">The function is called for each loading entity.</param>
                /// <param name="onError">Function to report an error.</param>
                /// <param name="blobLogger">The blob logger to write raw recovery blobs to.</param>
                /// <param name="token">Cancellation token</param>
                private void LoadDefinitions <TEntity>(
                    string category,
                    ReactiveEntityKind kind,
                    Action <TEntity> onLoading,
                    Action <string, TEntity, Exception> onError,
                    BlobLogger blobLogger,
                    CancellationToken token)
                    where TEntity : ReactiveEntity
                {
                    Debug.Assert(!string.IsNullOrEmpty(category), "Category should not be null or empty.");
                    Debug.Assert(onLoading != null, "onLoading should not be null.");

                    var trace = _engine.Parent.TraceSource;

                    trace.Recovery_LoadingDefinitionsStarted(_engine.Parent.Uri, category);

                    if (!_reader.TryGetItemKeys(category, out IEnumerable <string> entities))
                    {
                        entities = Array.Empty <string>();
                    }

                    var total  = 0;
                    var failed = 0;

                    Parallel.ForEach(
                        entities,
                        new ParallelOptions
                    {
                        MaxDegreeOfParallelism = _engine.Parent.Options.RecoveryDegreeOfParallelism,
                        TaskScheduler          = RecoveryScheduler.Default,
                        CancellationToken      = token,
                    },
                        key =>
                    {
                        var entity = default(TEntity);

                        try
                        {
                            var stopwatch = Stopwatch.StartNew();
                            if (!_reader.TryGetItemReader(category, key, out Stream stream))
                            {
                                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "No items in key '{0}' for category '{1}'.", key, category));
                            }
                            var elapsedReading = stopwatch.Elapsed;

                            blobLogger.Append(category, key, stream);

                            var policy = _engine.Parent._serializationPolicy;

                            stopwatch.Restart();
                            using (var reader = new EntityReader(stream, _engine.Registry, policy))
                            {
                                reader.ReadHeader();
                                _engine.TryMitigate(
                                    () => entity = (TEntity)reader.Load(kind),
                                    ReactiveEntity.CreateInvalidInstance(new Uri(key), kind),
                                    true,
                                    _placeholderMitigator);
                                reader.ReadFooter();
                            }
                            var elapsedLoading = stopwatch.Elapsed;

                            entity.SetMetric(EntityMetric.ReadEntity, elapsedReading);
                            entity.SetMetric(EntityMetric.LoadEntity, elapsedLoading);

                            onLoading(entity);
                        }
                        catch (MitigationBailOutException) { }
#pragma warning disable CA1031 // Do not catch general exception types. (By design; mitigation callback is the "handler".)
                        catch (Exception ex)
                        {
                            Interlocked.Increment(ref failed);

                            trace.Recovery_LoadingDefinitionsFailure(_engine.Parent.Uri, category, key, ex.Message);

                            onError(key, entity, ex);
                        }
#pragma warning restore CA1031

                        Interlocked.Increment(ref total);
                    });

                    token.ThrowIfCancellationRequested();

                    trace.Recovery_LoadingDefinitionsCompleted(_engine.Parent.Uri, category, total, failed);
                }
 public void Enqueue(ReactiveEntity entity)
 {
     _collectibleEntities.Enqueue(entity);
 }
 /// <summary>
 /// Predicate defining whether the definition should be included in the checkpoint.
 /// </summary>
 /// <param name="entity">The entity.</param>
 /// <returns>
 /// True if should be included.
 /// </returns>
 protected override bool ShouldSaveDefinition(ReactiveEntity entity) => true;
 /// <summary>
 /// Predicate defining whether the definition should be included in the checkpoint.
 /// </summary>
 /// <param name="entity">The entity.</param>
 /// <returns>
 /// True if should be included.
 /// </returns>
 protected override bool ShouldSaveDefinition(ReactiveEntity entity)
 {
     Debug.Assert(entity != null, "Entity should not be null.");
     return(!entity.IsPersisted);
 }
 /// <summary>
 /// Predicate defining whether the definition should be included in the checkpoint.
 /// </summary>
 /// <param name="entity">The entity.</param>
 /// <returns>True if should be included.</returns>
 protected abstract bool ShouldSaveDefinition(ReactiveEntity entity);