public void Processes_Instructions_Only_Once()
        {
            // This test shows what's happening in issue #10112
            // The DatabaseServerMessenger will run its sync operation every five seconds which calls CacheInstructionService.ProcessInstructions,
            // which is why the CacheRefresherNotification keeps dispatching, because the cache instructions gets constantly processed.
            var sut = (CacheInstructionService)GetRequiredService <ICacheInstructionService>();

            CreateAndDeliveryMultipleInstructions(sut);

            var lastId = -1;
            // Run once
            ProcessInstructionsResult result = sut.ProcessInstructions(CacheRefreshers, ServerRoleAccessor.CurrentServerRole, CancellationToken, LocalIdentity, DateTime.UtcNow.AddSeconds(-1), lastId);

            Assert.Multiple(() =>
            {
                Assert.AreEqual(3, result.LastId);                          // 3 records found.
                Assert.AreEqual(2, result.NumberOfInstructionsProcessed);   // 2 records processed (as one is for the same identity).
                Assert.IsFalse(result.InstructionsWerePruned);
            });

            // DatabaseServerMessenger stores the LastID after ProcessInstructions has been run.
            lastId = result.LastId;

            // The instructions has now been processed and shouldn't be processed on the next call...
            // Run again.
            ProcessInstructionsResult secondResult = sut.ProcessInstructions(CacheRefreshers, ServerRoleAccessor.CurrentServerRole, CancellationToken, LocalIdentity, DateTime.UtcNow.AddSeconds(-1), lastId);

            Assert.Multiple(() =>
            {
                Assert.AreEqual(0, secondResult.LastId);                          // No instructions was processed so LastId is 0, this is consistent with behavior from V8
                Assert.AreEqual(0, secondResult.NumberOfInstructionsProcessed);   // Nothing was processed.
                Assert.IsFalse(secondResult.InstructionsWerePruned);
            });
        }
        public void Correct_ID_For_Instruction_With_Same_Identity()
        {
            var sut = (CacheInstructionService)GetRequiredService <ICacheInstructionService>();

            CreateAndDeliveryMultipleInstructions(sut);

            var lastId = -1;
            ProcessInstructionsResult result = sut.ProcessInstructions(CacheRefreshers, ServerRoleAccessor.CurrentServerRole, CancellationToken, LocalIdentity, DateTime.UtcNow.AddSeconds(-1), lastId);

            Assert.AreEqual(3, result.LastId); // Make sure LastId is 3, the rest is tested in other test.
            lastId = result.LastId;

            // Add new instruction
            List <RefreshInstruction> instructions = CreateInstructions();

            sut.DeliverInstructions(instructions, LocalIdentity);

            ProcessInstructionsResult secondResult = sut.ProcessInstructions(CacheRefreshers, ServerRoleAccessor.CurrentServerRole, CancellationToken, LocalIdentity, DateTime.UtcNow.AddSeconds(-1), lastId);

            Assert.Multiple(() =>
            {
                Assert.AreEqual(4, secondResult.LastId);
                Assert.AreEqual(0, secondResult.NumberOfInstructionsProcessed);
                Assert.IsFalse(secondResult.InstructionsWerePruned);
            });
        }
        public void Can_Process_And_Purge_Instructions()
        {
            // Purging of instructions only occurs on single or master servers, so we need to ensure this is set before running the test.
            EnsureServerRegistered();
            var sut = (CacheInstructionService)GetRequiredService <ICacheInstructionService>();

            CreateAndDeliveryMultipleInstructions(sut);

            ProcessInstructionsResult result = sut.ProcessInstructions(CacheRefreshers, ServerRoleAccessor.CurrentServerRole, CancellationToken, LocalIdentity, DateTime.UtcNow.AddHours(-1), -1);

            Assert.IsTrue(result.InstructionsWerePruned);
        }
        public void Can_Process_Instructions()
        {
            var sut = (CacheInstructionService)GetRequiredService <ICacheInstructionService>();

            // Create three instruction records, each with two instructions.  First two records are for a different identity.
            CreateAndDeliveryMultipleInstructions(sut);

            ProcessInstructionsResult result = sut.ProcessInstructions(CacheRefreshers, ServerRoleAccessor.CurrentServerRole, CancellationToken, LocalIdentity, DateTime.UtcNow.AddSeconds(-1), -1);

            Assert.Multiple(() =>
            {
                Assert.AreEqual(3, result.LastId);                          // 3 records found.
                Assert.AreEqual(2, result.NumberOfInstructionsProcessed);   // 2 records processed (as one is for the same identity).
                Assert.IsFalse(result.InstructionsWerePruned);
            });
        }
        public void Processes_No_Instructions_When_CancellationToken_is_Cancelled()
        {
            var sut = (CacheInstructionService)GetRequiredService <ICacheInstructionService>();

            CreateAndDeliveryMultipleInstructions(sut);

            var cancellationTokenSource = new CancellationTokenSource();

            cancellationTokenSource.Cancel();

            ProcessInstructionsResult result = sut.ProcessInstructions(CacheRefreshers, ServerRoleAccessor.CurrentServerRole, cancellationTokenSource.Token, LocalIdentity, DateTime.UtcNow.AddSeconds(-1), -1);

            Assert.Multiple(() =>
            {
                Assert.AreEqual(0, result.LastId);
                Assert.AreEqual(0, result.NumberOfInstructionsProcessed);
                Assert.IsFalse(result.InstructionsWerePruned);
            });
        }
Example #6
0
            /// <inheritdoc/>
            public ProcessInstructionsResult ProcessInstructions(
                CacheRefresherCollection cacheRefreshers,
                ServerRole serverRole,
                CancellationToken cancellationToken,
                string localIdentity,
                DateTime lastPruned,
                int lastId)
            {
                using (_profilingLogger.DebugDuration <CacheInstructionService>("Syncing from database..."))
                    using (ICoreScope scope = ScopeProvider.CreateCoreScope())
                    {
                        var numberOfInstructionsProcessed = ProcessDatabaseInstructions(cacheRefreshers, cancellationToken,
                                                                                        localIdentity, ref lastId);

                        // Check for pruning throttling.
                        if (cancellationToken.IsCancellationRequested || (DateTime.UtcNow - lastPruned) <=
                            _globalSettings.DatabaseServerMessenger.TimeBetweenPruneOperations)
                        {
                            scope.Complete();
                            return(ProcessInstructionsResult.AsCompleted(numberOfInstructionsProcessed, lastId));
                        }

                        var instructionsWerePruned = false;
                        switch (serverRole)
                        {
                        case ServerRole.Single:
                        case ServerRole.SchedulingPublisher:
                            PruneOldInstructions();
                            instructionsWerePruned = true;
                            break;
                        }

                        scope.Complete();

                        return(instructionsWerePruned
                        ? ProcessInstructionsResult.AsCompletedAndPruned(numberOfInstructionsProcessed, lastId)
                        : ProcessInstructionsResult.AsCompleted(numberOfInstructionsProcessed, lastId));
                    }
            }
        /// <summary>
        /// Synchronize the server (throttled).
        /// </summary>
        public override void Sync()
        {
            if (!EnsureInitialized())
            {
                return;
            }

            lock (_locko)
            {
                if (_syncing)
                {
                    return;
                }

                // Don't continue if we are released
                if (_cancellationToken.IsCancellationRequested)
                {
                    return;
                }

                if ((DateTime.UtcNow - _lastSync) <= GlobalSettings.DatabaseServerMessenger.TimeBetweenSyncOperations)
                {
                    return;
                }

                // Set our flag and the lock to be in it's original state (i.e. it can be awaited)
                _syncing = true;
                _syncIdle.Reset();
                _lastSync = DateTime.UtcNow;
            }

            try
            {
                ProcessInstructionsResult result = CacheInstructionService.ProcessInstructions(
                    _cacheRefreshers,
                    _serverRoleAccessor.CurrentServerRole,
                    _cancellationToken,
                    LocalIdentity,
                    _lastPruned,
                    _lastSyncedFileManager.LastSyncedId);

                if (result.InstructionsWerePruned)
                {
                    _lastPruned = _lastSync;
                }

                if (result.LastId > 0)
                {
                    _lastSyncedFileManager.SaveLastSyncedId(result.LastId);
                }
            }
            finally
            {
                lock (_locko)
                {
                    // We must reset our flag and signal any waiting locks
                    _syncing = false;
                }

                _syncIdle.Set();
            }
        }