Exemplo n.º 1
0
        /// <summary>
        /// Determines whether the specified file should be processed.
        /// </summary>
        /// <param name="filePath">The candidate file for processing.</param>
        /// <returns>True if the file should be processed, false otherwise.</returns>
        public virtual bool ShouldProcessFile(string filePath)
        {
            if (IsStatusFile(filePath))
            {
                return(false);
            }

            string statusFilePath = GetStatusFile(filePath);

            if (!File.Exists(statusFilePath))
            {
                return(true);
            }

            StatusFileEntry statusEntry = null;

            try
            {
                GetLastStatus(statusFilePath, out statusEntry);
            }
            catch (IOException)
            {
                // if we get an exception reading the status file, it's
                // likely because someone started processing and has it locked
                return(false);
            }

            return(statusEntry == null || (statusEntry.State != ProcessingState.Processed &&
                                           statusEntry.ProcessCount < MaxProcessCount));
        }
Exemplo n.º 2
0
        internal bool GetLastStatus(string statusFilePath, out StatusFileEntry statusEntry)
        {
            statusEntry = null;

            if (!File.Exists(statusFilePath))
            {
                return(false);
            }

            using (Stream stream = File.OpenRead(statusFilePath))
            {
                statusEntry = GetLastStatus(stream);
            }

            return(statusEntry != null);
        }
Exemplo n.º 3
0
        internal StatusFileEntry GetLastStatus(Stream statusFileStream)
        {
            StatusFileEntry statusEntry = null;

            using (var reader = new StreamReader(statusFileStream, Encoding.UTF8, false, 1024, true))
            {
                string   text      = reader.ReadToEnd();
                string[] fileLines = text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
                string   lastLine  = fileLines.LastOrDefault();
                if (!string.IsNullOrEmpty(lastLine))
                {
                    using var stringReader = new StringReader(lastLine);
                    statusEntry            = (StatusFileEntry)_serializer.Deserialize(stringReader, typeof(StatusFileEntry));
                }
            }

            statusFileStream.Seek(0, SeekOrigin.End);

            return(statusEntry);
        }
Exemplo n.º 4
0
        internal StreamWriter AcquireStatusFileLock(string filePath, WatcherChangeTypes changeType, out StatusFileEntry statusEntry)
        {
            Stream stream = null;

            statusEntry = null;
            try
            {
                // Attempt to create (or update) the companion status file and lock it. The status
                // file is the mechanism for handling multi-instance concurrency.
                string statusFilePath = GetStatusFile(filePath);
                stream = File.Open(statusFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);

                // Once we've established the lock, we need to check to ensure that another instance
                // hasn't already processed the file in the time between our getting the event and
                // acquiring the lock.
                statusEntry = GetLastStatus(stream);
                if (statusEntry != null && statusEntry.State == ProcessingState.Processed)
                {
                    // For file Create, we have no additional checks to perform. However for
                    // file Change, we need to also check the LastWrite value for the entry
                    // since there can be multiple Processed entries in the file over time.
                    if (changeType == WatcherChangeTypes.Created)
                    {
                        return(null);
                    }
                    else if (changeType == WatcherChangeTypes.Changed &&
                             File.GetLastWriteTimeUtc(filePath) == statusEntry.LastWrite)
                    {
                        return(null);
                    }
                }

                stream.Seek(0, SeekOrigin.End);
                var streamReader = new StreamWriter(stream)
                {
                    AutoFlush = true
                };
                stream = null;

                return(streamReader);
            }
            catch
            {
                return(null);
            }
            finally
            {
                if (stream != null)
                {
                    stream.Dispose();
                }
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Process the file indicated by the specified <see cref="FileSystemEventArgs"/>.
        /// </summary>
        /// <param name="eventArgs">The <see cref="FileSystemEventArgs"/> indicating the file to process.</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
        /// <returns>
        /// A <see cref="Task"/> that returns true if the file was processed successfully, false otherwise.
        /// </returns>
        public virtual async Task <bool> ProcessFileAsync(FileSystemEventArgs eventArgs, CancellationToken cancellationToken)
        {
            try
            {
                StatusFileEntry status   = null;
                string          filePath = eventArgs.FullPath;
                using (var statusWriter = AcquireStatusFileLock(filePath, eventArgs.ChangeType, out status))
                {
                    if (statusWriter == null)
                    {
                        return(false);
                    }

                    // We've acquired the lock. The current status might be either Failed
                    // or Processing (if processing failed before we were unable to update
                    // the file status to Failed)
                    int processCount = 0;
                    if (status != null)
                    {
                        processCount = status.ProcessCount;
                    }

                    while (processCount++ < MaxProcessCount)
                    {
                        FunctionResult result = null;
                        if (result != null)
                        {
                            var delay = GetRetryInterval(result, processCount);
                            await Task.Delay(delay);
                        }

                        // write an entry indicating the file is being processed
                        status = new StatusFileEntry
                        {
                            State        = ProcessingState.Processing,
                            Timestamp    = DateTime.Now,
                            LastWrite    = File.GetLastWriteTimeUtc(filePath),
                            ChangeType   = eventArgs.ChangeType,
                            InstanceId   = InstanceId,
                            ProcessCount = processCount
                        };
                        _serializer.Serialize(statusWriter, status);
                        statusWriter.WriteLine();

                        // invoke the job function
                        var input = new TriggeredFunctionData
                        {
                            TriggerValue = eventArgs
                        };
                        result = await _executor.TryExecuteAsync(input, cancellationToken);

                        // write a status entry indicating the state of processing
                        status.State     = result.Succeeded ? ProcessingState.Processed : ProcessingState.Failed;
                        status.Timestamp = DateTime.Now;
                        _serializer.Serialize(statusWriter, status);
                        statusWriter.WriteLine();

                        if (result.Succeeded)
                        {
                            return(true);
                        }
                    }

                    return(false);
                }
            }
            catch
            {
                return(false);
            }
        }