<![CDATA[Greg Shackles]]>https://gregshackles.com/https://gregshackles.com/favicon.pngGreg Shackleshttps://gregshackles.com/Ghost 4.32Mon, 27 Jul 2026 16:32:34 GMT60<![CDATA[Analyzing .NET Dependencies with Neo4j]]>

Recently I was doing some planning work for one of our larger repositories to determine how we might approach splitting it up, and wanted to start asking a lot of questions about the project dependencies within it. There are various great tools out there like NDepend to help analyze complexity

]]>
https://gregshackles.com/analyzing-net-dependencies-with-neo4j/61ce48a0437e8200017d4164Mon, 30 Dec 2019 22:22:11 GMT

Recently I was doing some planning work for one of our larger repositories to determine how we might approach splitting it up, and wanted to start asking a lot of questions about the project dependencies within it. There are various great tools out there like NDepend to help analyze complexity and dependencies, but I found myself wanting to really query the data in a lot of different ways, as well as inject it with knowledge we had about our projects such as which ones were part of the deployable artifacts, etc.

Since dependencies are naturally represented as graphs, particularly since they can be nested several levels through chains of dependencies, I figured I'd see if I could easily get the data into a Neo4j database and start querying it that way. It ended up being very easy and worked great, so I thought I'd share a quick version of what I hacked together since it was a fun and useful experiment. For this example I'll use the Xamarin.Forms repository, since it contains a number of different projects and dependencies within it.

Loading the Data

Generating the CSVs

Neo4j makes it nice and easy to import data via CSV files, so that's what I decided to go with. First, choose this option to locate the import folder for your database:

neo4j-importfolder

Make note of the path of that folder, since we'll need to plug that into the next step. Next I reached for F#, my favorite scripting language for stuff like this, and started writing up a quick FSX script to find all csproj files in the repository and parse out their project references. My first pass at this used the XML type provider, but I ran into some parsing issues with it on some project files, and ultimately just dropping down to System.Xml was concise enough that I just stuck with that.

First, some functions for parsing project files and writing out the resulting CSV files:

open System.IO
open System.Xml

let getProjectReferences (path:string) =
  let doc = XmlDocument()
  doc.Load(path)

  doc.GetElementsByTagName "ProjectReference"
  |> Seq.cast<XmlNode>
  |> Seq.map (fun node -> 
    Path.GetFileNameWithoutExtension node.Attributes.["Include"].Value)

let repoPath = @"C:\code\github\xamarin\Xamarin.Forms"
let neoImportPath = @"<your import path here>"

let writeFile name lines =
  File.WriteAllLines(Path.Combine(neoImportPath, name), Array.ofSeq lines)

This also makes the assumption that there is only one project in the repository with a given name, as a means of making things more readable by stripping off .csproj from the file name.

Next, we'll read all csproj files and create a map of their project dependencies:

let allDependencies =
  Directory.EnumerateFiles(repoPath, "*.csproj", SearchOption.AllDirectories)
  |> Seq.map (fun path -> 
    (Path.GetFileNameWithoutExtension path), (getProjectReferences path))

That's all the data we need, so now we just need to write out those CSV files. First, the list of projects:

allDependencies
|> Seq.map (fun (project, _) -> sprintf @"""%s""" project)
|> writeFile "projects.csv"

And then the dependencies:

allDependencies
|> Seq.filter (fun (_, projectDependencies) -> not <| (Seq.isEmpty projectDependencies))
|> Seq.collect (fun (project, projectDependencies) ->
    projectDependencies
    |> Seq.map(fun dependency -> sprintf @"""%s"",""%s""" project dependency))
|> writeFile "dependencies.csv"

Importing the CSVs

Now that those are generated, we just need to import those into the database using a bit of Cypher. First we'll do the projects:

LOAD CSV FROM 'file:///projects.csv' AS row
WITH toString(row[0]) AS name
CREATE (p:Project {name: name})

That will parse out each row in the CSV file and create Project nodes for each of them, assigning the name property based on the value. Next we'll load up the dependencies, matching them against the project nodes we just created, and creating a DEPENDS_ON relationship between each of them:

LOAD CSV FROM 'file:///dependencies.csv' AS row
WITH toString(row[0]) AS dependent, toString(row[1]) AS dependency
MATCH (dependentProject:Project {name: dependent})
MATCH (dependencyProject:Project {name: dependency})
MERGE (dependentProject)-[rel:DEPENDS_ON]->(dependencyProject)
RETURN count(rel)

You can see here that the DEPENDS_ON relationship also indicates the direction of that dependency. Similar to properties on project nodes, if we wanted we could also add properties to the relationships as well, so a future version of this could also include things like package dependencies as well, and indicate the type of dependency as a property on that relationship.

Now we've got all our projects and dependencies loaded into Neo4j and ready to query!

Querying the Data

Let's start simple and query out all the projects and their dependencies and visualize it, using the following query:

MATCH (p:Project) RETURN p

This ends up looking like:
neo4j-graph

Ok, so that alone doesn't end up being super useful since there's a lot going on, but it still says a lot! The Xamarin prefix makes it a little hard to read in this form as well, but clicking through on that center node shows that it's actually Xamarin.Forms.Core which is clearly one of the primary dependencies within this repository.

The visualization side of the graph data is cool, but let's check out some of the types of queries we can easily write based on having this data loaded into a graph database. For example, which projects have the most direct dependencies?

MATCH (dependent:Project)-[DEPENDS_ON]->(dependency:Project)
RETURN dependency.name, COUNT(dependent.name) AS numDirectDependents
ORDER BY numDirectDependents DESC

neo4j-directdependencies

One of the nice things about Cypher is how readable these relationship queries end up being, since the syntax includes the visual representation of them. Those are direct depedencies, but what if we wanted to extent that to include indirect ones as well? All we need to do is add a * into the relationship part of that query and Neo4j takes care of the rest:

MATCH (dependent:Project)-[DEPENDS_ON*]->(dependency:Project)
RETURN dependency.name, COUNT(DISTINCT dependent.name) AS numIndirectDependents
ORDER BY numIndirectDependents DESC

neo4j-indirectdependencies

We can see that Xamarin.Forms.Core is clearly one of the primary dependencies, but what percentage of projects actually depend on it?

MATCH (dependent:Project)
OPTIONAL MATCH (dependent)-[:DEPENDS_ON*]->(dependency:Project {name: "Xamarin.Forms.Core"})
WITH DISTINCT dependent.name as dependentName, 
	CASE dependency.name 
    	WHEN NULL THEN false 
      ELSE true 
	END AS dependsOnCore
RETURN dependsOnCore, COUNT(*)

neo4j-dependsoncore

So five projects don't depend on Xamarin.Forms.Core...what are they?

MATCH (dependent:Project)
WHERE NOT (dependent)-[:DEPENDS_ON*]->(:Project {name: "Xamarin.Forms.Core"})
RETURN DISTINCT dependent.name

neo4j-nodependsoncore


This just scratches the surface of the types of queries you can start writing here, but even just the basics have already proven to be really interesting and valuable as I start to poke at the dependency graph in different ways to see what shakes out, especially when combined with domain-specific information about our projects. Not bad for a quick hack project!

]]>
<![CDATA[Monitoring Akka.NET with Datadog and Phobos: Tracing]]>

In my previous post I started looking at how you can leverage Akka.NET's new Phobos product to start logging actor system metrics to Datadog. In this post I'm going to start taking that a little further by exploring the tracing functionality it offers as well.

]]>
https://gregshackles.com/monitoring-akka-net-with-datadog-and-phobos-tracing/61ce48a0437e8200017d4163Fri, 30 Nov 2018 15:00:13 GMT

In my previous post I started looking at how you can leverage Akka.NET's new Phobos product to start logging actor system metrics to Datadog. In this post I'm going to start taking that a little further by exploring the tracing functionality it offers as well.

Similar to the metrics side, Phobos provides a flexible platform that supports a variety of tracing systems, such as Zipkin, Jaeger, Application Insights, and also the OpenTracing standard. I'm a big fan of OpenTracing, and it just so happens that Datadog's APM solution works well with it, so that's what I'm going with here. Just like with metrics, though, if you wanted to go with a different platform then all you'll need to change is configuration, and your code remains the same.

Setting Up OpenTracing

I'm going to stick with the basic Greeter example I used in the last post here, since for now I really just want to see what we get out of the box. In later posts we'll explore some more interesting actor systems.

Just as a reminder, this is all the actor actually does:

Receive<Greet>(msg =>
    Console.WriteLine($"Hello, {msg.Who}"));

To set up logging to OpenTracing there's only a few quick things that need to be done. First we need these NuGet packages:

  • OpenTracing
  • Datadog.Trace.OpenTracing

Once those are installed, we can initialize a global tracer in our application prior to setting up the actor system:

var tracer = Datadog.Trace.OpenTracing.OpenTracingTracerFactory.CreateTracer();
GlobalTracer.Register(tracer);

This creates a tracer using Datadog's OpenTracing factory and registers it as the global tracer, which is a static instance. Any traces reported to this tracer will be logged down to the local Datadog agent on the machine.

Finally, just like everything else in Akka.NET we need to add a little bit of HOCON to tell it how to wire up tracing:

phobos {
    tracing {
        provider-type = default
    }
}

By default here it'll look for that global tracer we registered, and if it finds one it'll use that. This is why you'll want to make sure to register it prior to spinning up your actor system.

Initial Traces

Since we didn't really need to do much there to get tracing going, this section is mainly going to be images and an introduction to Datadog's APM interface. Let's see what we've got!

The first area you'll see is a list of services that registered traces:

APM service list

Akka.NET will register a service with a default name based on the application, which in our case becomes greeter-csharp. In this overview you can see a glimpse of average latency, how many requests the service is fulfilling, error rate, etc. This list becomes much more useful when you have a bunch of services, but you have to start somewhere.

Next, let's click through into the service itself and look at its own overview:

APM service overview

This view gives a nice high level view of how the service is doing, showing the total requests, distribution of their latencies broken into percentiles, and statistics for specific resources within that service. Phobos will create a resource for each actor in your system automatically, so here you can see the /user/greeter actor showing up as a distinct resource here. In a normal actor system you'd have a lot of different actors, so this would allow you to see them broken out individually.

Now let's click through into that actor resource and see its overview. Overall it'll look a lot like the previous screen, except that in the place of the resource list you'll find a list of actual traces that were captured for it:

APM trace list

By default Phobos will sample 100% of all traces, but you configure this easily with one line of HOCON if you want to only sample a percentage of messages in your system (which is generally a good idea for production systems).

Now let's click into one of those traces and see what we get:

Initial APM trace

Here you can see a flame graph with one span, which isn't particularly interesting on its own, but you can still see it register the duration of the call into that actor. Below that you can see all the metadata Phobos recorded with the span, which includes things like the actor path, message type, the sender, and more. This information is powerful since later on we can query our traces based on this metadata.

One of the other nice things here is the host info tab, where you can see how the underlying host of that actor was doing at the time of the trace:

APM trace's host info

This allows you to correlate things like CPU or memory usage with what your application was actually doing at the time, which is also quite powerful.

Getting Deeper

That's a glimpse of what you get effectively for free - we didn't have to make any changes to our actors and we got all that great information - but that still only goes so far. What if we want to layer in some more information?

Tags

In that initial trace we talked about the default metadata that Phobos provides for the trace. One of the nice things about that metadata is you can easily pile on your own data as well.

If you're familiar with most time-series databases (Datadog included), you're probably just as frustrated as I am about the inability to include high-cardinality fields in your metrics. That's still frustrating, but one awesome thing about Datadog's APM offering is that they offer infinite cardinality for tags there so you can tack on any information that's valuable to you. That's huge!

First, we'll want to get a reference to the Phobos actor context in our actor, just like we did in the last post to log custom metrics:

private readonly IPhobosActorContext _instrumentation = Context.GetInstrumentation();

With that in place, we can just add one line to our message handler to log a tag against the currently active span with the contents of the message:

Receive<Greet>(msg =>
{
    _instrumentation.ActiveSpan.SetTag("who", msg.Who);

    Console.WriteLine($"Hello, {msg.Who}");
});

What's nice here is that you don't need to do anything special to keep track of the current span you're operating under - Phobos handles that for you. Now when we look at the trace in Datadog you'll see the new bit of metadata from that tag:

Custom tag logged in the trace

You'll want to be careful not to log sensitive information in these tags, naturally, but this is a really great way to include relevant diagnostic information to your traces to help you understand what your system was doing at that time.

Spans

If you looked at that initial flame graph and found it uninteresting, I'm with you - it's not much of a flame graph with only one span like that. Let's add some more!

Since we're still in this little contrived example of an actor system, let's just add a little bit of code that pretends to do some intermittent work during the message handler:

for (var i = 0; i < 5; i++)
{
    using (_instrumentation.Tracer.BuildSpan("nap-time").WithTag("iteration", i).StartActive())
        System.Threading.Thread.Sleep(100);

    System.Threading.Thread.Sleep(50);
}

Here we loop five times, creating a span each time doing 100ms of "work", tagging each one with the iterator's index, and pausing 50ms between each one. Don't do this in a real app, of course, but let's just pretend these sleeps are network or database calls. Now if we take a look at the flame graph it'll look a bit more interesting:

Flame graph with new spans

We can see the five nap spans explicitly, each tagged with the data we gave it. Another interesting view that Datadog provides is the list tab, which presents the same data in a different form:

List view of new spans

Now we can start to get a better sense of where our actor was spending its time while it was handling the message, and in a nice visual way that doesn't require spelunking through a lot of log files to piece things together.


While this is just the basics of what tracing offers, you can hopefully already see how powerful a tool it can be in really observing how your systems are behaving. That said, this sort of thing is generally referred to as "distributed tracing" for a reason, so in future posts we'll explore what this looks like with multiple actors that communicate with each other like a real system would!

]]>
<![CDATA[Monitoring Akka.NET with Datadog and Phobos: Metrics]]>

If you're here on my blog, you're probably well aware that I'm a fan of both Akka.NET and Datadog, and observability in general. In fact, I even blogged last year about creating my own Datadog sink for Akka.Monitoring (which is still available

]]>
https://gregshackles.com/monitoring-akka-net-with-datadog-and-phobos-metrics/61ce48a0437e8200017d4162Wed, 28 Nov 2018 14:59:43 GMT

If you're here on my blog, you're probably well aware that I'm a fan of both Akka.NET and Datadog, and observability in general. In fact, I even blogged last year about creating my own Datadog sink for Akka.Monitoring (which is still available on NuGet and we still use it in production every day!).

This scratched some of my itches in terms of getting visibility into my actor systems, but it still fell a little short of what I wanted. For one, it still required adding code like this to my actors:

protected override void PreStart()
{
    Context.IncrementActorCreated();
    base.PreStart();
}

Definitely not the end of the world by any stretch, but still not ideal! It also doesn't play quite as nice with the F# side of things, where I like to define my actors as pure functions instead of using the OO APIs.

Additionally, when building distributed systems it becomes increasingly important to incorporate some sort of distributed tracing into the system to help you diagnose how it's behaving. This was definitely possible before as well, but would again require baking it all myself into all my actors.

Enter Phobos

This is why I got very excited when I saw Petabridge introduce Phobos earlier this year. Phobos aims to provide a stronger out-of-the-box offering around monitoring and distributed tracing, often without needing to make any actual changes to your actor code. Like everything else in Akka.NET, it's highly configurable, and cross-platform as well by nature of .NET Standard 2.0 (in a future post I'll certainly test this in a mobile app!). It also provides integrations into many well-known and established standards like StatsD, Application Insights, OpenTracing, and Zipkin.

Needless to say, this is all very relevant to my interests. As I start to really dig into Phobos and how to integrate it into my Datadog-driven world, I figured I'd try to write up some of my experiences and what it looks like to actually use it.

Metrics

There's a lot of areas to explore, but I figured I'd start with the basics: metrics. Ultimately I'd really love to replace my usage of my NuGet packag mentioned earlier with Phobos, or at least be able to ditch all the custom calls like Context.IncrementActorCreated(). Since Datadog speaks StatsD, that seems like the best place to start.

Default Metrics

Since I really want to see the out-of-the-box experience, I'll start with the simplest actor system in the world and use my Greeter sample application. It contains a single actor that, given a name, echoes out Hello, {name} to the console...clearly the type of problem for which distributed systems were developed.

C#

I'll start with the C# version first, but then we'll check out the F# one to see if things work there too. First I'll need to add a couple new NuGet references to the project:

  • Phobos.Actor
  • Phobos.Monitoring.StatsD

With those installed, all I need to do is set up the HOCON configuration and use it when spinning up the actor system:

var config = ConfigurationFactory.ParseString(@"
    akka.actor {
        provider = ""Phobos.Actor.PhobosActorRefProvider,Phobos.Actor""
    }

    phobos {
        monitoring {
            provider-type = statsd
            statsd {
                endpoint = 127.0.0.1
                port = 8125
            }
        }
    }");
    
using (var system = ActorSystem.Create("my-system", config))

Here we specify that the actor provider should be the Phobos one, to use the StatsD monitoring provider, and where to find the StatsD listener. In my case it'll be the local Datadog agent running on the host. There are more options you can configure as well, including which actors you want to opt in/out of monitoring, but we'll just stick with the defaults and monitoring all the things.

Fake StatsD Listener

One thing I like to do when testing StatsD metrics is to set up a little TCP listener on that port that just spits out the messages it receives. StatsD is a dead simple protocol, so it can be a useful way to see what's being reported, as well as a good way to learn how the protocol works. Here's an example Node script I sometimes use to do this:

const dgram = require('dgram');
const server = dgram.createSocket('udp4');

const log = message => console.log(`[${new Date().toUTCString()}] ${message}`);

server.on('message', message => log(message.toString()));
server.on('listening', () => {
    var address = server.address();

    log(`UDP Server listening on ${address.address}:${address.port}`);
});

server.bind(8125, '127.0.0.1');

With that running, let's fire up the actor system and see what we get:

[Wed, 28 Nov 2018 04:33:35 GMT] UDP Server listening on 127.0.0.1:8125
[Wed, 28 Nov 2018 04:33:46 GMT] 
my-system.my-system.akka.actor.created:1|c
my-system.akka.actor.created:1|c
my-system.user.greeter.messages.received:1|c
my-system.my-system.user.greeter.messages.received:1|c
my-system.Greeter.CSharp.GreetingActor.messages.received:1|c
my-system.my-system.Greeter.CSharp.GreetingActor.messages.received:1|c
my-system.my-system.akka.messages.received:1|c
my-system.akka.messages.received:1|c

Remember that all we changed was configuration, and not any actor code! Out of the box we got metrics reported for the creation of the system, the actor, and a variety of variants of the messages received. I'll be looking for ways to consolidate this down and make use of tags to clean it up, similar to what I did in my NuGet package, but this is a great start!

F#

Ok, let's try this in F#!

let config = ConfigurationFactory.ParseString("""
    akka.actor {
        provider = "Phobos.Actor.PhobosActorRefProvider,Phobos.Actor"
    }

    phobos {
        monitoring {
            provider-type = statsd
            statsd {
                endpoint = 127.0.0.1
                port = 8125
            }
        }
    }""");

use system = ActorSystem.Create("my-system", config)

This is basically the same code we had in C# - wire up the system using HOCON and we're off to the races:

[Wed, 28 Nov 2018 04:42:22 GMT] 
my-system.my-system.akka.actor.created:1|c
my-system.akka.actor.created:1|c
my-system.user.greeter.messages.received:1|c
my-system.my-system.user.greeter.messages.received:1|c
my-system.Akka.FSharp.Actors+FunActor`2[[Program+Message, Greeter.FSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[System.Object, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].messages.received:1|c
my-system.my-system.akka.messages.received:1|c
my-system.akka.messages.received:1|c

Pretty much the same thing! The amusing exception here is where it tries to create a metric using the actor's class name, which has interesting results for function-based actors :) Either way, it's awesome that the underlying system lit up and started logging these metrics without changing any actor code.

Hook up Datadog

Now that we've got StatsD metrics printing to the console, I'll swap out my little UDP listener with the actual Datadog agent. Nothing has to change in the actor system configuration for this - I just have to turn off the custom listener and start the Datadog service.

Once that's up and running, the metrics start appearing in Datadog as expected and I can start creating alerts and dashboards based on them:

Metrics showing up in Datadog

More Metrics

That's what you get by default, but what if you want to sprinkle in some more metric goodness? Let's do that.

Mailbox Length

One of the things I like to have an eye on in my systems is the length of an actor's mailbox, in order to get an indication of whether it's falling behind on processing or something is wrong. Phobos actually makes that dead simple, by exposing it as a configuration property. Just update the monitoring block in the HOCON and you're good to go:

phobos {
    monitoring {
        monitor-mailbox-depth = on

WIth that in place you'll see metrics like these being reported as gauges:

my-system.user.greeter.mailbox.queuelength:0|g
my-system.my-system.user.greeter.mailbox.queuelength:0|g
my-system.my-system.Greeter.CSharp.GreetingActor.mailbox.queuelength:0|g

Custom Metrics

You'll probably also come across situations where you want to log your own custom metrics as well. If you're using something like StatsD you could do that directly through that, but wouldn't it be nicer to be able to log your custom metrics through the same pipeline as the rest of the Phobos metrics?

The story here is a bit better in C# than F#, for similar reasons to the old Akka.Monitoring stuff. What you can do is use an actor's context to get its Phobos context:

private readonly IPhobosActorContext _instrumentation = Context.GetInstrumentation();

Once you have that, you can use its Monitor property to send metrics through the system:

_instrumentation.Monitor.IncrementCounter("awesome-counter", 1);

With that in place you'll see it come through like all the rest of the counters:

my-system.user.greeter.awesome-counter:1|c
my-system.my-system.user.greeter.awesome-counter:1|c
my-system.Greeter.CSharp.GreetingActor.awesome-counter:1|c
my-system.my-system.Greeter.CSharp.GreetingActor.awesome-counter:1|c

The API exposed on IMonitor doesn't currently allow for passing through tags with a metric, but I'm hoping this can be added in future versions. Either way, with that one line of code we've got a custom metric going through Phobos to StatsD and ultimately Datadog. If down the line we wanted to switch to Application Insights or anything else, it would just be a configuration change and everything else would stay the same.


There's a lot more to explore with Phobos, but it's exciting to see this sort of functionality starting to get baked right into the framework (and its supporting packages). In the next post I'll start to look at some of the distributed tracing functionality available in Phobos, and how we can expose that in Datadog's APM tools.

]]>
<![CDATA[Tracking Identity Column Saturation in SQL Server with Datadog]]>

Int32 ought to be enough for any table's identity column

-- Most developers at some point

We've all done it, creating a new table in SQL Server and giving it a nice auto-incrementing integer as the primary key. There's no way that table will

]]>
https://gregshackles.com/tracking-identity-column-saturation-in-sql-server-with-datadog/61ce48a0437e8200017d4161Thu, 19 Jul 2018 18:15:05 GMT

Int32 ought to be enough for any table's identity column

-- Most developers at some point

We've all done it, creating a new table in SQL Server and giving it a nice auto-incrementing integer as the primary key. There's no way that table will ever reach 2,147,483,647 rows, right? Now, for most tables that's likely true, but the last thing you want is to be surprised when suddenly you can no longer insert into your table due to using up that full Int32 space.

In our system there are some tables we knew would be reaching those limits in the relatively near future, and as part of preparing for the migration I wanted to get some good observability on how close we were to the limits and how it was trending. Again, the last thing we wanted was to be surprised. Naturally for me this meant wanting to get this into Datadog so it can easily be visualized and alerted on.

Defining the Agent Job

One way to implement this would have been to create a normal Datadog agent check and run the query that way. This time around I wanted to see how easily I could do it via scheduled SQL Server Agent jobs and report the metric down to the local Datadog agent via UDP directly. You can define agent jobs in PowerShell, so it ended up being very straightforward.

First I defined a couple variables to declare which database and table to look at:

$table = 'MyTable'
$database = 'MyDatabase'

For now this is just limited to one table, but sets the stage for being able to layer in more down the line, perhaps simply querying everything.

Next, I used IDENT_CURRENT to grab the current identity value for that table, and calculate the percentage used (note that this is assuming an integer column type here):

$pctUsed = (Invoke-Sqlcmd `
             -Query "SELECT (IDENT_CURRENT('$database.dbo.$table') / 2147483647) * 100 
                     AS PctUsed" `
             -ServerInstance "sql-server-host" `
           ).PctUsed

With that I construct a metric, tagged with the database and table, and send that to the local Datadog agent:

$message = "olo.database.identity_space_used:$pctUsed|g|#table:$table,database:$database"
$messageBytes = [Text.Encoding]::ASCII.GetBytes($message)

$socket = New-Object System.Net.Sockets.UDPClient 
$socket.Send($messageBytes, $messageBytes.Length, "127.0.0.1", 8125) 
$socket.Close() 

And that's it! I can set this agent job on a schedule and get regularly reported metrics flowing into Datadog to track the column saturation where I need it. Since this is PowerShell I could also have just pulled in the Datadog client library as well, but in this case it was nicer to keep it dependency-free and construct the message manually, since it's straightforward enough.

]]>
<![CDATA[Styling Xamarin.Forms Apps with CSS]]>

Some months ago a feature landed in Xamarin.Forms that seemed to truly polarize the Xamarin.Forms community: support for styling applications using CSS. Some argued that it was an unnecessary introduction to "Web" technology to the native development experience, and others that it simply isn't

]]>
https://gregshackles.com/styling-xamarin-forms-apps-with-css/61ce48a0437e8200017d415fTue, 05 Jun 2018 16:57:44 GMT

Some months ago a feature landed in Xamarin.Forms that seemed to truly polarize the Xamarin.Forms community: support for styling applications using CSS. Some argued that it was an unnecessary introduction to "Web" technology to the native development experience, and others that it simply isn't the right solution to the problem.

While I sympathize with the latter opinion and think there's plenty of room for some good debate on the right path forward, I count myself as part of a third camp: I think that CSS is a powerful (and frequently maligned) solution to the problem of styling native mobile applications.


Read the rest of the article over at Visual Studio Magazine.

]]>
<![CDATA[Writing .NET Core Global Tools with F#]]>

The release of .NET Core 2.1 brought with it a bunch of great additions, and one of the ones I've been looking forward to the most is the addition of support for creating global tools. This has always been a great feature in the JavaScript world, allowing

]]>
https://gregshackles.com/writing-net-core-global-tools-with-fsharp/61ce48a0437e8200017d415eThu, 31 May 2018 13:30:00 GMT

The release of .NET Core 2.1 brought with it a bunch of great additions, and one of the ones I've been looking forward to the most is the addition of support for creating global tools. This has always been a great feature in the JavaScript world, allowing you to write and distribute command-line tools via npm.

F# has long been my favorite go-to language for scripting, and as such I've compiled quite a few F# scripts that I run regularly for a variety of things. With the addition of global tooling support in .NET Core, now I can create actual command-line tools from those scripts and even distribute them via NuGet!

Introducing: fsharpsay

I'll admit up front that in order to come up with an easy example to show off, I decided to reinterpret Microsoft's dotnetsay example from the aforementioned blog post. This one will be totally different, though...I'll use the F# logo instead of .NET Bot! Let's take a look at how easy it is to create a new tool.

First, we'll create a new F# console app:

dotnet new console -lang F#

In the generated fsproj file add a PackAsTool element such that the whole file looks like:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <RootNamespace>FSharpSay</RootNamespace>
    <PackAsTool>true</PackAsTool>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Program.fs" />
  </ItemGroup>
</Project>

Next we'll create a function that takes in a message and prints it coming from the F# logo:

let sayIt = printfn @"
            %s
            __________________
                              \
                                \
                                `  ` 
                              `/+  /:` 
                            `/ss+  /++:`
                          `/ssss+  /++++:.
                        ./ssssss+  /++++++/.
                      ./ssssssss+  /++++++++/.
                    ./ssssssssss+  /++++++++++/.
                  ./ssssssssssss+  +++++++++++++/-
                ./ssssssssssssss/  /++++++++++++++/-`
              ./ssssssssssssss+.    ./+++++++++++++++-`
            ./ssssssssssssss+.        ./+++++++++++++++-`
          .+ssssssssssssss+.  `:+       ./+++++++++++++++:`
        .+ssssssssssssss+.  `:os+         ./+++++++++++++++:`
      .+ssssssssssssss+.  `:osss+           ./+++++++++++++++:`
    .+ssssssssssssss+.  `:osssss+             ./+++++++++++++++/.
  .+ssssssssssssss+.  `:osssssss+               ./+++++++++++++++/.
  /sssssssssssssso-   .osssssssss+                 -++++++++++++++++:
  -+ssssssssssssso/`  `:osssssss+               `:+++++++++++++++/.
    -+ssssssssssssso/`  `:osssss+             `:+++++++++++++++/.
      -+ssssssssssssss/.  `:osss+           `:+++++++++++++++/.
        -+ssssssssssssss/.  `:os+         .:+++++++++++++++:.
          -+ssssssssssssss/.  `:+       ./+++++++++++++++:`
            .+ssssssssssssss/.  `     ./+++++++++++++++:`
              .+ssssssssssssss/.    ./+++++++++++++++:`
                .+ssssssssssssss/  :+++++++++++++++-`
                  .+ssssssssssss+  /+++++++++++++-` 
                    .+ssssssssss+  /++++++++++/-` 
                      .+ssssssss+  /++++++++/-  
                        .+ssssss+  /++++++/. 
                          .+ssss+  /++++/. 
                            .+ss+  /++/. 
                              .++  /:. 
                                `  `      "

F# is known for brevity, but unfortunately there's not much that can be done with the big logo!

Finally, the entry point for the app:

[<EntryPoint>]
let main argv =
    match argv with 
        | [|message|] -> message
        | _ -> "F# rocks!"
    |> sayIt

    0

That's actually all you need. A global tool really is just a console app, meaning you can even test it out via dotnet run the way you normally would for a console app.

Packaging and Installing

Now let's go ahead and create a redistributable tool out of the app by creating a NuGet package:

dotnet pack -c release -o nupkg

This will create a standard nupkg package for the app, which you could then push to NuGet itself or any other feed of your choosing. For now, let's just install it from the local file:

dotnet tool install --add-source ./nupkg -g fsharpsay

By specifying -g, we're saying that we want this tool to be available globally on the machine.

Running the Tool

Now that it's installed, we can go ahead and run it from the command line the same way you would any other app in your path:

> fsharpsay "F# rocks"

            F# rocks
            __________________
                              \
                                \
                                `  ` 
                              `/+  /:` 
                            `/ss+  /++:`
                          `/ssss+  /++++:.
                        ./ssssss+  /++++++/.
                      ./ssssssss+  /++++++++/.
                    ./ssssssssss+  /++++++++++/.
                  ./ssssssssssss+  +++++++++++++/-
                ./ssssssssssssss/  /++++++++++++++/-`
              ./ssssssssssssss+.    ./+++++++++++++++-`
            ./ssssssssssssss+.        ./+++++++++++++++-`
          .+ssssssssssssss+.  `:+       ./+++++++++++++++:`
        .+ssssssssssssss+.  `:os+         ./+++++++++++++++:`
      .+ssssssssssssss+.  `:osss+           ./+++++++++++++++:`
    .+ssssssssssssss+.  `:osssss+             ./+++++++++++++++/.
  .+ssssssssssssss+.  `:osssssss+               ./+++++++++++++++/.
  /sssssssssssssso-   .osssssssss+                 -++++++++++++++++:
  -+ssssssssssssso/`  `:osssssss+               `:+++++++++++++++/.
    -+ssssssssssssso/`  `:osssss+             `:+++++++++++++++/.
      -+ssssssssssssss/.  `:osss+           `:+++++++++++++++/.
        -+ssssssssssssss/.  `:os+         .:+++++++++++++++:.
          -+ssssssssssssss/.  `:+       ./+++++++++++++++:`
            .+ssssssssssssss/.  `     ./+++++++++++++++:`
              .+ssssssssssssss/.    ./+++++++++++++++:`
                .+ssssssssssssss/  :+++++++++++++++-`
                  .+ssssssssssss+  /+++++++++++++-` 
                    .+ssssssssss+  /++++++++++/-` 
                      .+ssssssss+  /++++++++/-  
                        .+ssssss+  /++++++/. 
                          .+ssss+  /++++/. 
                            .+ss+  /++/. 
                              .++  /:. 
                                `  `

Available on NuGet

Clearly this is a must-have tool that the world needs, so I went ahead and made the source available on GitHub and published the tool to NuGet. To install it, simply run this and you're good to go:

dotnet tool install -g fsharpsay

Now if you'll excuse me, I've got some more global tools to convert!

]]>
<![CDATA[Checking Savings Bond Values with F#, Docker, and Azure]]>

When I was growing up some of my family insisted on giving me savings bonds for things like Christmas and my birthday, in what I assume is was a frugal way to teach me about very (very) delayed gratification. I still have a bunch, but the value isn't

]]>
https://gregshackles.com/checking-savings-bond-values-with-f-docker-and-azure/61ce48a0437e8200017d415dSun, 14 Jan 2018 22:42:04 GMTChecking Savings Bond Values with F#, Docker, and Azure

When I was growing up some of my family insisted on giving me savings bonds for things like Christmas and my birthday, in what I assume is was a frugal way to teach me about very (very) delayed gratification. I still have a bunch, but the value isn't particularly high and the process of checking their value is tedious, so I generally only end up checking it maybe every five years or so.

I figured it was about time to check again, but this time I wanted to think about what I could automate. The idea: read in a list of bonds from a CSV file, automatically enter those into the website for checking their value, and report their total value to me. Even having to remember to run this manually seems a bit tedious, so I also want it to automatically run every month and email me the results.

The App

For the app itself I decided to do it as an F# console app, and take advantage of canopy, a lovely library for automated browser testing. Using FSharp.Data's CSV type provider, I defined a simple time to match what's in the data file:

type Bonds = CsvProvider<HasHeaders = false, Schema = "SerialNumber(string),IssueDate(string)">

Next, I'll start running Chrome and browse to the site:

start chrome
url "https://www.treasurydirect.gov/BC/SBCPrice"

Now I want to enter each bond into the form on the page and submit it. Canopy makes this really easy and consise:

Bonds.Load("c:\\bonds.csv").Rows
    |> Seq.iter (fun bond ->
        let denomination = match bond.SerialNumber with
                           | serial when serial.StartsWith("L") -> 50
                           | serial when serial.StartsWith("C") -> 100
                           | _ -> failwith "Unknown bond denomination"

        "select[name=Denomination]" << string denomination
        "input[name=SerialNumber]" << bond.SerialNumber
        "input[name=IssueDate]" << bond.IssueDate
        click "input[name='btnAdd.x']"
)

With just those few lines of code, all the bonds will have been entered into the site and the totals are now available to read out:

let totals = elements "table#ta1 tr:nth-child(2) td"
let lines = seq {
    yield sprintf "Total Value: %s" totals.[1].Text
    yield sprintf "Total Price Paid: %s" totals.[0].Text
    yield sprintf "Total Interest: %s" totals.[2].Text
    yield sprintf "YTD Interest: %s" totals.[3].Text
} 
let content = lines |> String.concat "<br />"

For now this will just be a boring little report, with these four items printed on separate lines. Finally, all we need to do is send the email and quit the browser. To send the email I'm using a SendGrid account:

let client = SendGridClient(Environment.GetEnvironmentVariable "SendGridApiKey")
let emailAddress = EmailAddress("[email protected]", "Savings Bond Calculator")

MailHelper.CreateSingleEmail(emailAddress, emailAddress, "Savings Bond Values", content, content)
|> client.SendEmailAsync
|> Async.AwaitTask
|> Async.Ignore
|> Async.RunSynchronously

quit()

That's the entire app! Running it successfully reminds me how little my stack of bonds is worth. I did say I wanted to run this on a schedule, though, so let's kick things up a notch.

Dockerizing the App

In order to run this easily on a machine other than my own, I decided to go ahead and create a Docker image for it that I can run in Azure. Here's my Dockerfile:

FROM microsoft/windowsservercore
COPY bin/Release/ /

ADD http://chromedriver.storage.googleapis.com/2.35/chromedriver_win32.zip /
ADD https://dl.google.com/tag/s/dl/chrome/install/googlechromestandaloneenterprise64.msi /

RUN msiexec /i googlechromestandaloneenterprise64.msi /quiet

ENTRYPOINT BondCalculator.exe

I create a Windows image, copy over the compiled app, install Chrome and the Chrome web driver, and then finally run the task. Once the app finishes the image will terminate, since there's no reason to keep anything running most of the time for this.

I also threw together a little Powershell script to build and create the image:

Invoke-Expression "msbuild /p:VisualStudioVersion=14.0 /p:Configuration=Release"

Copy-Item C:\bonds.csv bin\Release\bonds.csv

Invoke-Expression "docker build -t bond-calculator ."

Once that runs we now have a ginormous 11.2GB image for the app:

> docker images --format "{{.Repository}}: {{.Size}}"
bond-calculator: 11.2GB

And now we can run the app with docker run:

docker run --env SendGridApiKey=<key> bond-calculator

Pushing to Azure

I have a private container registry in Azure that I can use to store my containers, so I need to push this image out to that:

docker tag bond-calculator myregistry.azurecr.io/bond-calculator
docker push myregistry.azurecr.io/bond-calculator

With that pushed, I can now use the Azure CLI to create a new container instance to actually run it in Azure:

az container create `
    --resource-group mygroup `
    --name bond-calculator `
    --image myregistry.azurecr.io/bond-calculator:latest `
    --cpu 1 --memory 1 `
    --registry-password topsecret `
    --restart-policy OnFailure `
    --os-type Windows `
    --environment-variables SendGridApiKey=<key>

This creates a new container with 1 CPU and 1GB of memory and runs my app on it. Sure enough, a few minutes later a new email report shows up in my inbox! Using the Azure CLI again we can look up more details about the container:

> az container show --resource-group gshackles --name bond-calculator
...
        "currentState": {
          "detailStatus": "Completed",
          "exitCode": 0,
          "finishTime": "2018-01-14T16:34:53+00:00",
          "startTime": "2018-01-14T16:34:15+00:00",
          "state": "Terminated"
        },
...

The container ran for 38 seconds, so that's all I'm going to have to pay for. I think I can live with that.

Running On A Schedule

Now that I've got it running in Azure, I want to get it running on a schedule so that I don't need to actually trigger it myself. At first I figured I'd just write up a quick Azure Function to do it, but that didn't pan out so well. I had planned to just write a little Powershell function that executes monthly and issues the Powershell equivalent of that az container create command above, but it turns out that the AzureRM modules loaded in the function host are pretty old, and don't yet include the cmdlets for container instances.

After toying around with a few different hacky ideas, I realized that Logic Apps actually have support for doing just what I need. I put together a simple little workflow that triggers monthly and creates a container instance with the same parameters as earlier:

Checking Savings Bond Values with F#, Docker, and Azure

Just to make sure it all still worked, I triggered a run for the logic app and sure enough, a new email arrived a little while after:

Checking Savings Bond Values with F#, Docker, and Azure


Was this all overkill for what I actually needed here? Almost certainly! In the end, it was a fun exercise to put all these pieces together like this, and turned out to be a pretty great option for these types of scenarios where I need something running on a schedule and it can't be supported by Azure Functions.

For anyone interested, the code for this app is all available on GitHub.

]]>
<![CDATA[Getting Started with Augmented Reality in iOS 11]]>

Over the last several years augmented reality (AR) has become a hot topic across all platforms and technology sectors. Apple's release of iOS 11 included a new framework called ARKit that aims to make it easy for developers to add AR experiences into their apps without a lot

]]>
https://gregshackles.com/getting-started-with-augmented-reality-in-ios-11/61ce48a0437e8200017d415cFri, 08 Dec 2017 15:08:31 GMT

Over the last several years augmented reality (AR) has become a hot topic across all platforms and technology sectors. Apple's release of iOS 11 included a new framework called ARKit that aims to make it easy for developers to add AR experiences into their apps without a lot of hassle or ceremony. While it's still a little limited in its initial form, Apple was still able to create an approachable framework for incorporating AR into apps, even for developers without much (if any) 2-D or 3-D programming experience.

In this article, I won't dive deep into how ARKit works. Instead, I'll walk through creating an AR app from scratch to demonstrate how it fits together. Because I've always thought my household could use more minions (of the "Despicable Me" variety) hanging around, I'll create an app that allows me to place a 3-D minion anywhere I'd like just by tapping on a spot in my house from within the app.

Check out the rest of the article over at Visual Studio Magazine

]]>
<![CDATA[A Mobile DevOps Retrospective, Part III: Measurement, the Last Mile]]>

As I mentioned recently in parts one and two, I recently had the pleasure of writing a series of guest posts for Microsoft's App Center blog about mobile DevOps and our experiences at Olo. The third (and final) post in the series is now available, enjoy!

A Mobile

]]>
https://gregshackles.com/a-mobile-devops-retrospective-part-iii-measurement-the-last-mile/61ce48a0437e8200017d415bWed, 06 Dec 2017 02:30:00 GMT

As I mentioned recently in parts one and two, I recently had the pleasure of writing a series of guest posts for Microsoft's App Center blog about mobile DevOps and our experiences at Olo. The third (and final) post in the series is now available, enjoy!

A Mobile DevOps Retrospective, Part III: Measurement, the Last Mile

]]>
<![CDATA[A Mobile DevOps Retrospective Part II: Automation]]>

As I blogged about recently, I had the pleasure of writing a series of guest posts for Microsoft's Mobile Center blog about mobile DevOps and our experiences at Olo. I'm happy to share that part two is now available, and the third will be out soon.

]]>
https://gregshackles.com/a-mobile-devops-retrospective-part-ii-automation/61ce48a0437e8200017d415aTue, 31 Oct 2017 15:24:50 GMT

As I blogged about recently, I had the pleasure of writing a series of guest posts for Microsoft's Mobile Center blog about mobile DevOps and our experiences at Olo. I'm happy to share that part two is now available, and the third will be out soon. Enjoy!

A Mobile DevOps Retrospective Part II: Automation

]]>
<![CDATA[Using Akka.NET Actor Systems in Xamarin Apps]]>

Akka.NET is a great toolkit for building concurrent and fault-tolerant systems by way of the actor model. Most think of actor systems as something you would just do on the server side of things, as part of building large distributed systems, but the approach works great for all sorts

]]>
https://gregshackles.com/using-akka-net-in-xamarin-apps/61ce48a0437e8200017d4159Sun, 08 Oct 2017 18:41:08 GMT

Akka.NET is a great toolkit for building concurrent and fault-tolerant systems by way of the actor model. Most think of actor systems as something you would just do on the server side of things, as part of building large distributed systems, but the approach works great for all sorts of applications. The recent release of Akka.NET 1.3 brought with it support for .NET Standard 1.6, so naturally I needed to try using it from a Xamarin app.

To give it a spin, I'll build a simple Xamarin.Forms app that takes a URL and begins crawling it for links, reports back what it finds, crawls those new links, and so on. Something like this wouldn't be too difficult to model without the actor model, but actors work really well for this type of scenario.

Message Types

Let's define some message types for communicating progress during the crawl. First, a command to initiate scraping a given URL:

public class Scrape 
{
    public string Url { get; }

    public Scrape(string url) => Url = url;
}

Then we'll need a message to communicate the contents of a URL after it's downloaded:

public class DownloadUrlResult
{
    public string Html { get; }

    public DownloadUrlResult(string html) => Html = html;
}

Finally we need a message to communicate the final results of scraping a URL:

public class ScrapeResult
{
    public string Url { get; }
    public string Title { get; }
    public IList<string> LinkedUrls { get; }

    public ScrapeResult(string url, string title, IList<string> linkedUrls)
    {
        Url = url;
        Title = title;
        LinkedUrls = linkedUrls;
    }
}

The Actor System

Now we can define some actors to do the work.

ScrapeActor

First we'll create an actor that is responsible for downloading and parsing a URL, and then messaging the results back to its parent:

public class ScrapeActor : ReceiveActor
{
    public ScrapeActor(IActorRef parent)
    {
        Receive<Scrape>(msg => OnReceiveScrape(msg));
        Receive<ScrapeResult>(msg => parent.Forward(msg));
    }

    private void OnReceiveScrape(Scrape msg)
    {
        var config = Configuration.Default.WithDefaultLoader();

        BrowsingContext.New(config).OpenAsync(msg.Url).ContinueWith(request =>
        {
            var document = request.Result;
            var links = document.Links
                                .Select(link => link.GetAttribute("href"))
                                .ToList();

            return new ScrapeResult(document.Url, document.Title, links);
        }, TaskContinuationOptions.ExecuteSynchronously).PipeTo(Self);
    }
}

The downloading and parsing here is being done by the AngleSharp library which makes it really easy. When a message comes in saying to scrape a URL it downloads and parses that URL, and then when the result finishes it forwards that up the chain. Because this actor is focused on just doing one task at a time, we can potentially spin up as many of this concurrently as we need to speed up processing.

CoordinatorActor

With that actor ready to go, next we'll set up a coordinator actor that manages a pool of ScrapeActors:

public class CoordinatorActor : ReceiveActor
{
    private readonly IActorRef _crawlers;

    public CoordinatorActor()
    {
        _crawlers = Context.ActorOf(
            Props.Create(() => new ScrapeActor(Self)).WithRouter(new SmallestMailboxPool(10)));

        Receive<Scrape>(msg => _crawlers.Tell(msg));
        Receive<ScrapeResult>(msg => OnReceiveScrapeResult(msg));
    }

    private void OnReceiveScrapeResult(ScrapeResult result)
    {
        foreach (var url in result.LinkedUrls)
            _crawlers.Tell(new Scrape(url));

        if (!string.IsNullOrWhiteSpace(result.Title))
            Context.System.EventStream.Publish(result);
    }
}

As scrape requests come in it sends those down to the worker in its pool with the smallest mailbox. This is where the actor model really shines, since you can easily adjust the size of the pool or the routing algorithm without actually making any real code changes. You could also load this entirely from configuration files to avoid code changes at all.

As results come in it sends new scrape requests back to the pool of workers in order to keep the crawling going, and then publishes the results to the event stream, which is a built-in publish/subscribe channel in Akka.NET.

ResultDispatchActor

Finally we'll create a small actor that will act as a bridge between the actor system and a view model driving the app's behavior (this will be defined shortly):

public class ResultDispatchActor : ReceiveActor
{
    public ResultDispatchActor(MainViewModel viewModel) =>
        Receive<ScrapeResult>(result => viewModel.Results.Add(result));
}

As results are received they are appended to the view model's list of results. One interesting thing to note here is that since an actor only processes one message at a time, you eliminate a lot of collection concurrency issues you might have to worry about otherwise.

Starting The System

Now that our actors are defined we just need to compose them into an actual actor system. For this all we'll just do that statically when the app starts:

public static class CrawlingSystem
{
    private static readonly ActorSystem _system;
    private static readonly IActorRef _coordinator;

    static CrawlingSystem()
    {
        _system = ActorSystem.Create("crawling-system");
        _coordinator = _system.ActorOf(Props.Create<CoordinatorActor>(), "coordinator");
    }
    
    public static void StartCrawling(string url, MainViewModel viewModel)
    {
        var props = Props.Create(() => new ResultDispatchActor(viewModel));
        var dispatcher = _system.ActorOf(props);

        _system.EventStream.Subscribe(dispatcher, typeof(ScrapeResult));

        _coordinator.Tell(new Scrape(url));
    }
}

Here we expose a StartCrawling method that takes in a URL and a view model, creates a bridge actor for that view model, and subscribes it to the stream of results.

The App

Now let's actually plug this into an app. First, let's define that MainViewModel class:

public class MainViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public ObservableCollection<ScrapeResult> Results { get; } = new ObservableCollection<ScrapeResult>();
    public ICommand StartCrawlingCommand { get; }

    private string _url;
    public string Url
    {
        get { return _url; }
        set
        {
            _url = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Url)));
        }
    }

    public MainViewModel() =>
        StartCrawlingCommand = new Command(() => CrawlingSystem.StartCrawling(_url, this));
}

The view model exposes a URL property that can be bound to an entry field, a collection of results, and a command that initiates crawling for the given URL.

Now we can define the UI in XAML:

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:akka="clr-namespace:MobileCrawler.CSharp;assembly=MobileCrawler.CSharp"
    x:Class="MobileCrawler.CSharp.MainPage">
    <ContentPage.Content>
        <StackLayout Padding="15, 30, 15, 15" Spacing="10">
            <StackLayout>
                <Entry x:Name="Query" Text="{Binding Url}" 
                       HorizontalOptions="FillAndExpand" Keyboard="Url"
                       Placeholder="Enter a URL" HeightRequest="40" FontSize="20">
                    <Entry.Behaviors>
                        <akka:EntryCompletedBehavior Command="{Binding StartCrawlingCommand}" />
                    </Entry.Behaviors>
                </Entry>
            </StackLayout>

            <ListView ItemsSource="{Binding Results}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextCell Text="{Binding Title}" Detail="{Binding Url}" />
                    </DataTemplate>
                </ListView.ItemTemplate>

                <ListView.Header>
                    <StackLayout Orientation="Horizontal" Padding="10" Spacing="10">
                        <Label Text="{Binding Results.Count}" />
                        <Label Text="links crawled" />
                    </StackLayout>
                </ListView.Header>
            </ListView>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

In the code-behind all we need to do is set up the view model:

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();

        BindingContext = new MainViewModel();
    }
}

That's all we need - the view model and binding take care of the rest. Let's give it a shot:

iOS app scraping my site

Not bad! This obviously only scratches the surface of what Akka can do, but I'm pretty excited to be able to start leveraging in Xamarin applications going forward. The full app can be found in my akka-samples repository on GitHub.

]]>
<![CDATA[A Mobile DevOps Retrospective Part I: 150+ Apps Later]]>

I recently had the pleasure to write a series of guest posts for the Microsoft's Mobile Center blog about mobile DevOps, and our experiences over the years at Olo. I'm happy to say that part one of the series is now out, with parts two and

]]>
https://gregshackles.com/a-mobile-devops-retrospective-part-i-150-apps-later/61ce48a0437e8200017d4158Tue, 26 Sep 2017 16:18:30 GMT

I recently had the pleasure to write a series of guest posts for the Microsoft's Mobile Center blog about mobile DevOps, and our experiences over the years at Olo. I'm happy to say that part one of the series is now out, with parts two and three coming soon. Enjoy!

A Mobile DevOps Retrospective Part I: 150+ Apps Later

]]>
<![CDATA[Building a Voice-Driven TV Remote - Part 8: Tracking Performance with Application Insights]]>

This is part eight of the Building a Voice-Driven TV Remote series:

  1. Getting The Data
  2. Adding Search
  3. The Device API
  4. Some Basic Alexa Commands
  5. Adding a Listings Search Command
  6. Starting to Migrate from HTTP to MQTT
  7. Finishing the Migration from HTTP to MQTT
  8. Tracking Performance with Application Insights

In the

]]>
https://gregshackles.com/building-a-voice-driven-tv-remote-part-8-tracking-performance-with-application-insights/61ce48a0437e8200017d4157Sun, 13 Aug 2017 18:21:35 GMT

This is part eight of the Building a Voice-Driven TV Remote series:

  1. Getting The Data
  2. Adding Search
  3. The Device API
  4. Some Basic Alexa Commands
  5. Adding a Listings Search Command
  6. Starting to Migrate from HTTP to MQTT
  7. Finishing the Migration from HTTP to MQTT
  8. Tracking Performance with Application Insights

In the last part I vastly improved the performance of the app by switching from HTTP to MQTT. With that in place, the next step I wanted to take was seeing where the remaining time was being spent. How quick are calls to the search service or the database? How long are the nightly downloads and imports taking? To answer these questions I set out to add Application Insights to the app to really get some visibility.

Initial Setup

The basic setup for Application Insights is as easy as it gets. After you create a new Insights app and get the instrumentation key from it, all you need to do is add an app setting to your function app named APPINSIGHTS_INSTRUMENTATIONKEY and you'll start getting metrics reporting to Insights automatically. With that in place, for example, I can easily see the execution history for the DownloadLineup function:

DownloadLineup performance

Each morning that is taking around 6-8s seconds to run, which is perfectly fine to me.

Tracking Sub-Operations

The main thing I wanted to get insight into is where the RemoteSkill function was spending its time, to figure out how I can make that as fast as possible. I could already see the overall function durations using the same method as above, but that doesn't help me see how it spends its time during that duration.

To add this in, I pulled in the Microsoft.ApplicationInsights NuGet package, which allows me to interact with Application Insights programmatically. First, I added a new file named telemetry.fsx to the function:

module Telemetry

open System
open Microsoft.ApplicationInsights
open Microsoft.ApplicationInsights.Extensibility
open Microsoft.ApplicationInsights.DataContracts

let private instrumentationKey = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY")
let private telemetryClient = TelemetryClient(InstrumentationKey = instrumentationKey)

let setOperationId operationId =
    telemetryClient.Context.Operation.Id <- operationId

let startOperation (name:string) = 
    telemetryClient.StartOperation<DependencyTelemetry>(name)

Here I create a TelemetryClient and expose a couple utility methods: one to set the overall operation ID so that all sub-operations will be tracked within the overall request operation, and another to start a new sub-operation. The operation is an IDisposable, so the duration for it lasts from when it's created until it's disposed.

With that in place, all I needed to do was start sprinkling calls to it all around the function implementation. For example, to track the calls to search service in search.fsx:

let private searchShows request =
    use operation = Telemetry.startOperation "ShowSearch"
    search "shows" request |> ShowSearchResults.Parse

let private searchChannels request =
    use operation = Telemetry.startOperation "ChannelSearch"
    search "channels" request |> ChannelSearchResults.Parse

Or to track executing a command in commands.fsx:

let executeCommand commandSlug = 
    use operation = Telemetry.startOperation "ExecuteCommand"
    ...

With a bunch of these in place I started executing commands and searches via Alexa to see how it looked. Here's an example of how one request stacked up:

RemoteSkill performance

The vertical order can get a little mixed up when operations start really close together, but you can still get a good idea of the flow here. In this case the whole request took 1385ms, 1024ms of which was spent executing the commands to change the channel. This is somewhat expected, since I had added a 250ms sleep in between each command execution to avoid overloading my cable box.

One obvious performance improvement here in terms of the function itself would be to allow for sending multiple commands at once via IoT Hub, and do the pauses on the Raspberry Pi instead of in the function. This would also be a nice cost optimization since with Azure Functions you pay for the time your function is spent running, and most of the time here is actually spent sleeping.

The calls to the search service are also a little bit slower than I was expecting so I'll also be looking into how to tweak that as well.

]]>
<![CDATA[Looking Ahead to Xamarin.Forms 3.0]]>

It's been a few years now since Xamarin.Forms was released into the world, and it continues to be a popular and evolving framework choice for Xamarin developers. Later this year Microsoft is planning to release Xamarin.Forms 3.0, its third major release of the framework, which

]]>
https://gregshackles.com/looking-ahead-to-xamarin-forms-3-0/61ce48a0437e8200017d4156Fri, 11 Aug 2017 13:21:31 GMT

It's been a few years now since Xamarin.Forms was released into the world, and it continues to be a popular and evolving framework choice for Xamarin developers. Later this year Microsoft is planning to release Xamarin.Forms 3.0, its third major release of the framework, which is slated to ship with a lot of exciting features and improvements. While it certainly won't be comprehensive, as the feature set is large and still in motion, in this article I'll walk through some of the highlights of what's coming later this year for Xamarin.Forms developers.

Read the rest over at Visual Studio Magazine.

]]>
<![CDATA[Building a Voice-Driven TV Remote - Part 7: Finishing the Migration from HTTP to MQTT]]>

This is part seven of the Building a Voice-Driven TV Remote series:

  1. Getting The Data
  2. Adding Search
  3. The Device API
  4. Some Basic Alexa Commands
  5. Adding a Listings Search Command
  6. Starting to Migrate from HTTP to MQTT
  7. Finishing the Migration from HTTP to MQTT
  8. Tracking Performance with Application Insights

In the

]]>
https://gregshackles.com/building-a-voice-driven-tv-remote-part-7-finishing-the-migration-from-http-to-mqtt/61ce48a0437e8200017d4155Sun, 06 Aug 2017 23:18:11 GMT

This is part seven of the Building a Voice-Driven TV Remote series:

  1. Getting The Data
  2. Adding Search
  3. The Device API
  4. Some Basic Alexa Commands
  5. Adding a Listings Search Command
  6. Starting to Migrate from HTTP to MQTT
  7. Finishing the Migration from HTTP to MQTT
  8. Tracking Performance with Application Insights

In the last post of the series I introduced a MQTT bridge to connect my Harmony system with Azure IoT, so now it's time to switch things over and remove the need for my functions to make API calls into my house.

Switching Commands

Naturally Microsoft has a handy package available that makes communicating with Azure IoT Hubs a breeze called Microsoft.Azure.Devices. In the implementation of RemoteSkill, recall that the commands.fsx file had this as the implementation for sending a command to the device:

let private makeRequest method urlPath =
    let url = sprintf "%s/%s" (Environment.GetEnvironmentVariable("HarmonyApiUrlBase")) urlPath
    let authHeader = "Authorization", (Environment.GetEnvironmentVariable("HarmonyApiKey"))

    Http.RequestString(url, httpMethod = method, headers = [authHeader])

let executeCommand commandSlug = sprintf "commands/%s" commandSlug |> makeRequest "POST" |> ignore

This meant that every command needed to assume all of the overhead of HTTP calls into my house. In addition to the fact that this meant my house needed a public API, this was also a performance killer since a command here effectively maps to a button pressed on a remote. Entering a channel number would result in four commands being sent - three for the digits and then one to hit enter.

Here's an updated implementation that sends the command through the IoT Hub:

let serviceClient = ServiceClient.CreateFromConnectionString (Environment.GetEnvironmentVariable("IoTHubConnectionString"))

let executeCommand commandSlug = 
    async {
        sprintf "harmony-api/hubs/living-room/command;%s" commandSlug
        |> Encoding.ASCII.GetBytes
        |> fun bytes -> new Message(bytes)
        |> fun message -> serviceClient.SendAsync("harmony-bridge", message)
        |> Async.AwaitIAsyncResult 
        |> Async.Ignore
        |> ignore

        do! Async.Sleep 250
    } |> Async.RunSynchronously

Since the signature of executeCommand remains unchanged, that's all that actually has to change! The 250ms sleep in between commands here is to make sure there's a little space in between commands sent one after another. While testing this I quickly found that it was easy to crash my cable box by sending requests too quickly:

With just this one method implementation change, all commands being sent into my house are now being routed through IoT Hub.

Switching Queries

Switching command execution over to MQTT gave a real noticeable performance boost, but it didn't totally eliminate the need for a public-facing API that the functions could access. Also in the commands.fsx file was one other method that would query the API for active commands for the current activity of my media center:

let getCommand (label: string) =
    makeRequest "GET" "commands"
    |> CommandsResponse.Parse
    |> fun res -> res.Commands
    |> Seq.tryFind (fun command -> command.Label.ToLowerInvariant() = label.ToLowerInvariant())

This one was a little trickier to switch to MQTT since it relies on state that changes depending on the active activity (where activity can be watching TV, AppleTV, Fire Stick, etc). To solve for this I decided to add some more persistence to the application, where every time the activity changed it would update the persistence to have the latest set of commands available to use.

Adding the Database

I thought about introducing something like Redis or CosmosDB for it, but that ended up being at odds with my goal of keeping this thing as cheap as possible. I already have a SQL instance that costs around $5 per month, while adding Redis would be another $16, and CosmosDB another $24. Based on price, adding another table in SQL Server was the obvious answer. I went ahead and created this simple table:

CREATE TABLE AvailableCommand(
	AvailableCommandId int IDENTITY(1,1) NOT NULL,
	Name nvarchar(32) NOT NULL,
	Slug nvarchar(16) NOT NULL,
	Label nvarchar(32) NOT NULL
)

There's no real need to keep any historic data, so each time the activity changes I'll just blow away the old data and replace it with the new ones. There are realistically only going to be up to 20-30 commands for each activity anyway.

Storing the Data

With the database table in place, the next task was to update the IoT bridge I created in the last post to subscribe to the MQTT topic that gets notified when the activity changes. When it changes, it can make the HTTP API call locally (so no need for this to ever be exposed externally to the Raspberry Pi itself or the local network) to get the new set of commands, and persist that to Azure.

To do that I pulled in a couple npm packages to simplify HTTP calls and database access:

npm i --save request-promise tedious 

Next I'll add a function that, given a list of commands, updates the SQL database:

function updateAvailableCommands(commands) {
    const connection = new tedious.Connection(config.sqlConfig);
    connection.on('connect', err => {
        if (err) {
            console.error('Error connecting to SQL', err);
            return;
        }

        const truncateRequest = new tedious.Request("truncate table AvailableCommand", err => {
            if (err) {
                console.error('Error truncating table', err);
            }
        });

        truncateRequest.on('requestCompleted', () => {
            const updateCommands = connection.newBulkLoad('AvailableCommand', (err, rowCount) => {
                if (err) {
                    console.error('Error inserting commands', err);
                } else {
                    console.log(`Inserted ${rowCount} command(s)`);
                }
            });
            updateCommands.addColumn('Name', tedious.TYPES.NVarChar, { nullable: false });
            updateCommands.addColumn('Slug', tedious.TYPES.NVarChar, { nullable: false });
            updateCommands.addColumn('Label', tedious.TYPES.NVarChar, { nullable: false });

            commands.forEach(command => 
                updateCommands.addRow({ Name: command.name, 
                                        Slug: command.slug, 
                                        Label: command.label }));

            connection.execBulkLoad(updateCommands);
        })

        connection.execSql(truncateRequest);
    }); 
}

Most of the code here is either JavaScript ceremony or error logging, so there's not too much going on. It simply truncates the existing data in the table and bulk loads the new data in.

Lastly, I just need to subscribe to the topic and update the commands when the activity changes:

broker.on('publish', packet => {
    if (packet.topic !== 'harmony-api/hubs/living-room/current_activity') {
        return;
    }

    request({ url: 'http://localhost:8282/hubs/living-room/commands', json: true })
        .then(res => updateAvailableCommands(res.commands));
});

Now whenever the activity changes the SQL database will be updated with the new set of available commands.

Updating the Function

Now that I've got a data store in place with a list of the available commands, I just need to rip out that last API call from commands.fsx and replace it with a database read:

[<Literal>]
let configFile = "D:\\home\\site\\wwwroot\\RemoteSkill\\app.config"

let getCommand (label: string) =
    use cmd = new SqlCommandProvider<"SELECT Slug FROM AvailableCommand WHERE Label=@label", "name=TVListings", ConfigFile=configFile, SingleRow=true>()
    cmd.Execute(label = label)

Thanks to the beauty of the SQL type provider that's all that's actually needed to read out the command in a typesafe way. This change does actually change the signature of getCommand over the previous version, though, in that it only returns the slug instead of the full command object. That just means I need to tweak the handleDirectCommand method in run.fsx to just expect the slug, which is all it cared about anyway:

let handleDirectCommand (intent: Intent) =
    match (Commands.getCommand intent.Slots.["command"].Value) with
    | Some(slug) ->
        Commands.executeCommand slug
        buildResponse "OK" true
    | None -> buildResponse "Sorry, that command is not available right now" true

And that's it! I no longer have any need to make direct outbound API calls from the functions into my house, so I was able to shut down the NGINX site altogether, as well as the No-IP job and Let's Encrypt...pretty much everything I'd done in part 3 of this series.

There's still some improvements that can be made, but the performance improvement by switching to MQTT has been massive and it makes this skill so much more useful. Here's a little video of me channel surfing on the current version:


Next post in series: Tracking Performance with Application Insights

]]>