/// <summary> /// Converts a string from lowercase to uppercase, taking an extra long time. /// During that time, it periodically calls testCancel so that the caller can /// abort the operation if it's taking too long. /// </summary> /// <returns>An uppercase string.</returns> private string ShoutString(string text, ThrowIfAborted throwIfAborted) { string upperText = text.ToUpper(); // Pretend like we're working hard and taking time. Time is a function of // the number of letters in the word. var workDeadline = DateTime.UtcNow + TimeSpan.FromSeconds( upperText.Length * (int)Math.Log(upperText.Length)); var oneSecond = TimeSpan.FromSeconds(1); while (workDeadline > DateTime.UtcNow) { throwIfAborted(); System.Threading.Thread.Sleep(oneSecond); } // Simulate different kinds of errors depending on text. if (upperText.Contains("CHICKEN")) { // Simulate a fatal error that cannot possible be handled. throw new FatalException("Oh no! Not chickens!"); } if (upperText.Contains("CORN")) { // Simulate a flaky error that happens sometimes, but not always. if (_init.Random.Next(3) > 0) { throw new CornException(); } } if (upperText.Contains("COW")) { // Simulate an error that eventually times out with error. throw new CowException(); } return(upperText); }
/// <summary> /// Converts a string from lowercase to uppercase, taking an extra long time. /// During that time, it periodically calls testCancel so that the caller can /// abort the operation if it's taking too long. /// </summary> /// <returns>An uppercase string.</returns> private string ShoutString(string text, ThrowIfAborted throwIfAborted) { string upperText = text.ToUpper(); // Pretend like we're working hard and taking time. Time is a function of // the number of letters in the word. var workDeadline = DateTime.UtcNow + TimeSpan.FromSeconds( upperText.Length * (int)Math.Log(upperText.Length)); var oneSecond = TimeSpan.FromSeconds(1); while (workDeadline > DateTime.UtcNow) { throwIfAborted(); System.Threading.Thread.Sleep(oneSecond); } // Simulate different kinds of errors depending on text. if (upperText.Contains("CHICKEN")) { // Simulate a fatal error that cannot possible be handled. throw new FatalException("Oh no! Not chickens!"); } if (upperText.Contains("CORN")) { // Simulate a flaky error that happens sometimes, but not always. if (_init.Random.Next(3) > 0) throw new CornException(); } if (upperText.Contains("COW")) { // Simulate an error that eventually times out with error. throw new CowException(); } return upperText; }
/// <summary> /// Waits for a shout request message to arrive in the Pub/Sub /// subscription. Converts the text to uppercase and posts the results /// back to the website. /// </summary> /// <returns> /// The number of messages pulled from the subscription, /// or -1 if an expected error occurred. /// </returns> public int ShoutOrThrow(System.Threading.CancellationToken cancellationToken) { // Pull a shout request message from the subscription. string subscriptionPath = MakeSubscriptionPath(_init.SubscriptionName); WriteLog("Pulling shout request messages from " + subscriptionPath + "...", TraceEventType.Verbose); var pullRequest = _init.PubsubService.Projects.Subscriptions.Pull( new PullRequest() { MaxMessages = 1, ReturnImmediately = false }, subscriptionPath).ExecuteAsync(); Task.WaitAny(new Task[] { pullRequest }, cancellationToken); var pullResponse = pullRequest.Result; int messageCount = pullResponse.ReceivedMessages == null ? 0 : pullResponse.ReceivedMessages.Count; WriteLog("Received " + messageCount + " messages.", TraceEventType.Information); if (messageCount < 1) { return(0); // Nothing pulled. Nothing to do. } // Examine the received message. var shoutRequestMessage = pullResponse.ReceivedMessages[0]; var attributes = shoutRequestMessage.Message.Attributes; string postStatusUrl; string postStatusToken; DateTime requestDeadline; try { postStatusUrl = attributes["postStatusUrl"]; postStatusToken = attributes["postStatusToken"]; long unixDeadline = Convert.ToInt64(attributes["deadline"]); requestDeadline = FromUnixTime(unixDeadline); } catch (Exception e) { WriteLog("Bad shout request message attributes.\n" + e.ToString(), TraceEventType.Warning); Acknowledge(shoutRequestMessage.AckId); return(-1); } // Tell the world we are shouting this request. PublishStatus(postStatusUrl, postStatusToken, "shouting"); WriteLog("Shouting " + postStatusUrl, TraceEventType.Verbose); try { // Decode the payload, the string we want to shout. byte[] data = Convert.FromBase64String(shoutRequestMessage.Message.Data); string decodedString = Encoding.UTF8.GetString(data); // Watch the clock and cancellation token as we work. We need to extend the // ack deadline if the request takes a while. var tenSeconds = TimeSpan.FromSeconds(10); DateTime ackDeadline = DateTime.UtcNow + tenSeconds; ThrowIfAborted throwIfAborted = () => { cancellationToken.ThrowIfCancellationRequested(); var now = DateTime.UtcNow; if (requestDeadline < now) { throw new FatalException("Request timed out."); } if (ackDeadline < now) { // Tell the subscription we need more time: WriteLog("Need more time...", TraceEventType.Verbose); _init.PubsubService.Projects.Subscriptions.ModifyAckDeadline( new ModifyAckDeadlineRequest { AckIds = new string[] { shoutRequestMessage.AckId }, AckDeadlineSeconds = 15, }, MakeSubscriptionPath(_init.SubscriptionName)).Execute(); ackDeadline = now + tenSeconds; } }; // Shout it. string upperText = ShoutString(decodedString, throwIfAborted); // Publish the result. PublishStatus(postStatusUrl, postStatusToken, "success", upperText); Acknowledge(shoutRequestMessage.AckId); return(1); } catch (OperationCanceledException) { return(1); // Service stopped. Nothing to report. } catch (FatalException e) { WriteLog("Fatal exception while shouting:\n" + e.Message, TraceEventType.Error); Acknowledge(shoutRequestMessage.AckId); PublishStatus(postStatusUrl, postStatusToken, "fatal", e.Message); return(-1); } catch (Exception e) { // Something went wrong while shouting. Report the error. WriteLog("Exception while shouting:\n" + e.Message, TraceEventType.Error); PublishStatus(postStatusUrl, postStatusToken, "error", e.Message); return(-1); } }