/// <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-retriable error) it should be disposed. /// </summary> internal override StorageTransmission Peek() { IEnumerable <FileInfo> files = this.GetFiles("*.trn"); lock (this.peekLockObj) { foreach (FileInfo file in files) { try { // if a file was peeked before, skip it (wait until it is disposed). if (this.peekedTransmissions.ContainsKey(file.Name) == false && this.deletedFilesQueue.Contains(file.Name) == 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 => this.OnPeekedItemDisposed(file.Name); // add the transmission to the list. this.peekedTransmissions.Add(file.Name, file.FullName); return(storageTransmissionItem); } } catch (Exception e) { string msg = string.Format(CultureInfo.InvariantCulture, "Failed to load an item from the storage. file: {0} Exception: {1}", file, e); CoreEventSource.Log.LogVerbose(msg); } } } return(null); }
internal override void Delete(StorageTransmission item) { try { if (this.StorageFolder == null) { return; } // Initial storage size calculation. this.EnsureSizeIsCalculatedAsync().ConfigureAwait(false).GetAwaiter().GetResult(); IStorageFile file = this.StorageFolder.GetFileAsync(item.FileName).AsTask().ConfigureAwait(false).GetAwaiter().GetResult(); long fileSize = this.GetSizeAsync(file).ConfigureAwait(false).GetAwaiter().GetResult(); file.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask().ConfigureAwait(false).GetAwaiter().GetResult(); this.deletedFilesQueue.Enqueue(item.FileName); // calculate size Interlocked.Add(ref this.storageSize, -fileSize); Interlocked.Decrement(ref this.storageCountFiles); } catch (IOException e) { string msg = string.Format(CultureInfo.InvariantCulture, "Failed to delete a file. file: {0} Exception: {1}", item == null ? "null" : item.FullFilePath, e); CoreEventSource.Log.LogVerbose(msg); } }
private static async Task SaveTransmissionToFileAsync(Transmission transmission, IStorageFile file) { try { using (Stream stream = await file.OpenStreamForWriteAsync().ConfigureAwait(false)) { await StorageTransmission.SaveAsync(transmission, stream).ConfigureAwait(false); } } catch (UnauthorizedAccessException) { string message = string.Format("Failed to save transmission to file. UnauthorizedAccessException. File path: {0}, FileName: {1}", file.Path, file.Name); CoreEventSource.Log.LogVerbose(message); throw; } }
/// <summary> /// Send transmissions in a loop. /// </summary> protected void SendLoop() { TimeSpan prevSendingInterval = TimeSpan.Zero; TimeSpan sendingInterval = this.sendingIntervalOnNoData; try { while (!this.stopped) { using (StorageTransmission transmission = this.storage.Peek()) { if (this.stopped) { // This second verfiication is required for cases where 'stopped' was set while peek was happening. // Once the actual sending starts the design is to wait until it finishes and deletes the transmission. // So no extra validation is required. break; } // If there is a transmission to send - send it. if (transmission != null) { bool shouldRetry = this.Send(transmission, ref sendingInterval); if (!shouldRetry) { // If retry is not required - delete the transmission. this.storage.Delete(transmission); } } else { sendingInterval = this.sendingIntervalOnNoData; } } LogInterval(prevSendingInterval, sendingInterval); this.DelayHandler.WaitOne(sendingInterval); prevSendingInterval = sendingInterval; } this.stoppedHandler.Set(); } catch (ObjectDisposedException) { } }
private static async Task <StorageTransmission> LoadTransmissionFromFileAsync(IStorageFile file) { try { using (Stream stream = await file.OpenStreamForReadAsync().ConfigureAwait(false)) { StorageTransmission storageTransmissionItem = await StorageTransmission.CreateFromStreamAsync(stream, file.Path).ConfigureAwait(false); return(storageTransmissionItem); } } catch (Exception e) { string message = string.Format("Failed to load transmission from file. File path: {0}, FileName: {1}, Exception: {2}", file.Path, file.Name, e); CoreEventSource.Log.LogVerbose(message); throw; } }
internal override void Delete(StorageTransmission item) { if (this.StorageFolder == null) { return; } try { File.Delete(item.FullFilePath); this.deletedFilesQueue.Enqueue(item.FileName); } catch (IOException e) { string msg = string.Format(CultureInfo.InvariantCulture, "Failed to delete a file. file: {0} Exception: {1}", item == null ? "null" : item.FullFilePath, e); CoreEventSource.Log.LogVerbose(msg); } catch (UnauthorizedAccessException e) { string msg = string.Format(CultureInfo.InvariantCulture, "Failed to delete a file. file: {0} Exception: {1}", item == null ? "null" : item.FullFilePath, e); CoreEventSource.Log.LogVerbose(msg); } }
/// <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>A boolean value that indicates if there was a retriable error.</returns> protected virtual bool Send(StorageTransmission transmission, ref TimeSpan nextSendInterval) { try { if (transmission != null) { transmission.SendAsync().ConfigureAwait(false).GetAwaiter().GetResult(); // After a successful sending, try immeidiately to send another transmission. nextSendInterval = this.SendingInterval; } } // https://github.com/microsoft/ApplicationInsights-dotnet/blob/70e438848915ec163fd7794221b27be34260d3b4/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs#L158 // HttpClient.SendAsync throws HttpRequestException only on the following scenarios: // "The request failed due to an underlying issue such as network connectivity, DNS failure, server certificate validation or timeout." // i.e for Server errors (500 status code), no exception is thrown. catch (HttpRequestException) { nextSendInterval = this.CalculateNextInterval(null, nextSendInterval, this.maxIntervalBetweenRetries); return(true); } catch (WebException e) { int?statusCode = GetStatusCode(e); nextSendInterval = this.CalculateNextInterval(statusCode, nextSendInterval, this.maxIntervalBetweenRetries); return(IsRetryable(statusCode, e.Status)); } catch (Exception e) { nextSendInterval = this.CalculateNextInterval(null, nextSendInterval, this.maxIntervalBetweenRetries); string msg = string.Format(CultureInfo.InvariantCulture, "Unknown exception during sending: {0}", e); CoreEventSource.Log.LogVerbose(msg); } return(false); }
internal abstract void Delete(StorageTransmission transmission);
/// <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>A boolean value that indicates if there was a retriable error.</returns> protected virtual bool Send(StorageTransmission transmission, ref TimeSpan nextSendInterval) { try { if (transmission != null) { transmission.SendAsync().ConfigureAwait(false).GetAwaiter().GetResult(); // After a successful sending, try immeidiately to send another transmission. nextSendInterval = this.SendingInterval; } } catch (WebException e) { int? statusCode = GetStatusCode(e); nextSendInterval = this.CalculateNextInterval(statusCode, nextSendInterval, this.maxIntervalBetweenRetries); return IsRetryable(statusCode, e.Status); } catch (Exception e) { nextSendInterval = this.CalculateNextInterval(null, nextSendInterval, this.maxIntervalBetweenRetries); string msg = string.Format(CultureInfo.InvariantCulture, "Unknown exception during sending: {0}", e); CoreEventSource.Log.LogVerbose(msg); } return false; }