Пример #1
0
 /// <nodoc />
 private CheckpointState(Role role, DateTime epochStartCursorTime, MachineLocation master)
     : this()
 {
     Role = role;
     StartSequencePoint = new EventSequencePoint(epochStartCursorTime);
     Master             = master;
 }
Пример #2
0
        /// <summary>
        /// Creates a checkpoint for a given sequence point.
        /// </summary>
        public Task <BoolResult> CreateCheckpointAsync(OperationContext context, EventSequencePoint sequencePoint)
        {
            context = context.CreateNested();
            return(context.PerformOperationAsync(
                       _tracer,
                       async() =>
            {
                bool successfullyUpdatedIncrementalState = false;
                try
                {
                    // Creating a working temporary directory
                    using (new DisposableDirectory(_fileSystem, _checkpointStagingDirectory))
                    {
                        // Saving checkpoint for the database into the temporary folder
                        _database.SaveCheckpoint(context, _checkpointStagingDirectory).ThrowIfFailure();

                        if (_configuration.UseIncrementalCheckpointing)
                        {
                            successfullyUpdatedIncrementalState = await CreateCheckpointIncrementalAsync(context, sequencePoint, successfullyUpdatedIncrementalState);
                        }
                        else
                        {
                            await CreateFullCheckpointAsync(context, sequencePoint);
                        }

                        return BoolResult.Success;
                    }
                }
                finally
                {
                    ClearIncrementalCheckpointStateIfNeeded(successfullyUpdatedIncrementalState);
                }
            }));
        }
Пример #3
0
        public CheckpointState CreateCheckpointState(IClock clock)
        {
            var index         = _index++;
            var checkpointId  = "chkpt" + _index;
            var sequencePoint = new EventSequencePoint(sequenceNumber: index);

            return(new CheckpointState(sequencePoint, checkpointId, clock.UtcNow, M1));
        }
Пример #4
0
        /// <nodoc />
        public CheckpointState(Role role, EventSequencePoint startSequencePoint, string checkpointId, DateTime checkpointTime)
        {
            Contract.Requires(!string.IsNullOrEmpty(checkpointId));

            Role = role;
            StartSequencePoint = startSequencePoint;
            CheckpointId       = checkpointId;
            CheckpointTime     = checkpointTime;
        }
Пример #5
0
        /// <nodoc />
        public CheckpointState(Role role, EventSequencePoint startSequencePoint, string checkpointId, DateTime checkpointTime, MachineLocation producer)
        {
            Contract.Requires(!string.IsNullOrEmpty(checkpointId));

            Role = role;
            StartSequencePoint = startSequencePoint;
            CheckpointId       = checkpointId;
            CheckpointTime     = checkpointTime;
            Producer           = producer;
        }
Пример #6
0
 private bool IsBefore(EventData eventData, EventSequencePoint sequencePoint)
 {
     if (sequencePoint.SequenceNumber != null)
     {
         return(eventData.SystemProperties.SequenceNumber < sequencePoint.SequenceNumber.Value);
     }
     else
     {
         return(eventData.SystemProperties.EnqueuedTimeUtc < sequencePoint.EventStartCursorTimeUtc.Value);
     }
 }
Пример #7
0
        /// <summary>
        /// Creates a checkpoint for a given sequence point.
        /// </summary>
        public Task <BoolResult> CreateCheckpointAsync(OperationContext context, EventSequencePoint sequencePoint)
        {
            context = context.CreateNested();

            string checkpointId   = "Unknown";
            long   checkpointSize = 0;

            return(context.PerformOperationAsync(
                       _tracer,
                       async() =>
            {
                bool successfullyUpdatedIncrementalState = false;
                try
                {
                    // Creating a working temporary directory
                    using (new DisposableDirectory(_fileSystem, _checkpointStagingDirectory))
                    {
                        // NOTE(jubayard): this needs to be done previous to checkpointing, because we always
                        // fetch the latest version's size in this way. This implies there may be some difference
                        // between the reported value and the actual size on disk: updates will get in in-between.
                        // The better alternative is to actually open the checkpoint and ask, but it seems like too
                        // much.
                        checkpointSize = _database.GetContentDatabaseSizeBytes().GetValueOrDefault(-1);


                        // Saving checkpoint for the database into the temporary folder
                        _database.SaveCheckpoint(context, _checkpointStagingDirectory).ThrowIfFailure();

                        if (_configuration.UseIncrementalCheckpointing)
                        {
                            checkpointId = await CreateCheckpointIncrementalAsync(context, sequencePoint);
                            successfullyUpdatedIncrementalState = true;
                        }
                        else
                        {
                            checkpointId = await CreateFullCheckpointAsync(context, sequencePoint);
                        }

                        return BoolResult.Success;
                    }
                }
                finally
                {
                    ClearIncrementalCheckpointStateIfNeeded(context, successfullyUpdatedIncrementalState);
                }
            },
                       extraStartMessage: $"SequencePoint=[{sequencePoint}]",
                       extraEndMessage: result => $"SequencePoint=[{sequencePoint}] Id=[{checkpointId}] SizeMb=[{(checkpointSize < 0 ? checkpointSize:checkpointSize*1e-6)}]"));
        }
Пример #8
0
        /// <inheritdoc />
        public BoolResult StartProcessing(OperationContext context, EventSequencePoint sequencePoint, IPartitionReceiveHandler processor)
        {
            using (_lock.AcquireWriteLock())
            {
                _handler = ev => Dispatch(ev, processor);
                var events = _hub.SubscribeAndGetEventsStartingAtSequencePoint(sequencePoint, _handler);

                foreach (var eventData in events)
                {
                    _handler(eventData);
                }
            }

            return(BoolResult.Success);
        }
Пример #9
0
        /// <inheritdoc />
        protected override BoolResult DoStartProcessing(OperationContext context, EventSequencePoint sequencePoint)
        {
            using (_lock.AcquireWriteLock())
            {
                _processing = true;

                while (_queue.TryTake(out var eventData))
                {
                    DispatchAsync(context, eventData).GetAwaiter().GetResult();
                    Interlocked.Increment(ref _sequenceNumber);
                }
            }

            return(BoolResult.Success);
        }
        public Task ReconciliationOverRealStorage()
        {
            var checkpointsKey = Guid.NewGuid().ToString();
            // Copy and paste a real connection string here.
            var storageConnectionString = string.Empty;
            // Consider updating this directory if you want to keep data between invocations.
            var workingDirectory = TestRootDirectoryPath;
            var configuration    = new LocalDiskCentralStoreConfiguration(
                workingDirectory,
                checkpointsKey);
            var blobStoreConfiguration = new BlobCentralStoreConfiguration(
                credentials: new AzureBlobStorageCredentials(storageConnectionString),
                containerName: "checkpoints",
                checkpointsKey: checkpointsKey);
            var producerMachineLocation = new MachineLocation();

            ConfigureWithOneMaster(s =>
            {
                s.ReconcileMode          = ReconciliationMode.Once.ToString();
                s.AzureStorageSecretName = Host.StoreSecret("StorageName", storageConnectionString);
            });

            return(RunTestAsync(
                       new Context(Logger),
                       2,
                       async context =>
            {
                var master = context.GetMaster();
                var worker = context.GetFirstWorker();
                var workerId = worker.LocalLocationStore.ClusterState.PrimaryMachineId;

                var workerSession = context.Sessions[context.GetFirstWorkerIndex()];

                var checkpointState = new CheckpointState(
                    Role.Worker,
                    EventSequencePoint.Parse("24382354"),
                    "MD5:8C4856EA13F6AD59B65D8F6781D2A2F9||DCS||incrementalCheckpoints/24382354.10a0ca0f-d63f-4992-a088-f67bd00abd8a.checkpointInfo.txt|Incremental",
                    DateTime.Now,
                    producerMachineLocation,
                    producerMachineLocation);
                // Next heartbeat workers to restore checkpoint
                await worker.LocalLocationStore.ProcessStateAsync(new OperationContext(context), checkpointState, inline: true, forceRestore: true).ShouldBeSuccess();
                var reconcileResult = await worker.ReconcileAsync(context).ShouldBeSuccess();
                Output.WriteLine($"Reconcile result: {reconcileResult}");
            }));
        }
Пример #11
0
        private async Task CreateFullCheckpointAsync(OperationContext context, EventSequencePoint sequencePoint)
        {
            // Zipping the checkpoint
            var targetZipFile = _checkpointStagingDirectory + ".zip";

            File.Delete(targetZipFile);
            ZipFile.CreateFromDirectory(_checkpointStagingDirectory.ToString(), targetZipFile);

            // Track checkpoint size
            var fileInfo = new System.IO.FileInfo(targetZipFile);

            _tracer.TrackMetric(context, CheckpointSizeMetricName, fileInfo.Length);

            var checkpointBlobName = $"checkpoints/{sequencePoint.SequenceNumber}.{Guid.NewGuid()}.zip";
            var checkpointId       = await _storage.UploadFileAsync(context, new AbsolutePath(targetZipFile), checkpointBlobName, garbageCollect : true).ThrowIfFailureAsync();

            // Uploading the checkpoint
            await _checkpointRegistry.RegisterCheckpointAsync(context, checkpointId, sequencePoint).ThrowIfFailure();
        }
Пример #12
0
        public Task ShutdownDoesNotProcessEnqueuedEvents()
        {
            // This test is "weird" in the sense that it relies on a "controlled" race condition. We need to check that
            // the master does not wait for events to finish processing to complete its shutdown. The way we do this is
            // force an arbitrary delay when processing events, and then make sure that we haven't processed any after
            // shutdown.
            //
            // * If we do it with a cancellation token on shutdown started, then we have a race condition, because the
            //   token will be triggered, and the item could be processed before shutdown finishes.
            // * If we do it with a separate lock, we'll need some sort of delay which will be equivalent to what we do
            //   now.
            // * We can't use a cancellation token on shutdown finished, because shutdown awaits for the action blocks
            //   to complete, and this means we have a deadlock.
            //
            // By doing things this way, we are relying on the fact that it is unlikely for a thread to not run for a
            // second. If we assume that's true, then the slowdown will resume after shutdown has started waiting, and
            // we will do the appropriate fast-return path.
            var configuration = new SlowedContentLocationEventStoreConfiguration()
            {
                Slowdown = TimeSpan.FromSeconds(1)
            };

            var eventHandler = new TestEventHandler();

            return(WithContentLocationEventStore(async(tracingContext, clock, fileSystem, eventStore) =>
            {
                var context = new OperationContext(tracingContext);
                var sequencePoint = new EventSequencePoint(clock.UtcNow);

                eventStore.StartProcessing(context, sequencePoint).ShouldBeSuccess();

                eventStore.AddLocations(context, MachineId.FromIndex(0), new[] { new ContentHashWithSize(ContentHash.Random(), 1) }).ShouldBeSuccess();

                (await eventStore.ShutdownAsync(context)).ShouldBeSuccess();

                eventHandler.EventsHandled.Should().Be(0);
            }, configuration, eventHandler));
        }
Пример #13
0
        public Task <BoolResult> RegisterCheckpointAsync(OperationContext context, string checkpointId, EventSequencePoint sequencePoint)
        {
            return(context.PerformOperationWithTimeoutAsync(
                       Tracer,
                       async nestedContext =>
            {
                Contract.Assert(sequencePoint.SequenceNumber != null);

                var checkpoint = new RedisCheckpointInfo(checkpointId, sequencePoint.SequenceNumber.Value, _clock.UtcNow, Configuration.PrimaryMachineLocation.ToString());
                Tracer.Debug(nestedContext, $"Saving checkpoint '{checkpoint}' into the central store.");

                var slotNumber = await _checkpointsKey.UseNonConcurrentReplicatedHashAsync(
                    nestedContext,
                    Configuration.RetryWindow,
                    RedisOperation.UploadCheckpoint,
                    (batch, key) => batch.AddCheckpointAsync(key, checkpoint, MaxCheckpointSlotCount),
                    timeout: Configuration.ClusterRedisOperationTimeout)
                                 .ThrowIfFailureAsync();

                Tracer.Debug(nestedContext, $"Saved checkpoint into slot '{slotNumber}'.");
                return BoolResult.Success;
            },
                       Counters[GlobalStoreCounters.RegisterCheckpoint],
                       timeout: Configuration.ClusterRedisOperationTimeout));
        }
Пример #14
0
        /// <summary>
        /// Creates a checkpoint for a given sequence point.
        /// </summary>
        public Task <BoolResult> CreateCheckpointAsync(OperationContext context, EventSequencePoint sequencePoint)
        {
            context = context.CreateNested(nameof(CheckpointManager));

            string checkpointId = "Unknown";
            double contentColumnFamilySizeMb  = -1;
            double contentDataSizeMb          = -1;
            double metadataColumnFamilySizeMb = -1;
            double metadataDataSizeMb         = -1;
            double sizeOnDiskMb = -1;

            return(context.PerformOperationAsync(
                       _tracer,
                       async() =>
            {
                bool successfullyUpdatedIncrementalState = false;
                try
                {
                    // Creating a working temporary directory
                    using (new DisposableDirectory(_fileSystem, _checkpointStagingDirectory))
                    {
                        // Write out the time this checkpoint was generated to the database. This will be used by
                        // the workers in order to determine whether they should restore or not after restart. The
                        // checkpoint id is generated inside the upload methods, so we only generate the guid here.
                        // Since this is only used for reporting purposes, there's no harm in it.
                        var checkpointGuid = Guid.NewGuid();
                        DatabaseWriteCheckpointCreationTime(context, checkpointGuid.ToString(), DateTime.UtcNow);

                        // NOTE(jubayard): this needs to be done previous to checkpointing, because we always
                        // fetch the latest version's size in this way. This implies there may be some difference
                        // between the reported value and the actual size on disk: updates will get in in-between.
                        // The better alternative is to actually open the checkpoint and ask, but it seems like too
                        // much.
                        if (_database is RocksDbContentLocationDatabase rocksDb)
                        {
                            contentColumnFamilySizeMb = rocksDb.GetLongProperty(
                                RocksDbContentLocationDatabase.LongProperty.LiveFilesSizeBytes,
                                RocksDbContentLocationDatabase.Entity.ContentTracking).Select(x => x * 1e-6).GetValueOrDefault(-1);

                            contentDataSizeMb = rocksDb.GetLongProperty(
                                RocksDbContentLocationDatabase.LongProperty.LiveDataSizeBytes,
                                RocksDbContentLocationDatabase.Entity.ContentTracking).Select(x => x * 1e-6).GetValueOrDefault(-1);

                            metadataColumnFamilySizeMb = rocksDb.GetLongProperty(
                                RocksDbContentLocationDatabase.LongProperty.LiveFilesSizeBytes,
                                RocksDbContentLocationDatabase.Entity.Metadata).Select(x => x * 1e-6).GetValueOrDefault(-1);

                            metadataDataSizeMb = rocksDb.GetLongProperty(
                                RocksDbContentLocationDatabase.LongProperty.LiveDataSizeBytes,
                                RocksDbContentLocationDatabase.Entity.Metadata).Select(x => x * 1e-6).GetValueOrDefault(-1);
                        }

                        // Saving checkpoint for the database into the temporary folder
                        _database.SaveCheckpoint(context, _checkpointStagingDirectory).ThrowIfFailure();

                        try
                        {
                            sizeOnDiskMb = _fileSystem
                                           .EnumerateFiles(_checkpointStagingDirectory, EnumerateOptions.Recurse)
                                           .Sum(fileInfo => fileInfo.Length) * 1e-6;
                        }
                        catch (IOException e)
                        {
                            _tracer.Error(context, $"Error counting size of checkpoint's staging directory `{_checkpointStagingDirectory}`: {e}");
                        }

                        if (_configuration.UseIncrementalCheckpointing)
                        {
                            checkpointId = await CreateCheckpointIncrementalAsync(context, sequencePoint, checkpointGuid);
                            successfullyUpdatedIncrementalState = true;
                        }
                        else
                        {
                            checkpointId = await CreateFullCheckpointAsync(context, sequencePoint, checkpointGuid);
                        }

                        return BoolResult.Success;
                    }
                }
                finally
                {
                    ClearIncrementalCheckpointStateIfNeeded(context, successfullyUpdatedIncrementalState);
                }
            },
Пример #15
0
        private async Task <string> CreateCheckpointIncrementalAsync(OperationContext context, EventSequencePoint sequencePoint)
        {
            InitializeIncrementalCheckpointIfNeeded(restoring: false);

            var incrementalCheckpointsPrefix = $"incrementalCheckpoints/{sequencePoint.SequenceNumber}.{Guid.NewGuid()}.";
            // See the comment of _incrementalCheckpointInfo for the meaning of keys and values.
            var newCheckpointInfo = new ConcurrentDictionary <string, string>(StringComparer.OrdinalIgnoreCase);

            // Get files in checkpoint and apply changes to the incremental checkpoint directory (locally and in blob storage)
            var files = _fileSystem.EnumerateFiles(_checkpointStagingDirectory, EnumerateOptions.Recurse).Select(s => s.FullPath).ToList();

            await UploadFilesAsync(context, files, newCheckpointInfo, incrementalCheckpointsPrefix);

            // Finalize by writing the checkpoint info into the incremental checkpoint directory and updating checkpoint registry and storage
            WriteCheckpointInfo(_incrementalCheckpointInfoFile, newCheckpointInfo);

            var checkpointId = await _storage.UploadFileAsync(context, _incrementalCheckpointInfoFile, incrementalCheckpointsPrefix + _incrementalCheckpointInfoFile.FileName, garbageCollect : true).ThrowIfFailureAsync();

            // Add incremental suffix so consumer knows that the checkpoint is an incremental checkpoint
            checkpointId += IncrementalCheckpointIdSuffix;

            await _checkpointRegistry.RegisterCheckpointAsync(context, checkpointId, sequencePoint).ThrowIfFailure();

            // Have to create a dictionary in .NET 4.5.1 because ConcurrentDictionary does not implement IReadOnlyDictionary there.
            UpdateIncrementalCheckpointInfo(new Dictionary <string, string>(newCheckpointInfo));

            return(checkpointId);
        }
Пример #16
0
        public Task <BoolResult> RegisterCheckpointAsync(OperationContext context, string checkpointId, EventSequencePoint sequencePoint)
        {
            return(context.PerformOperationAsync(
                       Tracer,
                       () =>
            {
                Contract.Assert(sequencePoint.SequenceNumber != null);

                var checkpoint = new RedisCheckpointInfo(checkpointId, sequencePoint.SequenceNumber.Value, _clock.UtcNow, LocalMachineLocation.ToString());
                Tracer.Debug(context, $"Saving checkpoint '{checkpoint}' into the central store.");

                return ExecuteRedisAsync(context, async redisDb =>
                {
                    var slotNumber = await redisDb.ExecuteBatchAsync(context, batch =>
                    {
                        return batch.AddCheckpointAsync(_checkpointsKey, checkpoint, MaxCheckpointSlotCount);
                    }, RedisOperation.UploadCheckpoint);

                    Tracer.Debug(context, $"Saved checkpoint into slot '{slotNumber}' on {GetDbName(redisDb)}.");
                    return BoolResult.Success;
                });
            },
                       Counters[GlobalStoreCounters.RegisterCheckpoint]));
        }
Пример #17
0
 /// <nodoc />
 private CheckpointState(Role role, DateTime epochStartCursorTime)
     : this()
 {
     Role = role;
     StartSequencePoint = new EventSequencePoint(epochStartCursorTime);
 }
Пример #18
0
        private async Task <bool> CreateCheckpointIncrementalAsync(OperationContext context, EventSequencePoint sequencePoint, bool successfullyUpdatedIncrementalState)
        {
            InitializeIncrementalCheckpointIfNeeded();

            BoolResult result = BoolResult.Success;
            var        incrementalCheckpointsPrefix = $"incrementalCheckpoints/{sequencePoint.SequenceNumber}.{Guid.NewGuid()}.";
            var        newCheckpointInfo            = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);

            // Get files in checkpoint and apply changes to the incremental checkpoint directory (locally and in blob storage)
            var files = _fileSystem.EnumerateFiles(_checkpointStagingDirectory, EnumerateOptions.Recurse).Select(s => s.FullPath).ToList();

            foreach (var file in files)
            {
                var relativePath = file.Path.Substring(_checkpointStagingDirectory.Path.Length + 1);
                var incrementalCheckpointFile = _incrementalCheckpointDirectory / relativePath;
                if (_incrementalCheckpointInfo.TryGetValue(relativePath, out var storageId) && _database.IsImmutable(file) && _fileSystem.FileExists(incrementalCheckpointFile))
                {
                    // File was present in last checkpoint. Just add it to the new incremental checkpoint info
                    await _storage.TouchBlobAsync(context, file, storageId, isUploader : true).ThrowIfFailure();

                    newCheckpointInfo[relativePath] = storageId;
                    Counters[ContentLocationStoreCounters.IncrementalCheckpointFilesUploadSkipped].Increment();
                }
                else
                {
                    // File is new or mutable. Need to add to storage and update local incremental checkpoint
                    await HardlinkWithFallBackAsync(context, file, incrementalCheckpointFile);

                    storageId = await _storage.UploadFileAsync(context, file, incrementalCheckpointsPrefix + file.FileName).ThrowIfFailureAsync();

                    newCheckpointInfo[relativePath] = storageId;
                    Counters[ContentLocationStoreCounters.IncrementalCheckpointFilesUploaded].Increment();
                }
            }

            // Finalize by writing the checkpoint info into the incremental checkpoint directory and updating checkpoint registry and storage
            WriteCheckpointInfo(_incrementalCheckpointInfoFile, newCheckpointInfo);

            var checkpointId = await _storage.UploadFileAsync(context, _incrementalCheckpointInfoFile, incrementalCheckpointsPrefix + _incrementalCheckpointInfoFile.FileName, garbageCollect : true).ThrowIfFailureAsync();

            // Add incremental suffix so consumer knows that the checkpoint is an incremental checkpoint
            checkpointId += IncrementalCheckpointIdSuffix;

            await _checkpointRegistry.RegisterCheckpointAsync(context, checkpointId, sequencePoint).ThrowIfFailure();

            UpdateIncrementalCheckpointInfo(newCheckpointInfo);
            successfullyUpdatedIncrementalState = true;
            return(successfullyUpdatedIncrementalState);
        }
Пример #19
0
 internal IReadOnlyList <EventData> SubscribeAndGetEventsStartingAtSequencePoint(EventSequencePoint sequencePoint, Action <EventData> handler)
 {
     lock (_syncLock)
     {
         OnEvent += handler;
         return(_eventStream.SkipWhile(eventData => IsBefore(eventData, sequencePoint)).ToArray());
     }
 }