/// <summary> /// Gets the recommended timeout. /// </summary> /// <param name="accessToken">The Rollbar access token.</param> /// <returns>TimeSpan.</returns> public TimeSpan GetRecommendedTimeout(string accessToken) { TimeSpan payloadTimeout = TimeSpan.Zero; int totalPayloads = 0; AccessTokenQueuesMetadata tokenMetadata = null; lock (this._syncLock) { if (this._queuesByAccessToken.TryGetValue(accessToken, out tokenMetadata)) { foreach (var queue in tokenMetadata.Queues) { totalPayloads += queue.GetPayloadCount(); TimeSpan queueTimeout = queue.Logger.Config.MaxReportsPerMinute.HasValue ? TimeSpan.FromTicks(TimeSpan.FromMinutes(1).Ticks / queue.Logger.Config.MaxReportsPerMinute.Value) : TimeSpan.Zero; if (payloadTimeout < queueTimeout) { payloadTimeout = queueTimeout; } } } } return(TimeSpan.FromTicks((totalPayloads + 1) * payloadTimeout.Ticks)); }
/// <summary> /// Processes the persistent store once. /// </summary> /// <param name="destination">The destination.</param> private void ProcessPersistentStoreOnce(IDestination destination) { AccessTokenQueuesMetadata accessTokenMetadata = null; lock (this._syncLock) { if (!this._queuesByAccessToken.TryGetValue(destination.AccessToken, out accessTokenMetadata)) { accessTokenMetadata = new AccessTokenQueuesMetadata(destination.AccessToken); this._queuesByAccessToken.Add(destination.AccessToken, accessTokenMetadata); } } if (accessTokenMetadata.IsTransmissionSuspended && DateTimeOffset.Now < accessTokenMetadata.NextTimeTokenUsage) { // the token is suspended and the next usage time is not reached, // there is no point in continuing persistent store processing for this access token: return; } // 1. delete all the stale records of this destination and save the store context // (if any records were deleted): DateTime staleRecordsLimit = DateTime.UtcNow.Subtract(staleRecordAge); var staleRecords = this._storeRepository.GetStaleRecords(staleRecordsLimit); if (staleRecords != null && staleRecords.Length > 0) { this._storeRepository.DeleteRecords(staleRecords); } // 2. get the oldest record of this destination and try transmitting it: if (!ConnectivityMonitor.Instance.IsConnectivityOn) { return; // there is no point trying to transmit the oldest record (if any)... } var oldestRecord = this._storeRepository.GetOldestRecords(destination.ID); if (oldestRecord != null) { var rollbarResponse = TryPosting(oldestRecord); if (rollbarResponse == null) { return; //could not reach Rollbar API... } this.OnRollbarEvent( new CommunicationEventArgs(null, oldestRecord.PayloadJson, rollbarResponse) ); // This processor did its best communicating with Rollbar API. // Regardless of actual result, update next token usage and consider // this payload record processed so it can be deleted: accessTokenMetadata.UpdateNextTimeTokenUsage(rollbarResponse.RollbarRateLimit); this._storeRepository.DeleteRecords(oldestRecord); } }
/// <summary> /// Processes the queues. /// </summary> /// <param name="tokenMetadata">The token metadata.</param> private void ProcessQueues(AccessTokenQueuesMetadata tokenMetadata) { foreach (var queue in tokenMetadata.Queues) { if (DateTimeOffset.Now >= queue.NextDequeueTime) { RollbarResponse response = null; PayloadBundle payloadBundle = Process(queue, out response); if (payloadBundle == null || response == null) { continue; } switch (response.Error) { case (int)RollbarApiErrorEventArgs.RollbarError.None: payloadBundle.Signal?.Release(); queue.Dequeue(); tokenMetadata.ResetTokenUsageDelay(); break; case (int)RollbarApiErrorEventArgs.RollbarError.TooManyRequests: ObeyPayloadTimeout(payloadBundle, queue); tokenMetadata.IncrementTokenUsageDelay(); this.OnRollbarEvent( new RollbarApiErrorEventArgs(queue.Logger, payloadBundle.GetPayload(), response) ); return; default: ObeyPayloadTimeout(payloadBundle, queue); this.OnRollbarEvent( new RollbarApiErrorEventArgs(queue.Logger, payloadBundle.GetPayload(), response) ); break; } } } // let's see if we can unregister any recently released queues: var releasedQueuesToRemove = tokenMetadata.Queues.Where(q => q.IsReleased && (q.GetPayloadCount() == 0)).ToArray(); if (releasedQueuesToRemove != null && releasedQueuesToRemove.LongLength > 0) { foreach (var queue in releasedQueuesToRemove) { this.Unregister(queue); } } }
/// <summary> /// Gets the payload count. /// </summary> /// <param name="accessToken">Converts to ken.</param> /// <returns>System.Int32.</returns> public int GetPayloadCount(string accessToken) { int counter = 0; AccessTokenQueuesMetadata tokenMetadata = null; lock (this._syncLock) { if (this._queuesByAccessToken.TryGetValue(accessToken, out tokenMetadata)) { foreach (var queue in tokenMetadata.Queues) { counter += queue.GetPayloadCount(); } } } return(counter); }
private void IndexByToken(PayloadQueue queue) { string queueToken = queue.Logger.Config.AccessToken; if (queueToken == null) { //this is a valid case for the RollbarLogger singleton instance, //when the instance is created but not configured yet... return; } if (!this._queuesByAccessToken.TryGetValue(queueToken, out AccessTokenQueuesMetadata tokenMetadata)) { tokenMetadata = new AccessTokenQueuesMetadata(queueToken); this._queuesByAccessToken.Add(queueToken, tokenMetadata); } tokenMetadata.Queues.Add(queue); }
private void ProcessQueues(AccessTokenQueuesMetadata tokenMetadata) { foreach (var queue in tokenMetadata.Queues) { if (DateTimeOffset.Now >= queue.NextDequeueTime) { Payload payload = queue.Peek(); if (payload == null) { continue; } var response = Process(payload, queue.Logger.Config); if (response == null) { continue; } switch (response.Error) { case (int)RollbarApiErrorEventArgs.RollbarError.None: payload.Signal?.Release(); queue.Dequeue(); tokenMetadata.ResetTokenUsageDelay(); break; case (int)RollbarApiErrorEventArgs.RollbarError.TooManyRequests: ObeyPayloadTimeout(payload, queue); tokenMetadata.IncrementTokenUsageDelay(); this.OnRollbarEvent( new RollbarApiErrorEventArgs(queue.Logger.Config, payload, response) ); return; default: ObeyPayloadTimeout(payload, queue); this.OnRollbarEvent( new RollbarApiErrorEventArgs(queue.Logger.Config, payload, response) ); break; } } } }
/// <summary> /// Processes the queues. /// </summary> /// <param name="tokenMetadata">The token metadata.</param> private void ProcessQueues(AccessTokenQueuesMetadata tokenMetadata) { // let's see if we can unregister any recently released queues: var releasedQueuesToRemove = tokenMetadata.Queues.Where(q => q.IsReleased && (q.GetPayloadCount() == 0)).ToArray(); if (releasedQueuesToRemove != null && releasedQueuesToRemove.LongLength > 0) { foreach (var queue in releasedQueuesToRemove) { this.Unregister(queue); } } // process the access token's queues: foreach (var queue in tokenMetadata.Queues) { if (DateTimeOffset.Now < queue.NextDequeueTime) { // this means the queue overrides its reporting rate limit via its configuration settings // let's observe its settings and skip processing: continue; } if (tokenMetadata.IsTransmissionSuspended && DateTimeOffset.Now < tokenMetadata.NextTimeTokenUsage) { // the token is suspended and the next usage time is not reached, // let's flush the token queues (we are not allowed to transmit anyway) // and quit processing this token's queues this time (until next processing iteration): foreach (var tokenQueue in tokenMetadata.Queues) { foreach (var flushedBundle in tokenQueue.Flush()) { this.OnRollbarEvent( new PayloadDropEventArgs( queue.Logger, flushedBundle.GetPayload(), PayloadDropEventArgs.DropReason.TokenSuspension ) ); } } return; } PayloadBundle payloadBundle = null; RollbarResponse response = null; try { payloadBundle = Process(queue, out response); } catch (AggregateException aggregateException) { if (aggregateException.InnerExceptions.Any(e => e is HttpRequestException)) { this.Persist(queue); continue; } else { var bundle = queue.Dequeue(); this.OnRollbarEvent( new PayloadDropEventArgs(queue.Logger, bundle.GetPayload(), PayloadDropEventArgs.DropReason.InvalidPayload) ); queue.Dequeue(); throw; } } catch (System.Exception ex) { this.Persist(queue); continue; } if (payloadBundle != null && response == null) { var bundle = queue.Dequeue(); this.OnRollbarEvent( new PayloadDropEventArgs(queue.Logger, bundle.GetPayload(), PayloadDropEventArgs.DropReason.AllTransmissionRetriesFailed) ); } if (payloadBundle == null || response == null) { continue; } tokenMetadata.UpdateNextTimeTokenUsage(response.RollbarRateLimit); switch (response.Error) { case (int)RollbarApiErrorEventArgs.RollbarError.None: payloadBundle.Signal?.Release(); queue.Dequeue(); break; case (int)RollbarApiErrorEventArgs.RollbarError.TooManyRequests: ObeyPayloadTimeout(payloadBundle, queue); this.OnRollbarEvent( new RollbarApiErrorEventArgs(queue.Logger, payloadBundle.GetPayload(), response) ); return; default: ObeyPayloadTimeout(payloadBundle, queue); this.OnRollbarEvent( new RollbarApiErrorEventArgs(queue.Logger, payloadBundle.GetPayload(), response) ); break; } } }