// <summary>
        /// Initializes a server that has never synchronized before.
        /// </summary>
        /// <remarks>
        /// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded.
        /// Callers MUST ensure thread-safety.
        /// </remarks>
        private SyncBootState InitializeColdBootState()
        {
            lock (_locko)
            {
                if (_cancellationToken.IsCancellationRequested)
                {
                    return(SyncBootState.Unknown);
                }

                SyncBootState syncState = _syncBootStateAccessor.GetSyncBootState();

                if (syncState == SyncBootState.ColdBoot)
                {
                    // Get the last id in the db and store it.
                    // Note: Do it BEFORE initializing otherwise some instructions might get lost
                    // when doing it before. Some instructions might run twice but this is not an issue.
                    var maxId = CacheInstructionService.GetMaxInstructionId();

                    // if there is a max currently, or if we've never synced
                    if (maxId > 0 || _lastSyncedFileManager.LastSyncedId < 0)
                    {
                        _lastSyncedFileManager.SaveLastSyncedId(maxId);
                    }
                }

                return(syncState);
            }
        }
 private void CreateAndDeliveryMultipleInstructions(CacheInstructionService sut)
 {
     for (int i = 0; i < 3; i++)
     {
         List <RefreshInstruction> instructions = CreateInstructions();
         sut.DeliverInstructions(instructions, i == 2 ? LocalIdentity : AlternateIdentity);
     }
 }
        /// <inheritdoc/>
        public override void SendMessages()
        {
            ICollection <RefreshInstructionEnvelope> batch = GetBatch(false);

            if (batch == null)
            {
                return;
            }

            RefreshInstruction[] instructions = batch.SelectMany(x => x.Instructions).ToArray();
            batch.Clear();

            CacheInstructionService.DeliverInstructionsInBatches(instructions, LocalIdentity);
        }
        protected override void DeliverRemote(
            ICacheRefresher refresher,
            MessageType messageType,
            IEnumerable <object> ids = null,
            string json = null)
        {
            var idsA = ids?.ToArray();

            if (GetArrayType(idsA, out Type idType) == false)
            {
                throw new ArgumentException("All items must be of the same type, either int or Guid.", nameof(ids));
            }

            IEnumerable <RefreshInstruction> instructions = RefreshInstruction.GetInstructions(refresher, JsonSerializer, messageType, idsA, idType, json);

            CacheInstructionService.DeliverInstructions(instructions, LocalIdentity);
        }
        private void BatchMessage(
            ICacheRefresher refresher,
            MessageType messageType,
            IEnumerable <object> ids = null,
            Type idType = null,
            string json = null)
        {
            ICollection <RefreshInstructionEnvelope> batch        = GetBatch(true);
            IEnumerable <RefreshInstruction>         instructions = RefreshInstruction.GetInstructions(refresher, JsonSerializer, messageType, ids, idType, json);

            // Batch if we can, else write to DB immediately.
            if (batch == null)
            {
                CacheInstructionService.DeliverInstructionsInBatches(instructions, LocalIdentity);
            }
            else
            {
                batch.Add(new RefreshInstructionEnvelope(refresher, instructions));
            }
        }
        /// <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();
            }
        }