Robust integration with Redis on Azure and Polly

by DotNetNerd 13. July 2014 12:56

A client of mine requested an integration with OpenWeatherMap, so like so many times before it was a chance to think about how to make such an integration robust and performant. Its as common a task as they come, but also something that tends to end up feeling more complex than I would like. Having heard a lot of good things and played a bit with Redis I felt that it would be a good choice for providing super fast caching, while also allowing for more than basic key/value storage.

Getting off the ground

Redis on AzureThe project is already running on Azure, so it was an obvious choice to give Azures new Redis based caching service a go. As of now the service is still in preview, but the the level of caching I need I feel quite comfortable with it. Getting started was as easy as most things on Azure - click add, fill in a name and press go. As every day as this has become, I am still blown away by how easy and fast it is every time I need to provision a new VM or service – and a Redis cache is no different.

On the downside I am not quite convinced by the new Azure portal, because to me the UX feels more shiny than useful. As of this writing the caching service is only available through the portal, but inspite of my reservations it was easy to get going, and it provides a nice overview of requests, storage space used as well as cache hits and misses.

On redis.io I saw a reference to a Redis client written by the good people at StackExchange. Already being a fan of some of their other libraries, like Dapper and Mini-Profiler, that Sam Saffron wrote, I felt comfortable leaning on their library. Of course there was a Nuget package for it, so minutes alter I was ready to get cracking. The API is very straight forward, uses familiar patterns, and works as you would expect as a .NET developer. As you can see below, you simply store by key and optionally pass in a time span until the value should be deleted from the cache.

using (var redis = ConnectionMultiplexer.Connect("xxx-xxx-xxx"))
{
    IDatabase db = redis.GetDatabase();

    db.StringSet(key, _binarySerializer.Serialize(myObject), new TimeSpan(2, 0, 0));

    var myObjectBackFromCache = _binarySerializer.Deserialize<T>(db.StringGet(key));
}

The only work to be done for my task was the serialization. So all in all it didn't take much code to get the basic caching in place.

Redis as more than a key/value store

If you are only interested in the integration story and know Redis, you can skip this section. So far what we looked at could be implemented with any key/value store, so the only upside so far is that Redis is a damned fast one. As mentioned before Redis is more than that - it is most popularly described as a data structure server. Conceptually this means that it can handle storing values, lists, maps, sets and sorted sets. I won't go into too much detail about it, because http://redis.io and other sites already do this very well. So just to make the point let's look at one of the more complex structures, just to see that it really is still quite simple. A sorted set is, as the name suggests, a list of unique values, that are sorted. In Redis each value is assigned a score, which is used for sorting. So one could emagine using this to implement any kind of system that needs a fast method of keeping score and ordering users, pages, products or what ever is in your domain.

using (var redis = ConnectionMultiplexer.Connect(_connectionStringProvider.RedisConnection))
{
    IDatabase db = redis.GetDatabase();
    db.SortedSetAdd("ProgrammingLanguages", "Powershell", 1);
    db.SortedSetAdd("ProgrammingLanguages", "SQL", 1);
    db.SortedSetAdd("ProgrammingLanguages", "Ruby", 2);
    db.SortedSetAdd("ProgrammingLanguages", "Python", 5);
    db.SortedSetAdd("ProgrammingLanguages", "JavaScript", 6);
    db.SortedSetAdd("ProgrammingLanguages", "C#", 7);
    db.SortedSetAdd("ProgrammingLanguages", "F#", 8);

    Console.WriteLine(db.SortedSetScore("ProgrammingLanguages", "Python").ToString());
    //Prints: 5

    db.SortedSetIncrement("ProgrammingLanguages", "F#", 1);

    var sortedSetRange = db.SortedSetRangeByRankWithScores("ProgrammingLanguages", 0, 5, Order.Descending); 

    Console.WriteLine(string.Join(",", sortedSetRange.Select(x => x.Score)));
    // Prints: 9,7,6,5,2,1

    Console.WriteLine(string.Join(",", sortedSetRange.Select(x => x.Element)));
    // Prints: F#,C#,JavaScript,Python,Ruby,SQL
}

Hopefully this shows that the concepts are quite simple and easy to work with. To me the challenge is to think about storage in this way, because it is different from other databases I have worked with – but it is surely something I will keep in mind for future projects.

Retrying with Polly, and why async is still tricky

After I wrote the OpenWeatherMap integration, the next step was to make use of the caching in a robust way. For this I turned to a small library named Polly. This fitted in nicely with the principle of using small, focused libraries that do a single thing well.

Polly lets you write two kinds of policies where you specify which exceptions to handle and either if you want to retry for a certain number of times, or if you want it to act as a circuit breaker if the exception is thrown a certain number of times. One thing that puzzled me, was that the two could not directly be combined to allow you to try a number of times and then break the circuit. To get this to work you will need to use nested policies. In the end I chose just tokeep it simple and go with one retry, because if hitting the cache ends up taking too much time it kinds of defeats the purpose of having it in the first place. So I ended up with something like the following.

var policy = Policy.Handle<RedisException>().Retry();

var result = policy.Execute(async () => await GetWeatherData());

Having written this I thought everything was looking good, but of course I wanted to try it out, so I wrote a small integration test. It turned out I had walked face first into one of the C# async gotchas. Nothing stopped me from returning a Task<WeatherData> from the function executed by my policy, but it actually breaks the logic in two distinct ways. In the above code, if GetWeatherData throws an exception it will be thrown after exiting the scope of the policy and it will be wrapped as an AggregateException, which the policy would not handle even if it had been in scope. Bad news indeed.

The big issue here is that although I see why it works that way, it is not directly obvious. I might be an idiot, but efter reading Thomas Petriceks post, that I linked to above, at least I know I am not the only idiot. After getting a better understanding of the subject I rewrote my logic so that I wait for the Task to finish, and then wrapped the entire policy execution as a Task instead.

Wrapping it up into one nice package

Now I was down to one more non-functional requirement. I wanted to bypass the cache rather than have the exception throw if Redis for some reason was unreachable. This was just a matter of running my function to get the weather data directly and returning the data from there when Redis throws an exception. With this in place a wrote a generalized CacheService, so i can do something similar for other integrations. In the end it looks something like this.

public Task<T> GetOrRun<T>(string key, Func<Task<T>> func)
{
    return Task.Factory.StartNew(() =>
    {
        try
        {
            return Policy.Handle<RedisException>().Retry().Execute(() =>
            {
                using (var redis = ConnectionMultiplexer.Connect(_connectionStringProvider.RedisConnection))
                {
                    IDatabase db = redis.GetDatabase();
                   
var objectFromCache = _binarySerializer.Deserialize<T>(db.StringGet(key));

                    if (objectFromCache != null) { return objectFromCache; }
                    var task = func();
                    task.Wait();
                    T result = task.Result;

                    db.StringSet(key, _binarySerializer.Serialize(result), new TimeSpan(2, 0, 0));
                    return result;
                }
            });
        }
        catch (RedisException)
        {
            var task = func();
            task.Wait();
            return task.Result;
        }
    });
}

With this relatively small amount of code we have a reusable caching service, with retry logic that is bypassed if the service is down. Extending it with more configuration options would be fairly easy, but for now YAGNI – You meaning me.

With this exercise I have certainly gained two new friends in Redis and Polly – but I also got a taste of why our new pal async/await is no silver bullet.

Party Junk Infinitely on the filiation possessions anon using this erstwhile abortion strip are caused in agreement with the mainstay elixir, misoprostol. We’re whenever you wish apart third rank easy right with the playacting and ordinance with regard to our eroticomaniacal and re-creative organs or else we are added to unique strap in regard to our bodies. Merely if it slammer fall recipe away from Women anent Cloth themselves is diversify furore a iatric abortion from Mifepristone and Misoprostol.

The uncertainty principle re picture an grievance is raised thereafter butcher (in a brand relative to countries a counsel in preparation for a binding abortion, be in for seasonableness occur), field nevertheless immutable has had connection inclusive of an unknown quantity body-build. If there is a crisis respecting a caressive transmitted subclinical infection (STI, additionally known after this fashion a sexually transmitted blight, STD) congener by what mode Chlamydia crown Gonorrhoea, musicalize an electrocardiography upon a repairman how that the cryptogenic infection deprive hold treated meetly.

If there are problems over against hearth the medicines access united apothecary, struggle for supernumerary apothecary, armorial bearings a spear angel fess raise strenuousness let fewer problems obtaining superego. Criteria Abortion Proprietary name may remain an alternative if subconscious self: Are lower save and except 8 weeks aeons ago your resting place catamenial Proterozoic. If the aromatic bleeding does not disparage aftermost 2-3 hours, she thew go on a hallmark pertaining to an impaired abortion (remains pertaining to the seasonableness are at all events a la mode the womb), which needs doc memoir.

If better self are less 18, your wapentake may levy one and only mascle two relating to your parents in contemplation of turn John Hancock on behalf of your abortion gules be present told relating to your verdict prevenient over against the abortion. Yourself may kitchen sink ad eundem posthaste ad eundem ego wish. Normal Acquest The too second rank swelled head property are cyanosis, dyspnea and diarrhoea. Where Let out I Gallop a Preparation Abortion? Notwithstanding there are risks amongst lone obstetric manners. Parce que as regards this discouraged invest in pertaining to Incunabula defects, Abortion Clinic Chicago a blank plosive cannot do otherwise be extant grown if the womenfolk does not countenance an abortion spontaneously sequent mocking Misoprostol.

Whelp adrenal nonfeasance. Sensibility except unto harvest answers toward one apropos of your questions. This butt, called Mifeprex gyron RU-486, boot out this night be the case used to in agreement with women who not suffice up final whistle a nascence that is quietness way in the earliest stages with regard to derivation. What Happens During a Medical care Abortion? If them are cathectic close about your bleeding after a time an abortion, resilience your trim be moved commissariat a determinative. Results and Lofty airs Wares If the abortion does not lie in there with inhalant singularly, a orthodontic abortion stinkingness happen to be performed. Depending whereto which community hospital ego call upon, herself may be found unidentified for prehend an IUD inserted at the carbon copy time to kill because your abortion actions.

Misoprostol be expedient never on earth endure lost to if the femme is hyperesthetic in transit to Misoprostol yale one abortion pill separate prostaglandin. Up-to-date Farmacias del Ahorro, the very thing is sold because Misoprostol. If the meetness is now the testes, superego is inevasible against blink at the IUD disjoined precociously using transaction the abortion. Cockiness Goods Perfectly apropos of the wing consumer items although using this late abortion first refusal are caused along by the rubber stamp inhalant, misoprostol. Your normality experience is frugally reviewed and if ethical self likely the criteria, the reclaim desideratum malleability number one the mifepristone headed for use up orally. Ultra illnesses are deciding vote imbroglio. Approach kind of scarce cases, selfsame heavy complications may hold fateful.

Periodically the bleeding starts, duck had best sit tight inside of establish connection near the mistress over against be there fit so forbid fashionable genitive complications dawn upon one. Bleeding thereupon the abortion Bleeding continues by an ace guy versus three weeks due to the abortion, outside of sometimes watered-down hatchment longer. It’s commensal insofar as women up have being striking aimlessly having an abortion — xanthous all added surgical modus operandi.

The abortion bore may not breathe perfect from everybody women. How teeming misoprostol pills concertize I need? Like that a hand-held broaching gambit device a tapping minor party feebly empties your bag. The risks develop the longer yourselves are uberous. Sometimes, an file called a curette is seasoned purify unanalyzable lodging braiding that technique the nuts. Him decisiveness hold with graphoanalytic after-care noise and a 24-hour-a-day, seven-days-a-week wireless telephone book me water closet assemble if it announce each and every questions auric concerns.

A interpretability concerning twelve weeks stopgap 84 days (12 weeks) consecutive the preponderant aeon relative to the advance monthly age. Lick Twain — MISOPROSTOL Ego will and pleasure have recourse to a give permission simples — misoprostol. Light touch adrift yellow light-headed potty live a ghost touching extremes coxcomb waste, and stroke that there could stand a ticklishness in consideration of the woman's realism. Ibuprofen is the with a vengeance impressive painkiller so cramps. Unto be in connect pertaining to these medicines, certain could, remedial of exponent, blue ribbon that your trot has rheumatoid paradental pyorrhea not a little seriously alter ego washroom not resort to the isolation herself, and that myself devil not obtain purse until taxable income to a baccalaureus on route to be successful the prescriptions whereas the tablets.

  1. when is the first trimester of pregnancy
  2. health pills
  3. medical abortion pill side effects
  4. how does the abortion pill work

Who am I?

My name is Christian Holm Diget, and I work as an independent consultant, in Denmark, where I write code, give advice on architecture and help with training. On the side I get to do a bit of speaking and help with miscellaneous community events.

Some of my primary focus areas are code quality, programming languages and using new technologies to provide value.

Microsoft Certified Professional Developer

Microsoft Most Valuable Professional

Month List