Beispiel #1
0
        internal override void Delete(StorageTransmission item)
        {
            try
            {
                if (StorageFolder == null)
                {
                    return;
                }

                // Initial storage size calculation.
                CalculateSize();

                long fileSize = GetSize(item.FileName);
                File.Delete(Path.Combine(StorageFolder, item.FileName));

                _deletedFilesQueue.Enqueue(item.FileName);

                // calculate size
                Interlocked.Add(ref _storageSize, -fileSize);
                Interlocked.Decrement(ref _storageCountFiles);
            }
            catch (IOException e)
            {
                PersistenceChannelDebugLog.WriteException(e, "Failed to delete a file. file: {0}", item == null ? "null" : item.FullFilePath);
            }
        }
Beispiel #2
0
 protected void OnPeekedItemDisposed(string fileName)
 {
     try
     {
         if (PeekedTransmissions.ContainsKey(fileName))
         {
             PeekedTransmissions.Remove(fileName);
         }
     }
     catch (Exception e)
     {
         PersistenceChannelDebugLog.WriteException(e, "Failed to remove the item from storage items.");
     }
 }
Beispiel #3
0
        internal override async Task EnqueueAsync(Transmission transmission)
        {
            try
            {
                if (transmission == null || StorageFolder == null)
                {
                    return;
                }

                // Initial storage size calculation.
                CalculateSize();

                if ((ulong)_storageSize >= CapacityInBytes || _storageCountFiles >= MaxFiles)
                {
                    // if max storage capacity has reached, drop the transmission (but log every 100 lost transmissions).
                    if (_transmissionsDropped++ % 100 == 0)
                    {
                        PersistenceChannelDebugLog.WriteLine("Total transmissions dropped: " + _transmissionsDropped);
                    }

                    return;
                }

                // Writes content to a temporary file and only then rename to avoid the Peek from reading the file before it is being written.
                // Creates the temp file name
                string tempFileName = Guid.NewGuid().ToString("N");

                // Now that the file got created we can increase the files count
                Interlocked.Increment(ref _storageCountFiles);

                // Saves transmission to the temp file
                await SaveTransmissionToFileAsync(transmission, tempFileName).ConfigureAwait(false);

                // Now that the file is written increase storage size.
                long temporaryFileSize = GetSize(tempFileName);
                Interlocked.Add(ref _storageSize, temporaryFileSize);

                // Creates a new file name
                string now         = DateTime.UtcNow.ToString("yyyyMMddHHmmss");
                string newFileName = string.Format(CultureInfo.InvariantCulture, "{0}_{1}.trn", now, tempFileName);

                // Renames the file
                File.Move(Path.Combine(StorageFolder, tempFileName), Path.Combine(StorageFolder, newFileName));
            }
            catch (Exception e)
            {
                PersistenceChannelDebugLog.WriteException(e, "EnqueueAsync");
            }
        }
Beispiel #4
0
        /// <summary>
        ///     Get files from <see cref="storageFolder" />.
        /// </summary>
        /// <param name="fileQuery">Define the logic for sorting the files.</param>
        /// <param name="filterByExtension">Defines a file extension. This method will return only files with this extension.</param>
        /// <param name="top">
        ///     Define how many files to return. This can be useful when the directory has a lot of files, in that case
        ///     GetFilesAsync will have a performance hit.
        /// </param>
        private IEnumerable <string> GetFiles(string filterByExtension, int top)
        {
            try
            {
                if (StorageFolder != null)
                {
                    return(Directory.GetFiles(StorageFolder, filterByExtension).Take(top));
                }
            }
            catch (Exception e)
            {
                PersistenceChannelDebugLog.WriteException(e, "Peek failed while get files from storage.");
            }

            return(Enumerable.Empty <string>());
        }
Beispiel #5
0
        internal override void Init(string storageDirectoryPath)
        {
            PeekedTransmissions = new SnapshottingDictionary <string, string>();

            VerifyOrSetDefaultStorageDirectoryPath(storageDirectoryPath);

            CapacityInBytes = 10 * 1024 * 1024; // 10 MB
            MaxFiles        = 100;

            Task.Run(DeleteObsoleteFiles)
            .ContinueWith(
                task =>
            {
                PersistenceChannelDebugLog.WriteException(
                    task.Exception,
                    "Storage: Unhandled exception in DeleteObsoleteFiles");
            },
                TaskContinuationOptions.OnlyOnFaulted);
        }
Beispiel #6
0
 /// <summary>
 ///     Enqueue is saving a transmission to a <c>tmp</c> file and after a successful write operation it renames it to a
 ///     <c>trn</c> file.
 ///     A file without a <c>trn</c> extension is ignored by Storage.Peek(), so if a process is taken down before rename
 ///     happens
 ///     it will stay on the disk forever.
 ///     This thread deletes files with the <c>tmp</c> extension that exists on disk for more than 5 minutes.
 /// </summary>
 private void DeleteObsoleteFiles()
 {
     try
     {
         IEnumerable <string> files = GetFiles("*.tmp", 50);
         foreach (string file in files)
         {
             DateTime creationTime = File.GetCreationTimeUtc(Path.Combine(StorageFolder, file));
             // if the file is older then 5 minutes - delete it.
             if (DateTime.UtcNow - creationTime >= TimeSpan.FromMinutes(5))
             {
                 File.Delete(Path.Combine(StorageFolder, file));
             }
         }
     }
     catch (Exception e)
     {
         PersistenceChannelDebugLog.WriteException(e, "Failed to delete tmp files.");
     }
 }
Beispiel #7
0
        /// <summary>
        ///     Reads an item from the storage. Order is Last-In-First-Out.
        ///     When the Transmission is no longer needed (it was either sent or failed with a non-retryable error) it should be
        ///     disposed.
        /// </summary>
        internal override StorageTransmission Peek()
        {
            IEnumerable <string> files = GetFiles("*.trn", 50);

            lock (_peekLockObj)
            {
                foreach (string file in files)
                {
                    try
                    {
                        // if a file was peeked before, skip it (wait until it is disposed).
                        if (PeekedTransmissions.ContainsKey(file) == false &&
                            _deletedFilesQueue.Contains(file) == false)
                        {
                            // Load the transmission from disk.
                            StorageTransmission storageTransmissionItem = LoadTransmissionFromFileAsync(file)
                                                                          .ConfigureAwait(false).GetAwaiter().GetResult();

                            // when item is disposed it should be removed from the peeked list.
                            storageTransmissionItem.Disposing = item => OnPeekedItemDisposed(file);

                            // add the transmission to the list.
                            PeekedTransmissions.Add(file, storageTransmissionItem.FullFilePath);
                            return(storageTransmissionItem);
                        }
                    }
                    catch (Exception e)
                    {
                        PersistenceChannelDebugLog.WriteException(
                            e,
                            "Failed to load an item from the storage. file: {0}",
                            file);
                    }
                }
            }

            return(null);
        }
Beispiel #8
0
        /// <summary>
        ///     Initializes a new instance of the <see cref="Sender" /> class.
        /// </summary>
        /// <param name="storage">The storage that holds the transmissions to send.</param>
        /// <param name="transmitter">
        ///     The persistence transmitter that manages this Sender.
        ///     The transmitter will be used as a configuration class, it exposes properties like SendingInterval that will be read
        ///     by Sender.
        /// </param>
        /// <param name="startSending">
        ///     A boolean value that determines if Sender should start sending immediately. This is only
        ///     used for unit tests.
        /// </param>
        internal Sender(BaseStorageService storage, PersistenceTransmitter transmitter, bool startSending = true)
        {
            _stopped                = false;
            DelayHandler            = new AutoResetEvent(false);
            _stoppedHandler         = new AutoResetEvent(false);
            _drainingTimeout        = TimeSpan.FromSeconds(100);
            _defaultSendingInterval = TimeSpan.FromSeconds(5);

            _transmitter = transmitter;
            _storage     = storage;

            if (startSending)
            {
                // It is currently possible for the long - running task to be executed(and thereby block during WaitOne) on the UI thread when
                // called by a task scheduled on the UI thread. Explicitly specifying TaskScheduler.Default
                // when calling StartNew guarantees that Sender never blocks the main thread.
                Task.Factory.StartNew(SendLoop, CancellationToken.None, TaskCreationOptions.LongRunning,
                                      TaskScheduler.Default)
                .ContinueWith(
                    t => PersistenceChannelDebugLog.WriteException(t.Exception, "Sender: Failure in SendLoop"),
                    TaskContinuationOptions.OnlyOnFaulted);
            }
        }
Beispiel #9
0
        /// <summary>
        ///     Sends a transmission and handle errors.
        /// </summary>
        /// <param name="transmission">The transmission to send.</param>
        /// <param name="nextSendInterval">
        ///     When this value returns it will hold a recommendation for when to start the next sending
        ///     iteration.
        /// </param>
        /// <returns>True, if there was sent error and we need to retry sending, otherwise false.</returns>
        protected virtual bool Send(StorageTransmission transmission, ref TimeSpan nextSendInterval)
        {
            try
            {
                if (transmission != null)
                {
                    bool isConnected = NetworkInterface.GetIsNetworkAvailable();

                    // there is no internet connection available, return than.
                    if (!isConnected)
                    {
                        PersistenceChannelDebugLog.WriteLine(
                            "Cannot send data to the server. Internet connection is not available");
                        return(true);
                    }

                    transmission.SendAsync().ConfigureAwait(false).GetAwaiter().GetResult();

                    // After a successful sending, try immediately to send another transmission.
                    nextSendInterval = SendingInterval;
                }
            }
            catch (WebException e)
            {
                int?statusCode = GetStatusCode(e);
                nextSendInterval = CalculateNextInterval(statusCode, nextSendInterval, _maxIntervalBetweenRetries);
                return(IsRetryable(statusCode, e.Status));
            }
            catch (Exception e)
            {
                nextSendInterval = CalculateNextInterval(null, nextSendInterval, _maxIntervalBetweenRetries);
                PersistenceChannelDebugLog.WriteException(e, "Unknown exception during sending");
            }

            return(false);
        }