Faithlife Code Blog2023-11-09T16:25:33+00:00tag:faithlife.codes,2017:/blog/12REST Level 0.52023-09-15T08:00:00+00:00http://faithlife.codes/blog/2023/09/rest-level-zero-dot-five/<blockquote>
<p>I’ve read several articles about REST, even a bit of the original paper. But I still have quite a vague idea about what it is. I’m beginning to think that nobody knows, that it’s simply a very poorly defined concept. Like Web 2.0, everyone talked about it but nobody could give a clear definition. Thoughts?<br />
— <a href="https://news.ycombinator.com/item?id=7201871">RivieraKid</a></p>
</blockquote>
<p>This article will briefly lay out the Richardson Maturity Model of REST, which describes where an API sits in terms of RESTfulness. We’ll then propose a well-defined and simplified version of REST, called REST Level 0.5, which can be summarized on the back of a napkin. For brevity, we will not go into a deep discussion of REST or its history. For those interested in the history of REST and what it is, <a href="https://twobithistory.org/2020/06/28/rest.html">this</a> is a great resource.</p>
<p>A summarized version of the Richardson Maturity Model includes:</p>
<h2 id="level-0">Level 0</h2>
<ul>
<li>Uses HTTP, but only as a transport mechanism.</li>
<li>All requests <code class="language-plaintext highlighter-rouge">POST</code> to one URL.</li>
<li>Plain old XML or JSON.</li>
</ul>
<h2 id="level-1">Level 1</h2>
<ul>
<li>Nouns (resources) instead of verbs (procedures).</li>
<li>Each resource gets its own URL, which includes the ID of the resource in the path.</li>
<li>The verb still goes in the request body or headers.</li>
</ul>
<h2 id="level-2">Level 2</h2>
<ul>
<li>Use HTTP verbs.</li>
<li>Use HTTP status codes.</li>
<li>Use GET to read resource(s).</li>
<li>Use POST to modify resource(s).</li>
<li>Better yet, use POST to create, PUT or PATCH to edit, and DELETE to, you know, delete.</li>
</ul>
<h2 id="level-3">Level 3</h2>
<p>HATEOAS (Hypermedia As The Engine of Application State). Most APIs rarely venture this far. Essentially, requests can now return references to other resources as hyperlinks. This is outside the scope of our article, and unuseful for many APIs.</p>
<h2 id="the-problem">The Problem</h2>
<p>Many APIs will fall somewhere between level 0 and level 2, perhaps using a mixture of different levels for different parts.</p>
<p>The issue is level 2 is an awkward fit for many domains. There is a <code class="language-plaintext highlighter-rouge">DELETE</code>, but not an <code class="language-plaintext highlighter-rouge">UNDELETE</code>. Batching was explicitly not supported in the original REST paper, so various APIs roll their own implementations. <code class="language-plaintext highlighter-rouge">GET</code> does not make sense for many authenticated APIs, where built-in caching is unwanted. Interactions between many different types of resources are not well defined. Many have their own interpretations and definitions of what REST even entails.</p>
<p>Thus, we propose REST Level 0.5, which takes the parts we found useful and provides a single, well-defined set of rules that is quickly learned. There are no silver bullets, but REST Level 0.5 has been found to work well on multiple APIs we’ve built. As always, there may be a better fit for your domain.</p>
<h2 id="level-05">Level 0.5</h2>
<ul>
<li>One URL per method.</li>
<li>Only use <code class="language-plaintext highlighter-rouge">POST</code>.</li>
<li>Only use these three status codes: <code class="language-plaintext highlighter-rouge">200</code>, <code class="language-plaintext highlighter-rouge">400</code>, and <code class="language-plaintext highlighter-rouge">500</code> for API method implementations.</li>
<li>Use standard HTTP headers and status codes for cross-cutting concerns like authentication, authorization, rate limiting, etc.</li>
<li>We like to use a standard <code class="language-plaintext highlighter-rouge">JSON</code> payload request with a <code class="language-plaintext highlighter-rouge">JSON</code> payload response.</li>
</ul>
<p>A sample URL might look like this:</p>
<p><code class="language-plaintext highlighter-rouge">POST https://libraryresourcesapi.faithlife.com/v1/getTextRangesForWordNumbers</code></p>
<p>That’s it! Everything you need and nothing more.</p>
<p>For a detailed analysis, which includes many of the drawbacks of REST Level 0.5, we recorded a <a href="https://www.faithlifetv.com/items/1055113">talk</a>.</p>
Erik LanningTroubleshooting Azure DNS Timeouts2023-02-03T06:00:00+00:00http://faithlife.codes/blog/2023/02/troubleshooting-azure-dns-timeouts/<p>We recently completed a large “Lift and Shift” of our on-premises application hosting into the Microsoft Azure cloud. In this post, I will describe one of the problems we encountered, troubleshot, and resolved on the new Windows Virtual Machines that were now hosting our C# applications.</p>
<h2 id="the-problem">The Problem</h2>
<p>Our applications would generate network-related errors at random when running in the new environment. We could go days without seeing an error from any particular application, but there were <em>always</em> some errors – no rhyme nor reason across the network as a whole.</p>
<h3 id="examples">Examples</h3>
<p>When connecting to our MySql instances, we might have seen:</p>
<pre><code class="language-C#">MySqlConnector.MySqlException (0x80004005): Unable to connect to any of the specified MySQL hosts.
at MySqlConnector.Core.ServerSession.OpenTcpSocketAsync
at MySqlConnector.Core.ServerSession.ConnectAsync
</code></pre>
<p>When making HTTP requests we might have seen:</p>
<pre><code class="language-C#">System.Net.WebException: The remote name could not be resolved: 'example.com'
at System.Net.HttpWebRequest.EndGetResponse
</code></pre>
<p>or</p>
<pre><code class="language-C#">System.Net.Http.HttpRequestException: No such host is known. (example.com:443)
---> System.Net.Sockets.SocketException (11001): No such host is known.
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
at System.Net.Sockets.Socket.<ConnectAsync>g__WaitForConnectWithCancellation|277_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync
</code></pre>
<p>These errors would occur for host names that <em>absolutely</em> existed and were readily accessible otherwise. The specific host did not matter–these errors occurred for hosts we controlled as well as those controlled by third parties.</p>
<p>The main trouble was that the problem was just so <em>rare</em>, yet <em>persistent</em>; <em>specific</em>, yet <em>pernicious</em>. How could we reproduce it? Was our application code doing something wrong, or was it the C# framework, or could any request from those VMs reproduce it? Was it DNS, or was it the request after DNS resolved? If it was DNS, was it specific to the way we set up our DNS hosts or virtual network? If it was the request, was it the source-server, was it the network, or was it the destination server? Was it load-related, on the source VM or on some network bottleneck or in Azure DNS itself?</p>
<h2 id="troubleshooting">Troubleshooting</h2>
<p>We were eventually able to reproduce the errors outside of running apps using Powershell scripts running on our Azure VMs (it never reproduced outside of Azure). In the first versions of the script, it needed to run for a few days before we could know whether we had managed to reproduce the problem.</p>
<p>By testing many variants of the script, we were able to narrow the problem down to DNS resolution itself. I was then able to get the “Mean-Time-to-Reproduce” down to “randomly between two to six hours” by having the script clear the DNS cache between each attempt.</p>
<p>This is the simplest version of the Powershell we used to reproduce the problem:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Write-Host</span><span class="w"> </span><span class="s2">"Start: </span><span class="si">$(</span><span class="n">Get-Date</span><span class="w"> </span><span class="nt">-Format</span><span class="w"> </span><span class="s2">"o"</span><span class="p">)</span><span class="s2">"
</span><span class="nv">$stopwatch</span><span class="s2"> = [system.diagnostics.stopwatch]::StartNew()
do {
Start-Sleep -Seconds 1
Clear-DnsClientCache
</span><span class="nv">$time</span><span class="s2"> = (Measure-Command {
try {
</span><span class="nv">$result</span><span class="s2"> = [System.Net.Dns]::GetHostAddresses("</span><span class="n">azure.microsoft.com</span><span class="s2">")
} catch {
Write-Host </span><span class="bp">$_</span><span class="s2">
</span><span class="nv">$result</span><span class="s2"> = </span><span class="nv">$False</span><span class="s2">
}
}).TotalSeconds
} while (</span><span class="nv">$result</span><span class="s2">);
Write-Host "</span><span class="nv">$time</span><span class="nt">-second</span><span class="w"> </span><span class="nx">timeout</span><span class="w"> </span><span class="nx">after</span><span class="w"> </span><span class="nx">running</span><span class="w"> </span><span class="nx">for</span><span class="w"> </span><span class="err">$</span><span class="p">(</span><span class="nv">$stopwatch</span><span class="o">.</span><span class="nf">Elapsed</span><span class="o">.</span><span class="nf">TotalMinutes</span><span class="si">)</span><span class="s2"> minutes at </span><span class="si">$(</span><span class="n">Get-Date</span><span class="w"> </span><span class="nt">-Format</span><span class="w"> </span><span class="s2">"o"</span><span class="p">)</span><span class="s2">"
</span></code></pre></div></div>
<p>When it finally encountered the error, the DNS resolution would time out after about 11 seconds. The script output looked something like this:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Start: 2023-01-04T18:39:37.8532468+00:00
Exception calling \"GetHostAddresses\" with \"1\" argument(s): \"No such host is known\"
12.1075537-second timeout after running for 65.1770462016667 minutes at 2023-01-04T19:44:48.8129523+00:00
</code></pre></div></div>
<p>We now knew the problem wasn’t specific to our application code.</p>
<p>From there, we discovered that when using an entirely fresh Azure Windows VM in in an entirely fresh VNet using Azure’s default DNS provider, we could run this script and reproduce the problem. This meant it wasn’t (necessarily) a problem with our “Lift and Shift” network or our own Faithlife DNS configuration. Further, we could create <em>two</em> reproduction environments, one using Azure’s built-in DNS and one using Google’s <code class="language-plaintext highlighter-rouge">8.8.8.8</code> DNS. The Google environment did not reproduce the problem.</p>
<p>This narrowed the problem down decisively: it occurred on a default-configuration Microsoft Windows VM on a default-configuration Microsoft Azure Virtual Network trying to use default-configuration Microsoft Azure DNS to resolve <code class="language-plaintext highlighter-rouge">azure.microsoft.com</code>.</p>
<p>At this point, we escalated our Azure support ticket. The new rep spotted something interesting when we reproduced the problem and got them a packet capture! They noticed that the DNS request which failed happened to use UDP Source Port 65330 when talking to Azure DNS – and if you dig deep into the VNet documentation, you will discover that <a href="https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-faq#what-protocols-can-i-use-within-vnets">UDP Port 65330 is reserved for Azure’s own use</a>.</p>
<p>The final script to reproduce the problem looks like this:</p>
<!-- markdownlint-disable MD022 MD025 MD026 MD033 MD046 -->
<details>
<summary><strong>> dns-timeouts.sh</strong></summary>
<pre>#!/usr/bin/env bash
# Azure subscription ID to use
subscription="$1"
# Azure location (e.g. "westus3")
location="$2"
# When not provided, sets up a new reproduction environment.
# When provided, fetches results of previous run.
suffix="$3"
set -e +H
if test -z "${subscription}" -o -z "${location}"; then
echo "Usage: \`${0} subscription location [suffix]\`"
exit 1
fi
if test -z "${suffix}"; then
suffix=$RANDOM
create=1
else
create=0
fi
resource_group="dns-rg-$suffix"
azure_vnet="dns-vnet-azure-$suffix"
google_vnet="dns-vnet-google-$suffix"
azure_vm="dns-vm-azure-$suffix"
google_vm="dns-vm-google-$suffix"
storage_account="dnssa$suffix"
packet_capture="dns-capture-$suffix-$RANDOM"
if test $create -eq 1; then
echo "Creating networks..."
az group create --subscription "$subscription" --location "$location" \
--name "$resource_group"
azure_subnet_id=$(az network vnet create --subscription "$subscription" --location "$location" --resource-group "$resource_group" \
--name "$azure_vnet" --subnet-name test \
-otsv --query "newVNet.subnets[0].id" | tr -d '\r')
google_subnet_id=$(az network vnet create --subscription "$subscription" --location "$location" --resource-group "$resource_group" \
--name "$google_vnet" --subnet-name test --dns-servers "8.8.8.8" \
-otsv --query "newVNet.subnets[0].id" | tr -d '\r')
echo "Creating VMs..."
# If you get an error about `--subnet_id` being invalid, try setting `export MSYS_NO_PATHCONV=1` to disable path expansion
az vm create --subscription "$subscription" --resource-group "$resource_group" --location "$location" \
--name "$azure_vm" --computer-name "dns-a-$suffix" --subnet "$azure_subnet_id" \
--image Win2022AzureEditionCore --size Standard_B1s \
--admin-username azureuser --admin-password "12345abcde!ABCDE" --public-ip-address "" --nsg "" --public-ip-sku Basic \
--nic-delete-option delete --os-disk-delete-option delete --data-disk-delete-option delete
az vm create --subscription "$subscription" --resource-group "$resource_group" --location "$location" \
--name "$google_vm" --computer-name "dns-g-$suffix" --subnet "$google_subnet_id" \
--image Win2022AzureEditionCore --size Standard_B1s \
--admin-username azureuser --admin-password "12345abcde!ABCDE" --public-ip-address "" --nsg "" --public-ip-sku Basic \
--nic-delete-option delete --os-disk-delete-option delete --data-disk-delete-option delete
echo "Installing Network Watcher..."
az vm extension set --subscription "$subscription" --resource-group "$resource_group" --vm-name "$azure_vm" --publisher Microsoft.Azure.NetworkWatcher --name NetworkWatcherAgentWindows --version 1.4
echo "Creating Storage Account..."
az storage account create --subscription "$subscription" --resource-group "$resource_group" --location "$location" \
--name "$storage_account" \
--sku Standard_RAGRS \
--kind StorageV2
echo "Starting packet capture..."
az network watcher packet-capture create --subscription "$subscription" --resource-group "$resource_group" --vm "$azure_vm" --name "$packet_capture" --storage-account "$storage_account" \
--filters "[{\"protocol\":\"UDP\", \"remoteIpAddress\":\"168.63.129.16\"}]"
echo "Running test..."
# The RunPowerShellScript command will "time out" after about an hour, but the script will keep running on the VM until it exits.
test_command='
Write-Host "Start: $(Get-Date -Format "o")"
$stopwatch = [system.diagnostics.stopwatch]::StartNew()
do {
Start-Sleep -Seconds 1
Clear-DnsClientCache
$time = (Measure-Command {
try {
$result = [System.Net.Dns]::GetHostAddresses("azure.microsoft.com")
} catch {
Write-Host $_
$result = $False
}
}).TotalSeconds
} while ($result);
Write-Host "$time-second timeout after running for $($stopwatch.Elapsed.TotalMinutes) minutes at $(Get-Date -Format "o")"
'
az vm run-command invoke --subscription "$subscription" --resource-group "$resource_group" \
--name "$azure_vm" \
--command-id RunPowerShellScript --no-wait --scripts "$test_command"
az vm run-command invoke --subscription "$subscription" --resource-group "$resource_group" \
--name "$google_vm" \
--command-id RunPowerShellScript --no-wait --scripts "$test_command"
# Wait for completion. To fetch results, run script again.
echo "Test started. Please run \`${0} $subscription $location $suffix\` to retrieve results after letting script run for at least five hours."
else
echo "Stopping packet capture..."
az network watcher packet-capture stop --subscription $subscription --name $packet_capture --location $location
echo "Fetching results..."
# Enumerates the directory where Azure keeps the output of previous RunPowerShellScript commands
result_command='
Get-ChildItem -Path C:\Packages\Plugins\Microsoft.CPlat.Core.RunCommandWindows\1.1.15\Status `
| Get-Content | ConvertFrom-Json `
| % {
$a = @{
Step = $_.status.name;
Timestamp = $_.timestampUTC
};
$_.status.substatus `
| where { $_.name -eq "StdErr" -or $_.name -eq "StdOut" } `
| Foreach { $a[$_.name] = $_.formattedMessage.message };
[pscustomobject] $a;
} `
| Sort-Object -Descending -Property Timestamp | Select-Object -Skip 0 -First 3 | Format-List'
echo ""
echo "Azure DNS:"
printf '%b\n' "$(az vm run-command invoke --subscription "$subscription" --resource-group "$resource_group" \
--name "$azure_vm" \
--command-id RunPowerShellScript --scripts "$result_command" --query "value[].message | join('', @)")"
echo ""
echo "Google DNS:"
printf '%b\n' "$(az vm run-command invoke --subscription "$subscription" --resource-group "$resource_group" \
--name "$google_vm" \
--command-id RunPowerShellScript --scripts "$result_command" --query "value[].message | join('', @)")"
echo ""
echo "To clean up, \`az group delete --yes --no-wait --subscription $subscription --resource-group $resource_group\`."
fi
</pre>
</details>
<!-- markdownlint-enable MD022 MD025 MD026 MD033 MD046 -->
<h2 id="resolution">Resolution</h2>
<p>So this was the situation:</p>
<ul>
<li>When you make a DNS request, Windows will choose a “Dynamic Port” to use for your traffic.
<ul>
<li>The Microsoft-provided default range for Azure Windows images is ports 49152 through 65535, which has 16383 ports to choose from, including port 65330.</li>
<li>When a DNS request times out and retries, it will re-use the same dynamic “Source Port” until it gives up entirely (after 11 seconds).</li>
</ul>
</li>
<li>So, one out of 16,383 DNS requests would randomly fail <em>because Azure blocked the request entirely</em> when using port 65330. At one request per second, that’s about four hours “Mean-Time-to-Reproduce” (which matches what we were seeing).</li>
<li>Meanwhile, Google DNS was working because it was using TCP, not UDP, for requests routing outside of the VNET.</li>
</ul>
<p>Which finally brings us to the fix: Tell Windows not to use the port that Microsoft doesn’t want us using in Azure. You do so by giving it a “range” of <code class="language-plaintext highlighter-rouge">65330 - 1 - 49152 = 16177</code> ports to choose from instead of the full 16383.</p>
<p>We rolled this fix out to all of our Windows VMs using Puppet:</p>
<div class="language-puppet highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">exec</span> <span class="p">{</span> <span class="s1">'Disable UDP/65330'</span><span class="p">:</span>
<span class="py">command</span> <span class="p">=></span> <span class="s1">'Set-NetUDPSetting -DynamicPortRangeStartPort 49152 -DynamicPortRangeNumberOfPorts 16177'</span><span class="p">,</span>
<span class="py">unless</span> <span class="p">=></span> <span class="s1">'exit [int](Get-NetUDPSetting | % { $_.DynamicPortRangeStartPort + $_.DynamicPortRangeNumberOfPorts -gt 65330 })'</span><span class="p">,</span>
<span class="py">provider</span> <span class="p">=></span> <span class="n">powershell</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We’ve suggested that Microsoft consider correcting their base Windows images so that they do not default to using Azure-incompatible ports or, if that is infeasible, improving the Azure documentation on Windows VMs.</p>
Michael SterlingColor Your Functions!2022-06-10T06:00:00+00:00http://faithlife.codes/blog/2022/06/color-your-functions/<p>Faithlife’s Android engineers took a departure from our typical <a href="https://mcfunley.com/choose-boring-technology">boring approach</a> to adopt Kotlin Coroutines early—before structured concurrency landed in 0.26.0. In other words, when we departed the boring tech train, we hopped on an experimental maglev train’s maiden voyage. We weren’t sure we’d make it, but fortunately we did, and our bet paid off handsomely. Some of our codebases have never seen an <code class="language-plaintext highlighter-rouge">AsyncTask</code> implementation, which is a breath of fresh air. kotlinx.coroutines is generally loved by Android engineers.</p>
<h2 id="function-coloring">Function Coloring</h2>
<p>The function color metaphor is most often applied to distinguish synchronous vs asynchronous functions and comes from Bob Nystrom’s post <a href="https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/">“What Color is Your Function?”</a>. In his system, red functions are those that can only be called from other red functions. Blue functions can be called from anywhere. This neatly maps onto many programming languages’ concurrency models. Kotlin’s suspending functions are <i style="color:red">red</i>. They can only be called from other suspending functions.</p>
<blockquote>
<p>Kotlin chose a colored system for coroutines, as opposed to Go’s colorless system. Java’s <a href="https://wiki.openjdk.java.net/display/loom/Main">Project Loom</a> is aiming for a colorless approach also.</p>
</blockquote>
<p>When a function is denoted with <code class="language-plaintext highlighter-rouge">suspend</code>, the compiler tacks on the capability to pause the execution of the function. Function coloring makes concurrency <em>explicit</em>. Since a suspending function knows how to suspend itself, it also knows how to pause suspending functions it calls.</p>
<blockquote>
<p>I’ve written on the mechanics of kotlinx.coroutines in <a href="https://jzbrooks.com/kotlin-suspend-bytecode/">“Kotlin Suspend Bytecode”</a>.</p>
</blockquote>
<h2 id="structured-concurrency">Structured Concurrency</h2>
<p>Structured concurrency is a mechanism for limiting the lifetime of running coroutines to objects that have a well-defined lifetime, like UI elements.</p>
<p>In early versions of kotlinx.coroutines, <code class="language-plaintext highlighter-rouge">launch</code> (and the other coroutine builders) could be called from anywhere. There were essentially free functions. <code class="language-plaintext highlighter-rouge">launch</code> returned a <code class="language-plaintext highlighter-rouge">Job</code>, which provided a mechanism to join the coroutine on the current thread or to cancel it. Jobs had to be tracked manually, and forgetting to cancel them appropriately was a pretty easy mistake. The design of the coroutine library didn’t compel consideration of the asynchronous task’s lifetime.</p>
<p>When kotlinx.coroutines shipped structured concurrency, the improved API design compelled reconsidering every coroutine builder call in the codebase. The coroutine builder functions were now extensions on <code class="language-plaintext highlighter-rouge">CoroutineScope</code>, so they could only be called with an associated scope. This scope dictated the limits of the associated coroutine’s life. If a coroutine is suspended waiting on the network and its CoroutineScope is associated with the lifetime of UI element that was just dismissed, it’s cancelled.</p>
<blockquote>
<p>Swift adopted structured concurrency recently for its concurrency system, so iOS engineers can learn from our mistakes!</p>
</blockquote>
<h2 id="breaking-structured-concurrency">Breaking Structured Concurrency</h2>
<p>While the kotlinx.coroutine API is designed to encourage best practices, it’s pretty easy to break structured concurrency. One mistake is implementing <code class="language-plaintext highlighter-rouge">CoroutineScope</code> on an object without a well-defined lifetime.</p>
<p>This is a real function in one of our codebases, slightly modified to remove irrelevant details:</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">ReadingPlanClient</span> <span class="p">:</span> <span class="nc">CoroutineScopeBase</span><span class="p">()</span> <span class="p">{</span>
<span class="nd">@Synchronized</span>
<span class="k">fun</span> <span class="nf">getPlanTemplates</span><span class="p">():</span> <span class="nc">List</span><span class="p"><</span><span class="nc">ReadingPlanTemplate</span><span class="p">></span> <span class="p">{</span>
<span class="n">dtoCache</span><span class="p">.</span><span class="nf">getUnexpiredCachedDto</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span> <span class="n">resourceIdList</span> <span class="p">-></span>
<span class="k">return</span> <span class="n">resourceIdList</span><span class="p">.</span><span class="n">readingPlanTemplateList</span>
<span class="p">}</span>
<span class="n">dtoCache</span><span class="p">.</span><span class="nf">getExpiredCachedDto</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span> <span class="p">{</span>
<span class="nf">launch</span><span class="p">(</span><span class="n">defaultErrorHandler</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">fetchAndCache</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">it</span><span class="p">.</span><span class="n">readingPlanTemplateList</span>
<span class="p">}</span>
<span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">ReadingPlanClient</code> is a web service client that creates new coroutines in its own scope via the call to <code class="language-plaintext highlighter-rouge">launch</code>. That job has no chance of being preempted by cancellation since <code class="language-plaintext highlighter-rouge">ReadingPlanClient</code> does not have a well-defined lifetime and thereby is missing a good place to cancel running work. This usurps the design of coroutines.</p>
<p>Imagine a static field kept a reference to an instance of this class. When a fragment fetches reading plan templates and the user immediately navigates away from the screen, <em>all</em> of the fetching and caching work would still execute.</p>
<p>Consider another function that cooperates better with the structured concurrency model:</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@ViewModelKey</span><span class="p">(</span><span class="nc">ConversationViewModel</span><span class="o">::</span><span class="k">class</span><span class="p">)</span>
<span class="nd">@ContributesMultibinding</span><span class="p">(</span><span class="nc">AppScope</span><span class="o">::</span><span class="k">class</span><span class="p">)</span>
<span class="kd">class</span> <span class="nc">ConversationViewModel</span> <span class="nd">@Inject</span> <span class="k">constructor</span><span class="p">(</span>
<span class="c1">// ...</span>
<span class="p">)</span> <span class="p">:</span> <span class="nc">ViewModel</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// ... </span>
<span class="k">suspend</span> <span class="k">fun</span> <span class="nf">prepareConversation</span><span class="p">(</span><span class="n">conversationId</span><span class="p">:</span> <span class="nc">Long</span><span class="p">,</span> <span class="n">threadId</span><span class="p">:</span> <span class="nc">Long</span><span class="p">?</span> <span class="p">=</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="n">conversationId</span> <span class="p">=</span> <span class="n">conversationId</span>
<span class="k">this</span><span class="p">.</span><span class="n">threadId</span> <span class="p">=</span> <span class="n">threadId</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_conversation</span><span class="p">.</span><span class="n">value</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">coroutineScope</span> <span class="p">{</span>
<span class="kd">val</span> <span class="py">deferredConversation</span> <span class="p">=</span> <span class="nf">async</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">initialConversation</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">initialConversation</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">messagesRepository</span><span class="p">.</span><span class="nf">getConversation</span><span class="p">(</span><span class="n">conversationId</span><span class="p">.</span><span class="nf">toString</span><span class="p">())</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">val</span> <span class="py">deferredDraftMessage</span> <span class="p">=</span> <span class="nf">async</span> <span class="p">{</span>
<span class="n">messagesRepository</span><span class="p">.</span><span class="nf">loadDraftMessage</span><span class="p">(</span><span class="n">conversationId</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">val</span> <span class="py">deferredRecipients</span> <span class="p">=</span> <span class="nf">async</span> <span class="p">{</span>
<span class="n">messagesRepository</span><span class="p">.</span><span class="nf">getConversationRecipientsWithPresence</span><span class="p">(</span><span class="n">conversationId</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">_conversation</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">deferredConversation</span><span class="p">.</span><span class="nf">await</span><span class="p">()</span>
<span class="n">deferredDraftMessage</span><span class="p">.</span><span class="nf">await</span><span class="p">()</span><span class="o">?.</span><span class="nf">let</span><span class="p">(</span><span class="n">_draftMessage</span><span class="o">::</span><span class="n">setValue</span><span class="p">)</span>
<span class="n">_recipients</span><span class="p">.</span><span class="n">value</span> <span class="p">=</span> <span class="n">deferredRecipients</span><span class="p">.</span><span class="nf">await</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The important difference here is that the function is suspending. A suspending function can only be called from other suspending functions (a red function in Nystrom’s taxonomy). Since it can only be called from other suspending functions, it pushes the responsibility for a coroutine scope to the caller. Suspending functions don’t have a scope of their own. The call to <code class="language-plaintext highlighter-rouge">coroutineScope</code> provides the caller’s <code class="language-plaintext highlighter-rouge">CoroutineScope</code> so the suspending function can launch its own child coroutines via <code class="language-plaintext highlighter-rouge">async</code>.</p>
<p>In circumstances where <code class="language-plaintext highlighter-rouge">prepareConversation</code> is called from a fragment’s view lifecycle scope, the child coroutines will automatically be cancelled when the parent scope is cancelled. This might save a handful of network requests and subsequent caching work.</p>
<p>That is what structured concurrency buys.</p>
<p>Keeping a few simple guidelines in mind goes a long way toward getting the most out of the concurrency model.</p>
<h2 id="api-design--scope-lifetimes">API Design & Scope Lifetimes</h2>
<p>When building an API with coroutines, take care to avoid hiding suspending functions behind blue functions unless the class can reasonably own the coroutine scope required to house its coroutines. Since the red function calls have become implementation details of blue functions, this interface <em>seems</em> colorless. Ultimately that tactic spreads around details of coroutine execution and makes for a leaky abstraction.</p>
<h4 id="consider">Consider:</h4>
<ul>
<li>Who’s responsible for cancelling these coroutines?</li>
<li>When are these coroutines being cancelled?</li>
<li>What’s the default dispatcher for this scope?<br />(hint: it should always be the main dispatcher)</li>
<li>Has someone overridden the default error handler?</li>
</ul>
<p>Many Android classes provide a scope already—usually named <code class="language-plaintext highlighter-rouge">lifecycleScope</code>. So unless you have a really specific requirement, prefer what is provided there. When a new <code class="language-plaintext highlighter-rouge">CoroutineScope</code> appears in review, ask if there’s an existing scope that already meets the requirements.</p>
<p>Unless it is reasonable for your class to own the coroutine scope, expose suspending functions instead of blue functions that launch coroutines. This provides more flexibility to callers and better indicates the function’s nature.</p>
<h2 id="suspending-functions">Suspending Functions</h2>
<p>An API that exposes suspending functions is easier to fold into new suspending functions than one that exposes synchronous functions—which may or may not launch coroutines with a dubious lifetime and no control mechanism. Pushing asynchronous work into suspending functions means that once you have bridged the red/blue gap by creating a coroutine your functions are semantically similar to any other function. Calling red functions from a red context is simple. They accept parameters and return values. They execute from top to bottom.</p>
<p>Bridging the red/blue gap might seem tricky, but since suspending functions can only be called from other suspending functions, calling red functions from a blue context requires consideration of the most appropriate coroutine scope in which to create a new coroutine. This is a <em>benefit</em> of the design.</p>
<p>Red function semantics aren’t a bug, they’re a feature. Avoid coroutine scopes that have unbounded lifetimes. When in doubt, paint functions responsible for asynchronous work red. Be mindful of coroutine scopes and of function colors.</p>
<blockquote>
<p>If you’re interested in a more complete description of structured concurrency’s design benefits, check out <a href="https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/">“Notes on structured concurrency, or: Go statement considered harmful”</a>.</p>
</blockquote>
Justin BrooksUsing CBL-Mariner 2.0 for ASP.NET Core Apps2022-05-26T14:00:00+00:00http://faithlife.codes/blog/2022/05/using-cbl-mariner-for-asp-net-core-apps/<h2 id="introduction">Introduction</h2>
<p>When <a href="https://microsoft.github.io/CBL-Mariner/announcing-mariner-2.0/">CBL-Mariner 2.0 was announced</a>, I wanted to try
using its container images to run ASP.NET Core web apps.</p>
<p>As others have <a href="https://benfoster.io/blog/optimising-dotnet-docker-images/">already</a> <a href="https://www.thorsten-hans.com/how-to-build-smaller-and-secure-docker-images-for-net5/">blogged</a>,
we want to be sure to use a Docker multi-stage build with the <code class="language-plaintext highlighter-rouge">runtime-deps</code> image in order to reduce the size of the final image.
At first I considered following <a href="https://github.com/dotnet/dotnet-docker/issues/3141#issuecomment-942665747">this example</a>
to build a slimmed-down image based on CBL-Mariner’s<code class="language-plaintext highlighter-rouge"> distroless/minimal</code> image, but then I discovered Microsoft has a
<a href="https://github.com/dotnet/dotnet-docker/blob/main/src/runtime-deps/6.0/cbl-mariner2.0/amd64/Dockerfile">Dockerfile</a>
to build the <code class="language-plaintext highlighter-rouge">runtime-deps</code> image for CBL-Mariner 2.0. It’s <strong>not documented, and very likely not supported</strong>, but you can pull it from
Docker Hub as <code class="language-plaintext highlighter-rouge">mcr.microsoft.com/dotnet/runtime-deps:6.0-cbl-mariner2.0</code>.</p>
<p>I decided to go even further and use the <a href="https://github.com/dotnet/dotnet-docker/blob/main/src/runtime-deps/6.0/cbl-mariner2.0-distroless/amd64/Dockerfile"><code class="language-plaintext highlighter-rouge">distroless</code> image</a>.
Not only will it be smaller, it should be more secure due to reducing unnecessary files in the image, and running your application as non-<code class="language-plaintext highlighter-rouge">root</code>.</p>
<h2 id="multi-stage-dockerfile">Multi-Stage Dockerfile</h2>
<p>Here’s a working Dockerfile that builds an ASP.NET Core web app container image based on <code class="language-plaintext highlighter-rouge">cbl-mariner2.0-distroless</code>:</p>
<pre><code class="language-Containerfile"># build the application using CBL-Mariner 2.0
FROM mcr.microsoft.com/dotnet/sdk:6.0-cbl-mariner2.0 AS build
# copy in the project file and restore dependencies
WORKDIR /app
COPY *.csproj ./
RUN dotnet restore --runtime linux-x64
# copy in all source files
COPY . ./
# publish a trimmed application
RUN dotnet publish --no-restore -c Release -o out --runtime linux-x64 --self-contained true -p:PublishTrimmed=true -p:PublishSingleFile=true -p:LinkMode=trim
# run the application on a 'distroless' image
FROM mcr.microsoft.com/dotnet/runtime-deps:6.0-cbl-mariner2.0-distroless AS runtime
# copy in the built application
WORKDIR /app
COPY --from=build /app/out ./
# use a non-privileged port; https://github.com/dotnet/dotnet-docker/issues/3796
ENV ASPNETCORE_URLS=http://+:8080
# change the entrypoint name to match your web application
ENTRYPOINT ["./MyWebApp"]
</code></pre>
Bradley GraingerFaithlife’s Software Developer Interview Process2021-05-19T16:00:00+00:00http://faithlife.codes/blog/2021/05/faithlife-software-developer-interview-process/<p>The goal of this blog post is to prepare you to get a software development job at Faithlife! If you have additional questions after reading this article, check out <a href="https://faithlife.codes/blog/2021/05/recruitment-frequently-asked-questions/">this blog post</a> that outlines FAQs during Faithlife’s Software Development Interview Process.</p>
<p>Our interview process typically takes one to two months from the initial interview to offer. If you have other interviews close to the offer stage, please let us know! Most of the time, we can make the interview process speedier to accommodate you.</p>
<h3 id="1-phone-screen-12-rounds">1. Phone Screen (1–2 rounds)</h3>
<p>The initial phone screen is with a recruiter, engineering manager, or software development group lead and lasts 30–60 minutes. You can anticipate a mix of behavioral and technical questions.</p>
<h3 id="2-digital-pair-programming-1">2. Digital Pair Programming #1</h3>
<p>This stage consists of a 60–90 minute pair programming session. You’ll be able to choose the language you’re most comfortable with. Before the interview, you’ll receive an email with information that outlines the coding problem you’ll be working on. While we’d like you to have your IDE ready, you shouldn’t write code ahead of time.</p>
<p>For Faithlife de México Interviews:</p>
<p>In addition to the pair programming, you can anticipate a 30–60 minute meeting with some of our Faithlife de México employees.</p>
<h3 id="3-virtual-onsite-pair-programming-2--development-team-meeting--development-team-management-meeting">3. Virtual Onsite (Pair Programming #2 + Development Team Meeting + Development Team Management Meeting)</h3>
<p>The virtual on-site consists of a 90-minute pair programming session followed by a 90-minute meeting with some of our Faithlife employees and a 30-minute meeting with Development Team Management.</p>
<p>In the second pair programming, we want to see you write code in an unfamiliar situation. Most of the time, we choose the IDE and language you’ll code in and don’t provide the coding question ahead of time. As in the first pair programming, you’ll want to use Chrome (not Safari) when linking to the video call.</p>
<p>Faithlife’s Software Development interview process is designed to gradually increase in difficulty. Our goal is not to trip you up but to see a holistic view of your soft skills and technical abilities.</p>
<h3 id="4-offer">4. Offer</h3>
<p>Before an offer, we extend a preliminary offer which includes benefits information + offer specifics. After agreeing to the preliminary offer, you’ll receive the official offer. We then run a background check, speak with references, and finalize a start date. Once that’s completed, you’re officially a Faithlife employee! Congratulations!</p>
<p>We value your input! If you have recommendations on how we can improve our interview process, please email <a href="mailto:devjobs@faithlife.com">devjobs@faithlife.com</a></p>
Maria SmithsonRecruitment Frequently Asked Questions2021-05-18T17:30:00+00:00http://faithlife.codes/blog/2021/05/recruitment-frequently-asked-questions/<h3 id="what-is-it-like-to-work-at-faithlife">What is it like to work at Faithlife?</h3>
<p>Each person’s experience is unique. Faithlife’s <a href="https://www.glassdoor.com/Overview/Working-at-Faithlife-EI_IE888280.11,20.htm">Glassdoor</a> page provides a holistic view of the company culture. You can also check out the following video:</p>
<p><a href="https://faithlifetv.com/media/693630"><img src="https://files.logoscdn.com/v1/assets/11867190/optimized" alt="Working at Faithlife" /></a></p>
<h4 id="what-is-it-like-to-work-at-faithlife-de-méxico">What is it like to work at Faithlife de México?</h4>
<p>En Faithlife de México incorporamos la cultura laboral de Faithlife USA cumpliendo el marco legal de México. Nuestros desarrolladores mexicanos forman parte de diversos equipos estadounidenses trabajando de forma remota desde México. Valoramos la calidad de vida y familiar de cada desarrollador, destacando el trato íntegro hacia nuestros colaboradores.</p>
<p>At Faithlife de México we incorporate the work culture of Faithlife USA in compliance with the Mexican legal framework. Our Mexican developers are part of various U.S. teams working remotely from Mexico. We value the quality of life and family life of each developer, highlighting the integrity of our employees.</p>
<h3 id="where-can-i-learn-more-about-faithlife-products">Where can I learn more about Faithlife Products?</h3>
<p>Check out our <a href="https://faithlife.com/products">Faithlife Products</a> landing page to see a full list of all of our awesome products.</p>
<h3 id="does-faithlife-allow-remote-work">Does Faithlife allow remote work?</h3>
<p>Yes! Our goal is for remote work to be as collaborative, enjoyable, and flexible as possible. Here are some of the ways our remote workers stay connected:</p>
<ol>
<li>Monthly Software Development Lunch Groups specifically for remote workers!</li>
<li>Virtual Tech Talks every 2–3 months.</li>
<li>Weekly Virtual Demo Days that provide the opportunity to see what’s happening in the tech side of the company and showcase the cool things you’re working on.</li>
<li>Virtual launch celebrations.</li>
<li>Wellness activities and giveaways.</li>
<li>Holiday gifts.</li>
</ol>
<h3 id="how-does-faithlife-handle-time-zone-differences">How does Faithlife handle time zone differences?</h3>
<p>For your first few weeks of onboarding, you will be expected to work the same, or very close to the same, hours as your onboarding buddy. Once you have your footing, we generally recommend everyone try to be online between 10 am and 2 pm Pacific Time. This gives decent overlap for the various time zones.</p>
<h3 id="what-does-the-ideal-candidate-look-like-for-this-role">What does the “ideal” candidate look like for this role?</h3>
<p>We don’t have a “one size fits all” approach. For technical roles, we look for people who are excited to grow and learn new technologies. We also look for experience utilizing our tech stack (.NET, JavaScript, React, MySQL). For software development leadership roles, we look for candidates who have experience leading a team or teams while remaining hands-on in the code.</p>
<h3 id="whats-included-in-the-interview-process">What’s included in the interview process?</h3>
<p>Check out <a href="https://faithlife.codes/blog/2021/05/faithlife-software-developer-interview-process/">this blog post</a> to see Faithlife’s Software Development Interview Process in its entirety.</p>
<h3 id="is-there-anything-specific-i-should-prepare-for-prior-to-the-interview">Is there anything specific I should prepare for prior to the interview?</h3>
<p>We recommend the following:</p>
<ol>
<li>Read through our <a href="https://www.glassdoor.com/Overview/Working-at-Faithlife-EI_IE888280.11,20.htm">Glassdoor</a> reviews to get a sense of what it’s like to work at Faithlife.</li>
<li>Look through our products listed on <a href="https://equip.faithlife.com">Faithlife.com</a> — is there a specific product you’d like to work on? If so, tell the person/people you interview with!</li>
<li>We recommend answering behavioral interview questions utilizing the STAR interview format. Here’s <a href="https://www.themuse.com/advice/star-interview-method">an article</a> that describes how to use STAR.</li>
<li>You’re interviewing us just as much as we’re interviewing you! Prior to your interview, we recommend that you write down the questions you’d like to ask us.</li>
<li>Have fun! We’re excited to get to know you better and have designed our interview process to showcase you at your best. Part of the reason we let developers use their favorite IDE in the coding challenge is because we want you to feel comfortable, confident, and excited to code. Best of luck in your interview! We’re rooting for you.</li>
</ol>
<h3 id="what-language-will-i-use-for-pair-programming">What language will I use for pair programming?</h3>
<p>We have two separate pair programming sessions done at different stages in the interview process.</p>
<p>In the first pair programming, you’re able to choose the language that you’re most comfortable with. Prior to the interview, you’ll receive an email with information that outlines the coding problem you’ll be working on. While we’d like you to have your IDE ready, you shouldn’t write code ahead of time.</p>
<p>In the second pair programming, we want to see you write code in an unfamiliar situation. Most of the time, we choose the IDE and language you’ll code in and don’t provide the coding question ahead of time. As in the first pair programming, you’ll want to use Chrome (not Safari) when linking to the video call.</p>
<p>Faithlife’s Software Development interview process is designed to gradually increase in difficulty. Our goal is not to trip you up but to see a holistic view of your soft skills and technical abilities.</p>
<p>Still have questions? Check out <a href="https://faithlife.codes/blog/2021/05/faithlife-software-developer-interview-process/">this blog post</a> to see Faithlife’s Software Development Interview Process in its entirety.</p>
<h3 id="what-is-faithlifes-tech-stack">What is Faithlife’s Tech Stack?</h3>
<p>We primarily develop using C#, JavaScript/TypeScript, React, MySQL, Elasticsearch, Redis, and Azure. If you don’t have experience with one of the listed technologies, still apply! We don’t expect anyone to have experience with each of the technologies in our tech stack.</p>
<h3 id="what-does-it-take-to-be-successful-in-this-role">What does it take to be successful in this role?</h3>
<p>We look for people who live out our HOAGIES values. HOAGIES stands for:</p>
<h4 id="core-values">Core Values</h4>
<p>Honesty — Speak the truth with courage and kindness.<br />
Openness — Share generously, keep asking questions.<br />
Awesomeness — Provoke delighted exclamations: make the customer giggle.<br />
Growth — Onward, upward: growing the church, growing each other.<br />
Initiative — The person you’re waiting for is you.<br />
Elegance — Everything you need and nothing more.<br />
Shipping — Our products can’t help anyone until they have them.</p>
<h4 id="department-values">Department Values</h4>
<p>In addition to our company values (HOAGIES), Faithlife has Software Development Values.</p>
<p><strong>Stewardship</strong><br />
Responsible for planning, managing, and delivery of tasks and projects.</p>
<p>Ownership — Getting the job done<br />
Effectiveness — Doing the right job<br />
Efficiency — Doing the job right</p>
<p><strong>Membership</strong><br />
Working together, sharing responsibility, and growing co-workers.</p>
<p>Participation — Sharing your perspective<br />
Collaboration — Working together<br />
Leadership — Growing from others</p>
<p><strong>Craftsmanship</strong><br />
Building elegant software that equips the Church.</p>
<p>Direction — Seeing the purpose<br />
Breadth — Growing your skillset<br />
Depth — Mastering skills</p>
<h3 id="whats-the-salary-range-for-this-role">What’s the salary range for this role?</h3>
<p>Salaries for each role are reviewed annually to ensure Faithlife stays competitive. The exact salary for this role depends on your background/experience and isn’t something that can be assessed this early in the interview process.</p>
<p>Prior to your first phone interview, please determine what your desired salary range is. At the end of the first phone interview, you’ll have the opportunity to tell the interviewer your desired salary range.</p>
Maria SmithsonUsing Fiddler to inspect Terraform Azure requests2021-04-21T12:00:00+00:00http://faithlife.codes/blog/2021/04/using-fiddler-to-inspect-terraform-azure-requests/<p>When using Terraform to configure Azure resources, you may get an unhelpful error message (from <code class="language-plaintext highlighter-rouge">terraform apply</code>) similar to the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>module.<name>.<resource>.<name>: Creating...
╷
│ Error: Error creating/updating Some Thing. Component: Failure sending request:
StatusCode=409 -- Original Error: autorest/azure: Service returned an error. Status=<nil> <nil>
</code></pre></div></div>
<p>Setting <code class="language-plaintext highlighter-rouge">TF_LOG='DEBUG'</code> will dump the HTTP responses to <code class="language-plaintext highlighter-rouge">stderr</code>, but it’s extremely verbose and hard to find the
information you’re looking for. A much easier way to view HTTP responses is by using a HTTP debugger such as Fiddler.</p>
<p>There are a few setup steps you need to perform to enable this:</p>
<ol>
<li>Find your Fiddler certificate. Run <code class="language-plaintext highlighter-rouge">certmgr.msc</code> and in Intermediate Certification Authorities > Certificates, find <code class="language-plaintext highlighter-rouge">DO_NOT_TRUST_FiddlerRoot</code>.</li>
<li>Right-click > All Tasks > Export, export as Base-64 encoded X.509 (.CER). Save it to a temporary file.</li>
<li>Open <code class="language-plaintext highlighter-rouge">C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\Lib\site-packages\certifi\cacert.pem</code> in a text editor running as Administrator.</li>
<li>Paste the contents of the CER file (from step 2) to the end of the file; save it.</li>
</ol>
<p>Then, to debug Terraform requests:</p>
<ol>
<li>Run Fiddler; stop capturing all traffic.</li>
<li>Set HTTP(S) proxies: <code class="language-plaintext highlighter-rouge">$env:HTTP_PROXY='http://127.0.0.1:8888'; $env:HTTPS_PROXY='http://127.0.0.1:8888'</code></li>
<li>Run <code class="language-plaintext highlighter-rouge">terraform apply</code> and watch the requests stream into Fiddler.</li>
<li>Now it’s easy to select a single failing request and inspect the response body.</li>
</ol>
Bradley GraingerIgnoring ASP.NET Core static file requests in New Relic2020-01-08T13:25:00+00:00http://faithlife.codes/blog/2020/01/ignoring-asp-net-core-static-file-requests-in-new-relic/<p>We use New Relic for monitoring ASP.NET Core web applications. In one web service, we want to ignore requests for static files;
there are a large number of static file requests (that are served quickly), which artificially lowers the average response time and
error rate, and they’re unnecessary noise in New Relic.</p>
<p>One way would be to install the <a href="https://www.nuget.org/packages/NewRelic.Agent.Api/">New Relic API NuGet package</a>, then
explicitly ignore the transaction:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">app</span><span class="p">.</span><span class="nf">UseStaticFiles</span><span class="p">(</span><span class="k">new</span> <span class="n">StaticFileOptions</span>
<span class="p">{</span>
<span class="n">OnPrepareResponse</span> <span class="p">=</span> <span class="n">context</span> <span class="p">=></span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">context</span><span class="p">.</span><span class="n">File</span><span class="p">.</span><span class="n">Name</span> <span class="p">==</span> <span class="s">"file-to-ignore.txt"</span><span class="p">)</span>
<span class="n">NewRelic</span><span class="p">.</span><span class="n">Api</span><span class="p">.</span><span class="n">Agent</span><span class="p">.</span><span class="n">NewRelic</span><span class="p">.</span><span class="nf">IgnoreTransaction</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">});</span>
</code></pre></div></div>
<p>Another way would be to use <a href="https://docs.newrelic.com/docs/agents/net-agent/custom-instrumentation/introduction-net-custom-instrumentation">custom instrumentation</a>.
This is an XML file that specifies method names to match and ignore.</p>
<p>My first approach ignored <code class="language-plaintext highlighter-rouge">StaticFileMiddleware.Invoke</code>. Unfortunately, the static file middleware
will be called on every request (if you put <code class="language-plaintext highlighter-rouge">UseStaticFiles</code> before <code class="language-plaintext highlighter-rouge">UseMvc</code>), so this method will always be invoked and every request
will be ignored.</p>
<p>I tried to refine this by ignoring a method that’s <a href="https://github.com/dotnet/aspnetcore/blob/c17ce436b8703470231e7979cead042f5682c3f5/src/Middleware/StaticFiles/src/StaticFileMiddleware.cs#L133">only called when</a>
a static file is served:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><match</span> <span class="na">assemblyName=</span><span class="s">"Microsoft.AspNetCore.StaticFiles"</span> <span class="na">className=</span><span class="s">"Microsoft.AspNetCore.StaticFiles.StaticFileContext"</span><span class="nt">></span>
<span class="nt"><exactMethodMatcher</span> <span class="na">methodName=</span><span class="s">"SendAsync"</span> <span class="nt">/></span>
<span class="nt"><exactMethodMatcher</span> <span class="na">methodName=</span><span class="s">"SendStatusAsync"</span> <span class="nt">/></span>
<span class="nt"></match></span>
</code></pre></div></div>
<p>However, the static file requests were still being tracked. Inspecting the New Relic log file revealed this line:</p>
<blockquote>
<p>[Error] 2020-01-07 21:51:50 Skipping sequential layout method: (Module: C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\2.2.6\Microsoft.AspNetCore.StaticFiles.dll, AppDomain: clrhost)[Microsoft.AspNetCore.StaticFiles]Microsoft.AspNetCore.StaticFiles.StaticFileContext.SendStatusAsync(System.Int32)</p>
</blockquote>
<p>“Sequential layout” made me think of <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.layoutkind?view=netframework-4.8">the <code class="language-plaintext highlighter-rouge">LayoutKind</code> enum</a>.
When a coworker pointed out that <code class="language-plaintext highlighter-rouge">StaticFileContext</code> <a href="https://github.com/dotnet/aspnetcore/blob/v2.2.6/src/Middleware/StaticFiles/src/StaticFileContext.cs">is a <code class="language-plaintext highlighter-rouge">struct</code></a>,
I made the assumption that New Relic can’t profile methods belonging to a struct, at least when <code class="language-plaintext highlighter-rouge">[StructLayout(LayoutKind.Sequential)]</code> is applied, which is the C#
compiler default. (I don’t know why it has this limitation. Searching for the log message above provided no results; maybe it will now at least return this blog post?)</p>
<p>So I next searched for a method (on a <strong>class</strong>) that is only invoked when <code class="language-plaintext highlighter-rouge">StaticFileContext</code> sends a static file, and found <a href="https://github.com/dotnet/aspnetcore/blob/v3.1.0/src/Middleware/StaticFiles/src/LoggerExtensions.cs"><code class="language-plaintext highlighter-rouge">LoggerExtensions</code></a>.</p>
<p>I finally settled on this custom instrumentation to ignore <code class="language-plaintext highlighter-rouge">HEAD</code> requests that are handled by <code class="language-plaintext highlighter-rouge">StaticFileMiddleware</code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><instrumentation></span>
<span class="nt"><tracerFactory</span> <span class="na">name=</span><span class="s">"NewRelic.Agent.Core.Tracer.Factories.IgnoreTransactionTracerFactory"</span><span class="nt">></span>
<span class="nt"><match</span> <span class="na">assemblyName=</span><span class="s">"Microsoft.AspNetCore.StaticFiles"</span> <span class="na">className=</span><span class="s">"Microsoft.AspNetCore.StaticFiles.LoggerExtensions"</span><span class="nt">></span>
<span class="nt"><exactMethodMatcher</span> <span class="na">methodName=</span><span class="s">"Handled"</span> <span class="nt">/></span> <span class="c"><!-- .NET Core 3.0 --></span>
<span class="nt"><exactMethodMatcher</span> <span class="na">methodName=</span><span class="s">"LogHandled"</span> <span class="nt">/></span> <span class="c"><!-- .NET Core 2.2.6 --></span>
<span class="nt"></match></span>
<span class="nt"></tracerFactory></span>
<span class="nt"></instrumentation></span>
</code></pre></div></div>
<p>Use with caution: there is no logging that a request is being ignored due to custom instrumentation, so you can (and I did!)
waste a lot of time tracking down missing requests that are due to this. The code-based approach at the top of this post
is likely to be better in most situations.</p>
Bradley GraingerIntegrating Dependabot with GitHub Enterprise using Jenkins2019-12-13T08:00:00+00:00http://faithlife.codes/blog/2019/12/integrating-dependabot-with-github-enterprise/<p><a href="https://dependabot.com/">Dependabot</a> is a GitHub bot that scans your repositories for outdated
dependencies, then opens PRs to update them. Although <a href="https://github.com/dependabot/dependabot-core">the code</a>
supports GitHub Enterprise, the hosted version of Dependabot only supports GitHub.com. This
post will walk you through setting up dependabot-core so that it can scan for outdated dependencies
in a GitHub Enterprise repository, using Jenkins to run it.</p>
<p>Firstly, most of the credit should go to the <a href="https://github.com/dependabot/dependabot-core/graphs/contributors">dependabot team</a>
who have made the core code freely available, published a Docker image containing it, and written a script that automates the process.</p>
<p>Secondly, this assumes you have a GitHub Enterprise instance available at <code class="language-plaintext highlighter-rouge">https://github.example.com</code>, and
Jenkins at <code class="language-plaintext highlighter-rouge">https://jenkins.example.com</code>. Change the URLs as appropriate in the following instructions.</p>
<h2 id="1-set-up-credentials">1. Set Up Credentials</h2>
<p>You will need both GitHub Enterprise and GitHub.com personal access tokens. You can create these at
<code class="language-plaintext highlighter-rouge">https://github.example.com/settings/tokens</code> and <code class="language-plaintext highlighter-rouge">https://github.com/settings/tokens</code>. Select the
<strong>repo</strong> scope when creating the tokens. PRs opened by Dependabot will be created by the GitHub user who creates the tokens.</p>
<p>These credentials need to be stored safely in Jenkins. Go to <code class="language-plaintext highlighter-rouge">https://jenkins.example.com/credentials/store/system/domain/_/newCredentials</code>
and create two new <strong>Secret text</strong> credentials (one per PAT) as follows:</p>
<ul>
<li><strong>Scope:</strong> Global</li>
<li><strong>Secret:</strong> (paste personal access token here)</li>
<li><strong>ID:</strong> <code class="language-plaintext highlighter-rouge">dependabot-github-enterprise</code> / <code class="language-plaintext highlighter-rouge">dependabot-github-com</code> (ID based on PAT)</li>
<li><strong>Description:</strong> Dependabot PAT for GitHub Enterprise / Dependabot PAT for GitHub (name based on PAT)</li>
</ul>
<h2 id="2-create-the-jenkins-job">2. Create the Jenkins Job</h2>
<p>The Dependabot build can run for one package manager at a time. These instructions configure it
for NuGet. The other available options are <a href="https://github.com/dependabot/dependabot-script/blob/68afed075c54ae21abdf5f9863ebae091bd4f53f/generic-update-script.rb#L27-L42">listed here</a>;
adjust the script as necessary.</p>
<p>At Jenkins, create a new Freestyle job named <strong>Dependabot-NuGet</strong> with the following configuration:</p>
<ul>
<li><strong>GitHub project > Project url:</strong> https://github.com/dependabot/dependabot-script/</li>
<li><strong>This project is parameterized</strong>
<ul>
<li>Add a <strong>String Parameter</strong>:
<ul>
<li><strong>Name:</strong> PROJECT_PATH</li>
<li><strong>Description:</strong> GitHub repository name, including org</li>
</ul>
</li>
</ul>
</li>
<li><strong>Restrict where this project can be run:</strong> (This job requires a Linux build node that has access to Docker, select as appropriate)</li>
<li><strong>Source Code Management:</strong> Git
<ul>
<li><strong>Repository URL:</strong> https://github.com/dependabot/dependabot-script.git</li>
</ul>
</li>
<li><strong>Build Environment</strong>
<ul>
<li><strong>Use secret text(s) or file(s)</strong> - Add the following Bindings
<ul>
<li><strong>Secret text</strong>
<ul>
<li><strong>Variable:</strong> GITHUB_ENTERPRISE_ACCESS_TOKEN</li>
<li><strong>Credentials:</strong> Dependabot PAT for GitHub Enterprise</li>
</ul>
</li>
<li><strong>Secret text</strong>
<ul>
<li><strong>Variable:</strong> GITHUB_ACCESS_TOKEN</li>
<li><strong>Credentials:</strong> Dependabot PAT for GitHub</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><strong>Build</strong>
<ul>
<li><strong>Execute shell</strong></li>
</ul>
</li>
</ul>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker pull dependabot/dependabot-core:latest
docker run <span class="nt">-v</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2">:/home/dependabot/dependabot-script"</span> <span class="nt">-w</span> /home/dependabot/dependabot-script dependabot/dependabot-core bundle <span class="nb">install</span> <span class="nt">-j</span> 3 <span class="nt">--path</span> vendor
docker run <span class="nt">-v</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2">:/home/dependabot/dependabot-script"</span> <span class="nt">-w</span> /home/dependabot/dependabot-script <span class="nt">-e</span> <span class="nv">GITHUB_ENTERPRISE_HOSTNAME</span><span class="o">=</span>github.example.com <span class="nt">-e</span> <span class="nv">GITHUB_ENTERPRISE_ACCESS_TOKEN</span><span class="o">=</span><span class="nv">$GITHUB_ENTERPRISE_ACCESS_TOKEN</span> <span class="nt">-e</span> <span class="nv">GITHUB_ACCESS_TOKEN</span><span class="o">=</span><span class="nv">$GITHUB_ACCESS_TOKEN</span> <span class="nt">-e</span> <span class="nv">PACKAGE_MANAGER</span><span class="o">=</span>nuget <span class="nt">-e</span> <span class="nv">PROJECT_PATH</span><span class="o">=</span><span class="nv">$PROJECT_PATH</span> dependabot/dependabot-core bundle <span class="nb">exec </span>ruby ./generic-update-script.rb
</code></pre></div></div>
<p>Click <strong>Save</strong> to create the job.</p>
<h2 id="3-run-the-jenkins-job">3. Run the Jenkins Job</h2>
<p>At <code class="language-plaintext highlighter-rouge">https://jenkins.example.com/job/Dependabot-NuGet/</code>, Click <strong>Build with Parameters</strong>. You will be prompted for PROJECT_PATH;
enter <code class="language-plaintext highlighter-rouge">MyOrg/MyRepo</code>. Click <strong>Build</strong> to start.</p>
<p>The build should run, and list all the out-of-date dependencies it found; it will open a PR on GitHub Enterprise for each outdated dependency.</p>
Bradley GraingerBeing Like Water2019-10-30T08:00:00+00:00http://faithlife.codes/blog/2019/10/being-like-water/<p>I often forget just how young the software craft is. The landscape moves quickly, and many I’ve known have difficulty keeping up with the shifting ground beneath them. A couple of years ago I stumbled onto Huyen Tue Dao’s talk <a href="https://academy.realm.io/posts/360-andev-2017-keynote-huyen-tue-keeping-up-with-android/">Be Like Water</a>. She highlights the importance of being adaptable and reminds us that skills to build good software aren’t necessarily coupled to the systems we work with any given day. Shortly after joining Faithlife, a colleague shared Dan McKinley’s essay <a href="https://mcfunley.com/choose-boring-technology">Choose Boring Technology</a>, which tempers the virtue of adaptability in a helpful way. Some of these ideas stick with me today in some form or another and have substantially influenced the direction of the Faithlife Android app’s systems under the hood. In the year since <a href="https://faithlife.codes/blog/2018/11/android-mvp-with-coroutines/">I last wrote about the Faithlife app</a>, several things have changed. Many have stayed the same.</p>
<h2 id="architecture">Architecture</h2>
<p>Overall, our high level architecture is pretty similar when compared to the previous post. The notable difference is that we have come to adopt more of a Model-View-ViewModel (MVVM) paradigm.</p>
<p><img src="https://files.logoscdn.com/v1/files/38396971/content.png?signature=fxEvcmdfLKaORNRaAdpg5dDSgTE" alt="Current Architecture Overview" /></p>
<p>Instead of contract interfaces facilitating precise communication between the view and presentation/business logic in the presenter, the view takes inputs from the user and requests that the viewmodel perform some action. The view is notified of new information by observing properties on the viewmodel.</p>
<h2 id="android-jetpack">Android Jetpack</h2>
<p>Jetpack continues to be a huge boon to our small team’s productivity. We make use of several components like navigation, viewmodel, and livedata and continue to look to Jetpack components first when we have need of a supplement to the framework itself. Google forms the vessel for Android development, so using their materials will make us more adaptable in the future with less hassle.</p>
<h4 id="viewmodel--livedata">ViewModel & LiveData</h4>
<p><code class="language-plaintext highlighter-rouge">ViewModel</code> has made handling the lifecycle issues of the old days much less painful. There were solutions before (like headless fragments, <em>shudder</em>) for handling orientation changes well, but none provide the simplicity of <code class="language-plaintext highlighter-rouge">ViewModel</code>. Recently, some nice Kotlin coroutine features were added to the class that make structured concurrency easier than ever.</p>
<p><code class="language-plaintext highlighter-rouge">LiveData<T></code> make observing changes in viewmodel state a breeze by providing observability in a lifecycle-aware way. I wish the API had a more idiomatic affordance for setting values on the main thread with coroutines. Currently, you either have to call <code class="language-plaintext highlighter-rouge">postValue</code> or use <code class="language-plaintext highlighter-rouge">withContext(Dispatchers.Main)</code> and set <code class="language-plaintext highlighter-rouge">value</code> property directly. The latter is more idiomatic, the former is a little safer as it’s impossible to forget to set values on the main thread. We’ve made a habit of the latter since our viewmodel suspend functions are typically called from the main dispatcher context anyway. It’s a small concern for now.</p>
<h4 id="data-binding">Data Binding</h4>
<p>The Jetpack data binding library is something we’re gravitating away from. It has some nice qualities that definitely encourage a separation of view and logic concerns. However, the build time impact is—while not huge by any means—considerable. We decided to try the system some time after making the decision to adopt the MVVM paradigm. Before then, we just modified view properties directly in the <code class="language-plaintext highlighter-rouge">LiveData</code> observers we registered in their parent <code class="language-plaintext highlighter-rouge">Fragment</code>. The code generator for data binding generates some impressively elaborate Java code, but doesn’t play all that well with Kotlin. One example of this is that to use a top-level extension function in a data binding expression, you had to be aware that the function was compiled into a class that had the name of the file in which the function was defined appended with ‘Kt’. <em>Then</em> you had to know the way extension functions work. The object being extended (the receiver) is passed as the first argument of the function, then all the other arguments are passed.</p>
<p>For example, to use an <code class="language-plaintext highlighter-rouge">AliasKind</code> extension defined in <code class="language-plaintext highlighter-rouge">MentionExtensions.kt</code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><layout></span>
<span class="nt"><data></span>
<span class="nt"><import</span> <span class="na">type=</span><span class="s">"com.faithlife.mobile.MentionExtensionsKt"</span> <span class="nt">/></span>
<span class="nt"><variable</span>
<span class="na">name=</span><span class="s">"aliasKind"</span>
<span class="na">type=</span><span class="s">"com.faithlife.mobile.AliasKind"</span> <span class="nt">/></span>
<span class="nt"></data></span>
<span class="nt"><androidx.constraintlayout.widget.ConstraintLayout></span>
<span class="nt"><TextView</span>
<span class="na">android:text=</span><span class="s">"@{MentionExtensionsKt.getDisplayName(aliasKind, context)}"</span>
<span class="nt">/></span>
<span class="nt"></androidx.constraintlayout.widget.ConstraintLayout></span>
<span class="nt"></layout></span>
</code></pre></div></div>
<blockquote>
<p>Slightly modified markup from an internal discussion on the subject</p>
</blockquote>
<p>The data binding expression language also had magic variables that you could seemingly reference out of nowhere (like <code class="language-plaintext highlighter-rouge">context</code>). None of this is insurmountable, but we’ve agreed that manipulating views in observer listeners is better.</p>
<p>We’re likely going to try out the relatively new view binding system that seems to be a more type-safe way to get references to views from framework components. Adios <code class="language-plaintext highlighter-rouge">findViewById</code>.</p>
<p>We’re also keeping an eye on Jetpack Compose. Data binding expressions are a step in the wrong direction considering the new UI toolkit doesn’t use any markup at all.</p>
<h2 id="dagger-2">Dagger 2</h2>
<p>We’re also continuing to improve our use of Dagger as a facilitator of dependency injection. We never got into Dagger-Android as it seemed like it was somehow both more magical and constraining than it was worth. Google has shown they agree recently by announcing its deprecation. We’ve worked toward isolating components to subgraphs where possible and have used relatively recent support for setting custom fragment factories on any <code class="language-plaintext highlighter-rouge">FragmentManager</code>, in order to make dependency injection more possible in framework components where passing arguments to constructors was historically a bad idea. Our fragments and viewmodels can be passed dependencies via simple <code class="language-plaintext highlighter-rouge">@Inject</code> annotated constructors.</p>
<p>All of this makes isolating tests much easier, encourages reuse among different systems, and makes the separation of concerns much clearer among components in the system.</p>
<h2 id="more-than-water">More Than Water</h2>
<p>Engineering requires balance. Every choice is a trade-off. Two of our company values, shipping and elegance, characterize this tension well. We were early adopters of Kotlin coroutines and Jetpack Navigation. We didn’t find much value in Koin. We saw promise in databinding so we gave it a shot, but we’re reconsidering so that we might more easily pick up Compose down the road. Choosing how you invest your effort is a tremendously important skill for building good software. We don’t always nail it, but we certainly aspire to. We are adaptable, but we’re more than water; aware of our environment we strive to make choices that will put us in the best position to build a great product.</p>
Justin BrooksImproving WPF Text Display Performance2019-06-03T12:55:00+00:00http://faithlife.codes/blog/2019/06/improving-wpf-text-display-performance/<p>Logos 8 is a WPF Windows desktop application. It includes fonts that are embedded in the application
itself (instead of being installed in the system’s Fonts folder). When we added a Chinese Bible to the
Logos library, we bundled <a href="https://www.google.com/get/noto/help/cjk/">Noto Sans CJK</a> for display
of the Chinese text. But we found that drawing a screen of text would take multiple seconds (even
on a high-end machine) and allocate <em>gigabytes</em> of RAM.</p>
<p><img src="/blog/images/ReadFileFragmentTime.png" alt="Time usage of ReadFileFragment" /></p>
<p><img src="/blog/images/ReadFileFragmentMemory.png" alt="Memory usage of ReadFileFragment" /></p>
<p>The time is spent in WPF functions called by DWrite. (These are also the same functions responsible
for the excessive memory allocation.) <a href="https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal">DirectWrite</a>
is a hardware-accelerated text layout and rendering subsystem in Windows. It should provide
high-performance text rendering, but it’s clearly not in this situation.</p>
<p>Like many Win32 APIs, DirectWrite is built with <a href="https://en.wikipedia.org/wiki/Component_Object_Model">COM</a>,
a 25-year-old technology for creating software components. DWrite provides COM interfaces that an
application can call to render text, and also defines interfaces that a client can implement to
extend DWrite, e.g., by providing a custom font loader.</p>
<p>WPF implements a number of DWrite COM interfaces to make fonts embedded in .NET assemblies
available to DWrite:</p>
<ul>
<li><a href="https://docs.microsoft.com/en-us/windows/desktop/api/dwrite/nn-dwrite-idwritefontcollectionloader"><code class="language-plaintext highlighter-rouge">IDWriteFontCollectionLoader</code></a>: creates a <code class="language-plaintext highlighter-rouge">IDWriteFontFileEnumerator</code> that enumerates fonts</li>
<li><a href="https://docs.microsoft.com/en-us/windows/desktop/api/dwrite/nn-dwrite-idwritefontfileenumerator"><code class="language-plaintext highlighter-rouge">IDWriteFontFileEnumerator</code></a>: enumerates a collection of <code class="language-plaintext highlighter-rouge">IDWriteFontFile</code> objects</li>
<li><a href="https://docs.microsoft.com/en-us/windows/desktop/api/dwrite/nn-dwrite-idwritefontfile"><code class="language-plaintext highlighter-rouge">IDWriteFontFile</code></a>: provides a “reference key” that identifies a font and a <code class="language-plaintext highlighter-rouge">IDWriteFontFileLoader</code> that can load the font</li>
<li><a href="https://docs.microsoft.com/en-us/windows/desktop/api/dwrite/nn-dwrite-idwritefontfileloader"><code class="language-plaintext highlighter-rouge">IDWriteFontFileLoader</code></a>: creates a stream from a reference key</li>
<li><a href="https://docs.microsoft.com/en-us/windows/desktop/api/dwrite/nn-dwrite-idwritefontfilestream"><code class="language-plaintext highlighter-rouge">IDWriteFontFileStream</code></a>: gets a font file’s size and reads chunks of the file</li>
</ul>
<p>Our primary problem is with <code class="language-plaintext highlighter-rouge">IDWriteFontFileStream</code> and now, thanks to <a href="https://blogs.windows.com/buildingapps/2018/12/04/announcing-open-source-of-wpf-windows-forms-and-winui-at-microsoft-connect-2018/">Microsoft making WPF open source</a>,
then recently <a href="https://twitter.com/dotMorten/status/1131276647074656256">adding all the code</a>,
we can see exactly where the problem lies. <code class="language-plaintext highlighter-rouge">ReadFileFragment</code> allocates a buffer, copies unmanaged
memory into it, pins the buffer, then returns its address to DWrite
(<a href="https://github.com/dotnet/wpf/blob/6b989e5263fd2634ed0ec0a512d6b8f41c5d8c7b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/DWriteWrapper/FontFileStream.cpp#L68-L92">source code link</a>).</p>
<p>This is almost a worst-case scenario for the .NET garbage collector: hundreds of megabytes of memory
are being allocated, every buffer is being pinned so the GC can’t compact the heap, and the buffers are
probably living long enough (at least across multiple native/managed interop calls) to make it out of gen 0.</p>
<p>Moreover, it’s completely unnecessary. IDWriteFontFileStream only needs to return a pointer to a
portion of the font (no copying is necessary), which is simple if the font is already loaded in memory.
And it is: embedded fonts are concatenated in the “Resources” section of the assembly, and every
.NET DLL is mapped into the virtual address space of the process.</p>
<p>I wrote an implementation of <code class="language-plaintext highlighter-rouge">IDWriteFontFileStream</code> that is initialised with a pointer to the beginning
of the font data. <code class="language-plaintext highlighter-rouge">ReadFileFragment</code> becomes simply a pointer addition and assignment: no allocation,
no memcpy, extremely low overhead.</p>
<script src="https://gist.github.com/bgrainger/ae266e4b305b83503a852343869a2a68.js"></script>
<p>Getting a pointer to the beginning of the font data is somewhat trickier. We can parse the <a href="http://web.archive.org/web/20080828195258/https://msdn.microsoft.com/en-us/magazine/cc301805.aspx">PE header</a>
to find the <code class="language-plaintext highlighter-rouge">.text</code> section that contains the <a href="https://codingwithspike.wordpress.com/2012/08/12/building-a-net-disassembler-part-3-parsing-the-text-section/">CLR Header</a>.
From this we can find the offset to the embedded resources and use <a href="https://github.com/dotnet/coreclr/blob/master/src/System.Private.CoreLib/shared/System/Resources/ResourceReader.cs">ResourceReader.cs</a>
as a guide for parsing the binary format. This will give us the address of each font file in memory, and
enough information to construct a <a href="https://docs.microsoft.com/en-us/dotnet/framework/wpf/app-development/pack-uris-in-wpf">pack URI</a> for each font.</p>
<p>We now just have to find a way to replace WPF’s inefficient <code class="language-plaintext highlighter-rouge">FontFileStream</code> with our optimised
version. The interface-based nature of COM works to our advantage here. If we can replace the
<code class="language-plaintext highlighter-rouge">IDWriteFactory</code> interface that WPF calls into when it calls <code class="language-plaintext highlighter-rouge">RegisterFontCollectionLoader</code>, we could
substitute our own <code class="language-plaintext highlighter-rouge">IDWriteFontCollectionLoader</code> implementation, and ultimately return the efficient
stream from <code class="language-plaintext highlighter-rouge">IDWriteFontFileLoader::CreateStreamFromKey</code>.</p>
<p>I first looked into replacing WPF’s reference to the <code class="language-plaintext highlighter-rouge">IDWriteFactory</code> object, e.g., by using reflection
to change a private static field. But it was created by a <a href="https://github.com/dotnet/wpf/blob/8da038868e488699ecd641900fb9da0e6f860076/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/FontCache/DWriteFactory.cs#L33-L36">static constructor</a>
and didn’t seem possible to intercept early enough in the application’s lifetime.</p>
<p>Instead, I found a great library for Windows API hooking: <a href="https://easyhook.github.io/">EasyHook</a>.
This let me override the DWriteCreateFactory method exported from DWrite.dll for our application’s
process. We hook the API very early in startup, before WPF has called it. Then, when WPF does call it,
we instantiate the real <code class="language-plaintext highlighter-rouge">DWriteFactory</code> but return our own custom implementation of <code class="language-plaintext highlighter-rouge">IDWriteFactory</code>
that simply forwards most API calls to the real factory.</p>
<p>The two calls that aren’t forwarded directly are <code class="language-plaintext highlighter-rouge">RegisterFontCollectionLoader</code> and <code class="language-plaintext highlighter-rouge">RegisterFontFileLoader</code>.
Instead, another proxy object is created (around WPF’s loaders) and the proxy is registered with the
real DWrite. Finally, when DWrite calls <code class="language-plaintext highlighter-rouge">IDWriteFontFileLoader::CreateStreamFromKey</code> on our proxy,
we examine the “reference key” that’s supplied as a font identifier. If we don’t recognise it, we forward
the call to the original loader. But if it’s a pack URI that matches one created for our assembly resources
(above), an optimised <code class="language-plaintext highlighter-rouge">IDWriteFontFileStream</code> is created and returned instead.</p>
<p>The results <a href="https://community.logos.com/forums/p/139532/899899.aspx#899899">are incredible</a>:
instead of displaying one page of text every 2–3 seconds, we can now refresh the display dozens of
times per second. Managed allocations have been eliminated, there are no native/managed interop
calls from DWrite to the font loader, and CPU usage has been reduced by at least 100×.</p>
<p><img src="/blog/images/GetHitTestBoundsFixed.png" alt="Updated profile with optimised code" /></p>
<p>Faithlife isn’t using WPF on .NET Core yet but if we do, we’ll consider contributing this back to
<a href="https://github.com/dotnet/wpf">dotnet/wpf</a> so that we don’t have to use hacks like API hooking
and so that all WPF applications can benefit.</p>
<p><strong>Update:</strong> We’ve opened <a href="https://github.com/dotnet/wpf/pull/6254">PR 6254</a> to fix this problem in WPF.</p>
<p>(The code mentioned in this post was primarily written in 2017, extending code we first wrote
in 2015 to integrate HarfBuzz with DWrite, so structuring it to be contributed to an open source
project was not a concern at the time.)</p>
Bradley GraingerModel-View-Presenter on Android with Kotlin Coroutines2018-11-05T08:00:00+00:00http://faithlife.codes/blog/2018/11/android-mvp-with-coroutines/<p>Kotlin just released a major version that brings coroutines out of experimental status. We’ve been using coroutines for around six months now and have learned quite a bit about how to use coroutines and how not to. The coroutines library is a powerful tool and seems to be on the rise in popularity. There is a lot of great info out there about using coroutines in MVVM projects and in projects that make heavy use of Android Jetpack’s <a href="https://developer.android.com/topic/libraries/architecture/">Architecture Components</a>. Instead, we’d like to share how we are using coroutines in a model-view-presenter architected application.</p>
<p><strong>Disclaimer:</strong> I’ll assume you’re familiar with coroutines at a high level. If you need an intro, there are plenty of resources online that would serve you better than I would. I’ll link some in the Extras section.</p>
<h3 id="our-architecture-at-a-glance">Our Architecture at a Glance</h3>
<p>MVP is a tried and true architecutral pattern for Android. Recently we’ve been successfully leveraging new technologies, like coroutines and architecture components, to make MVP nicer than ever. Our current implementation is a stepping stone toward a better future that embraces a reactive paradigm with coroutines as the bedrock.</p>
<p><img src="https://files.logoscdn.com/v1/files/17840805/content.png?signature=EonlfGfeo-hUDML5vKZRfXwL4O8" alt="Current Architecture Overview" /></p>
<h3 id="implementation">Implementation</h3>
<p>There are certainly ways we can improve our architecture. We know we have a way to go yet, but we’ve found success on the road to a clean architecture.</p>
<h4 id="structured-concurrency">Structured Concurrency</h4>
<p>A driving principle of coroutines development is that coroutines are like lightweight threads. The early experimental versions of coroutines were a bit of a wild west that encouraged creating new coroutines all the time. A typical presenter might look something like this previous to coroutines version 0.26.</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">PlaylistPresenter</span> <span class="p">:</span> <span class="nc">PlaylistContract</span><span class="p">.</span><span class="nc">Presenter</span> <span class="p">{</span>
<span class="k">private</span> <span class="kd">var</span> <span class="py">job</span><span class="p">:</span> <span class="nc">Job</span><span class="p">?</span> <span class="p">=</span> <span class="k">null</span>
<span class="k">override</span> <span class="k">fun</span> <span class="nf">long</span> <span class="nf">fetchSongs</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// The default context was 'CommonPool' which </span>
<span class="c1">// delegated the work to a pool of non-ui threads.</span>
<span class="c1">// A new job is created for this coroutine.</span>
<span class="n">job</span> <span class="p">=</span> <span class="nf">launch</span> <span class="p">{</span>
<span class="kd">val</span> <span class="py">songs</span> <span class="p">=</span> <span class="nf">fetchSongsFromNetwork</span><span class="p">()</span>
<span class="nf">withContext</span><span class="p">(</span><span class="nc">UI</span><span class="p">)</span> <span class="p">{</span>
<span class="n">view</span><span class="o">?.</span><span class="nf">updateSongList</span><span class="p">(</span><span class="n">songs</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">override</span> <span class="k">fun</span> <span class="nf">cleanup</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// imagine tracking many job objects for multiple</span>
<span class="c1">// coroutines executing simultaneously. _shudder_</span>
<span class="n">job</span><span class="o">?.</span><span class="nf">cancel</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Unlike threads, which are often created in a global scope, coroutines can be tightly scoped to the entities that own them. Instead of firing coroutines into the ether and hoping everything goes well, the team working on coroutines introduced a better way with kotlinx.coroutines 0.26.</p>
<p>The <code class="language-plaintext highlighter-rouge">CoroutineScope</code> interface facilitates constraining a coroutine’s lifetime by an object with lifetime semantics.</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Presenter</span> <span class="p">:</span> <span class="nc">PlaylistContract</span><span class="p">.</span><span class="nc">Presenter</span><span class="p">,</span> <span class="nc">CoroutineScope</span> <span class="p">{</span>
<span class="k">private</span> <span class="kd">val</span> <span class="py">job</span> <span class="p">=</span> <span class="nc">Job</span><span class="p">()</span>
<span class="k">override</span> <span class="kd">val</span> <span class="py">coroutineContext</span><span class="p">:</span> <span class="nc">CoroutineContext</span> <span class="p">=</span> <span class="n">job</span> <span class="p">+</span> <span class="nc">Dispatchers</span><span class="p">.</span><span class="nc">IO</span>
<span class="k">override</span> <span class="k">fun</span> <span class="nf">long</span> <span class="nf">fetchSongs</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// This launch uses the coroutineContext defined</span>
<span class="c1">// by the coroutine presenter.</span>
<span class="nf">launch</span> <span class="p">{</span>
<span class="kd">val</span> <span class="py">songs</span> <span class="p">=</span> <span class="nf">fetchSongsFromNetwork</span><span class="p">()</span>
<span class="nf">withContext</span><span class="p">(</span><span class="nc">Dispatchers</span><span class="p">.</span><span class="nc">Main</span><span class="p">)</span> <span class="p">{</span>
<span class="n">view</span><span class="o">?.</span><span class="nf">updateSongList</span><span class="p">(</span><span class="n">songs</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">override</span> <span class="k">fun</span> <span class="nf">cleanup</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// By default, every coroutine initiated in this context</span>
<span class="c1">// will use the job and dispatcher specified by the </span>
<span class="c1">// coroutineContext.</span>
<span class="c1">// The coroutines are scoped to their execution environment.</span>
<span class="n">job</span><span class="p">.</span><span class="nf">cancel</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now the presenter is a <code class="language-plaintext highlighter-rouge">CoroutineScope</code>, and coroutines started in this scope can more appropriately be cleaned up or cancelled when the presenter is no longer necessary.</p>
<h4 id="a-view-presenter-contract-for-coroutines">A View-Presenter Contract for Coroutines</h4>
<p>Fortunately, generalizing the scope impementation to all presenters in a project is fairly straightforward.</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">interface</span> <span class="nc">BaseContract</span> <span class="p">{</span>
<span class="kd">interface</span> <span class="nc">View</span>
<span class="kd">interface</span> <span class="nc">Presenter</span><span class="p"><</span><span class="nc">T</span><span class="p">:</span> <span class="nc">View</span><span class="p">></span> <span class="p">{</span>
<span class="nd">@CallSuper</span>
<span class="k">fun</span> <span class="nf">takeView</span><span class="p">(</span><span class="n">view</span><span class="p">:</span> <span class="nc">T</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="n">view</span> <span class="p">=</span> <span class="n">view</span>
<span class="p">}</span>
<span class="nd">@CallSuper</span>
<span class="k">fun</span> <span class="nf">releaseView</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="n">view</span> <span class="p">=</span> <span class="k">null</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="py">view</span><span class="p">:</span> <span class="nc">T</span><span class="p">?</span> <span class="p">=</span> <span class="k">null</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">interface</span> <span class="nc">CoroutineContract</span> <span class="p">{</span>
<span class="kd">interface</span> <span class="nc">View</span> <span class="p">:</span> <span class="nc">BaseContract</span><span class="p">.</span><span class="nc">View</span><span class="p">,</span> <span class="nc">CoroutineScope</span> <span class="p">{</span>
<span class="k">private</span> <span class="kd">val</span> <span class="py">job</span> <span class="p">=</span> <span class="nc">Job</span><span class="p">()</span>
<span class="k">override</span> <span class="kd">val</span> <span class="py">coroutineContext</span><span class="p">:</span> <span class="nc">CoroutineContext</span> <span class="p">=</span> <span class="n">job</span> <span class="p">+</span> <span class="nc">Dispatchers</span><span class="p">.</span><span class="nc">Main</span>
<span class="p">}</span>
<span class="k">abstract</span> <span class="kd">class</span> <span class="nc">Presenter</span><span class="p"><</span><span class="nc">T</span><span class="p">:</span> <span class="nc">View</span><span class="p">></span> <span class="p">:</span> <span class="nc">BaseContract</span><span class="p">.</span><span class="nc">Presenter</span><span class="p"><</span><span class="nc">T</span><span class="p">>,</span> <span class="nc">CoroutineScope</span> <span class="p">{</span>
<span class="k">private</span> <span class="kd">val</span> <span class="py">job</span> <span class="p">=</span> <span class="nc">Job</span><span class="p">()</span>
<span class="k">override</span> <span class="kd">val</span> <span class="py">coroutineContext</span><span class="p">:</span> <span class="nc">CoroutineContext</span> <span class="p">=</span> <span class="n">job</span> <span class="p">+</span> <span class="nc">Dispatchers</span><span class="p">.</span><span class="nc">IO</span>
<span class="k">override</span> <span class="k">fun</span> <span class="nf">releaseView</span><span class="p">()</span> <span class="p">{</span>
<span class="n">job</span><span class="p">.</span><span class="nf">cancel</span><span class="p">()</span>
<span class="k">super</span><span class="p">.</span><span class="nf">releaseView</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>View methods will be called from the presenter. They should be modified with <code class="language-plaintext highlighter-rouge">suspend</code> and, importantly, they are responsible for changing the coroutine execution context back to the main thread via <code class="language-plaintext highlighter-rouge">withContext(this.coroutineContext)</code> where appropriate.</p>
<p>Presenter methods return are also <code class="language-plaintext highlighter-rouge">suspend</code> methods. Since presenters are coroutine scopes, you can easily use a coroutine builder on the presenter like <code class="language-plaintext highlighter-rouge">launch</code> to call the suspend methods on the presenter.</p>
<p><img src="https://files.logoscdn.com/v1/files/17840272/content.jpg?signature=Vsy0SeUqSHff1KP10zn2AvviiBc" alt="Example MVP Interaction" /></p>
<h4 id="testing">Testing</h4>
<p>Isolating and testing presenters on the JVM is easy if a few rules are followed:</p>
<h5 id="rules-for-presenters">Rules for Presenters</h5>
<ol>
<li>Avoid direct references to the Android framework. If necessary, they reference framework components through the view interface. This is antithetical to the <a href="https://martinfowler.com/eaaDev/PassiveScreen.html">passive view</a> philosophy, but the trade-off is worth sealing the Android framework in a box for testing purposes.</li>
<li>Presenter methods that are called by the view are suspending functions. Those functions should always be called within the presenter’s coroutine scope.</li>
<li><code class="language-plaintext highlighter-rouge">takeView</code> is the cue for the presenter that ui is ready for information. <code class="language-plaintext highlighter-rouge">releaseView</code> is the cue for the presenter that the ui is being torn down. Hard dependencies on the MVP view’s lifecycle are discouraged and should use the contract interfaces to communicate if necessary.</li>
</ol>
<p><a href="https://medium.com/androiddevelopers/write-once-run-everywhere-tests-on-android-88adb2ba20c5?linkId=59193135">Android Test</a> is out now and provides hope for a future where testing on the JVM <em>can</em> involve the Android framework. Fingers crossed.</p>
<h3 id="the-future">The Future</h3>
<p>We aspire to a more reactive variant of the MVP pattern. The good news is that we have several paths to making our reactive aspirations a reality.</p>
<p><img src="https://files.logoscdn.com/v1/files/17622407/content.png?signature=D1vJSbfnnNWXMJr_BvPNPEv46ZA" alt="Future Architecture" /></p>
<blockquote>
<p>A possible future using Room for a local data store that serves as an authority on data presented to the user. Maybe Room will support coroutine channels by the time we take this on. Maybe we’ll use LiveData. Who knows?</p>
</blockquote>
<p>Dagger 2, Retrofit, Lifecycle, & Coroutines make the foundation of our stack, but we’re always evaluating technologies that can help us write better code faster.</p>
<h3 id="extras">Extras</h3>
<ul>
<li><a href="https://youtu.be/_hfBv0a09Jc">Introduction to Coroutines</a> - a helpful introduction to reasoning about coroutine execution (warning: the coroutines api has changed a bit since talk was given)</li>
<li><a href="https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md">Kotlin Coroutines Guide</a> - the definitive guide, maintained by JetBrains</li>
<li><a href="https://medium.com/@elizarov/structured-concurrency-722d765aa952">Structured Concurrency</a> - an overview of the changes to the coroutines API in version 0.26.0</li>
<li><a href="https://youtu.be/P7ov_r1JZ1g">Android Suspenders</a> - a talk outlining best practices for using coroutines on Android</li>
</ul>
<p>We like Kotlin a lot. As it currently stands, the app is exclusively written in the language.
If you’re an Android developer and find this interesting, <a href="https://faithlife.com/jobs/AndroidSoftwareDeveloper">we’re hiring</a>!</p>
<p>We have some exciting things planned for the <a href="https://play.google.com/store/apps/details?id=com.faithlife.mobile">Faithlife App</a>.</p>
<p><br />
<em>Thanks to Logan Ash and Jacob Peterson for reviewing early versions of this post.</em></p>
<p><strong>Update 01/19/2019</strong></p>
<p>An earlier version of this post suggested that presenter methods should return an instance of <code class="language-plaintext highlighter-rouge">Job</code> so that tests can synchronize on the called coroutine in order to prevent race conditions. We ran into some problems with the way exceptions of child coroutines within a <code class="language-plaintext highlighter-rouge">runBlocking</code> coroutine builder are handled. <code class="language-plaintext highlighter-rouge">runBlocking</code> <a href="https://kotlinlang.org/docs/reference/coroutines/exception-handling.html#cancellation-and-exceptions">immediately cancels child coroutines when they thrown an exception</a>. After some consideration, we determined that the presenter should be responsible for exactly <em>how</em> the methods are called, just with the fact that they are suspending.</p>
<p>The structured concurrency updates and the <code class="language-plaintext highlighter-rouge">CoroutineScope</code> interface helped us achieve this separation. We can start presenter coroutines in the presenter’s scope from the view and in tests we wrap the entire test in a <code class="language-plaintext highlighter-rouge">runBlocking</code> coroutine builder so that suspend methods are executed synchronously.</p>
<p>We’re happier with the new system in it’s flexibility to treat suspending functions differently depending on the context in which they are called.</p>
Justin BrooksMaking renders faster with the React 16.5 profiler2018-09-21T08:00:00+00:00http://faithlife.codes/blog/2018/09/react-profiler/<p>React 16.5 recently shipped, which added support for some new Profiling tools. We recently used these tools to identify a major source of slow render performance.</p>
<p>Faithlife.com is a web application powered by React 16.3. The homepage consists of a reverse-chronological timeline of posts. We received some reports that interactions with posts (such as replying) caused the browser to lag, depending on how far down the post was on the page. The further down the page the post was, the more lag occurred.</p>
<p>After updating React to 16.5 on a local copy of Faithlife, our next step was to start profiling and capture what components were re-rendering. Below is a screenshot of what the tools showed us clicking the ‘Like’ button on any post:</p>
<p><img src="https://files.logoscdn.com/v1/files/14522904/content.png?signature=NmNdRI26PmGCtI8F6Z-3-o99Le4" alt="Slow renders screenshot" /></p>
<p>The blue blocks below NewsFeed show render being called on all the posts in the feed. If there were 10 items loaded, <code class="language-plaintext highlighter-rouge">NewsFeedItem</code> and all its children would get rendered 10 times. This can be fine for small components, but if the render tree is deep, rendering a component and its children unnecessarily can cause performance problems. As a user scrolls down on the page, more posts get loaded in the feed. This causes render to get called for posts all the way at the top, even though they haven’t changed!</p>
<p>This seemed like a good time to try changing <code class="language-plaintext highlighter-rouge">NewsFeedItem</code> to extend <code class="language-plaintext highlighter-rouge">PureComponent</code>, which will skip re-rendering the component and its children if the props have not changed (a shallow comparison is used for this check).</p>
<p>Unfortunately applying PureComponent was not enough - profiling again showed that unnecessary component renders were still happening. We then uncovered two issues preventing us from leveraging PureComponent’s optimizations:</p>
<h2 id="first-roadblock-use-of-children-props">First roadblock: Use of children props.</h2>
<p>We had a component that looked something like this:</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p"><</span><span class="nc">NewsFeedItem</span> <span class="na">contents</span><span class="p">=</span><span class="si">{</span><span class="nx">item</span><span class="p">.</span><span class="nx">contents</span><span class="si">}</span><span class="p">></span>
<span class="p"><</span><span class="nc">VisibilitySensor</span> <span class="na">itemId</span><span class="p">=</span><span class="si">{</span><span class="nx">item</span><span class="p">.</span><span class="nx">id</span><span class="si">}</span> <span class="na">onChange</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">handleVisibilityChange</span><span class="si">}</span> <span class="p">/></span>
<span class="p"></</span><span class="nc">NewsFeedItem</span><span class="p">></span>
</code></pre></div></div>
<p>This compiles down to:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">React</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span>
<span class="nx">NewsFeedItem</span><span class="p">,</span>
<span class="p">{</span> <span class="na">contents</span><span class="p">:</span> <span class="nx">item</span><span class="p">.</span><span class="nx">contents</span> <span class="p">},</span>
<span class="nx">React</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="nx">VisibilitySensor</span><span class="p">,</span> <span class="p">{</span> <span class="na">itemId</span><span class="p">:</span> <span class="nx">item</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span> <span class="na">onChange</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">handleVisibilityChange</span> <span class="p">})</span>
<span class="p">);</span>
</code></pre></div></div>
<p>Because React creates a new instance of <code class="language-plaintext highlighter-rouge">VisibilitySensor</code> during each render, the <code class="language-plaintext highlighter-rouge">children</code> prop always changes, so making <code class="language-plaintext highlighter-rouge">NewsFeedItem</code> a <code class="language-plaintext highlighter-rouge">PureComponent</code> would make things <em>worse</em>, since a shallow comparison in <code class="language-plaintext highlighter-rouge">shouldComponentUpdate</code> may not be cheap to run and will always return true.</p>
<p>Our solution here was to move VisibilitySensor into a render prop and use a bound function:</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p"><</span><span class="nc">NewsFeedItemWithHandlers</span>
<span class="na">contents</span><span class="p">=</span><span class="si">{</span><span class="nx">item</span><span class="p">.</span><span class="nx">contents</span><span class="si">}</span>
<span class="na">itemId</span><span class="p">=</span><span class="si">{</span><span class="nx">item</span><span class="p">.</span><span class="nx">id</span><span class="si">}</span>
<span class="na">handleVisibilityChange</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">handleVisibilityChange</span><span class="si">}</span>
<span class="p">/></span>
<span class="kd">class</span> <span class="nx">NewsFeedItemWithHandlers</span> <span class="kd">extends</span> <span class="nx">PureComponent</span> <span class="p">{</span>
<span class="c1">// The arrow function needs to get created outside of render, or the shallow comparison will fail</span>
<span class="nx">renderVisibilitySensor</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">(</span>
<span class="p"><</span><span class="nc">VisibilitySensor</span>
<span class="na">itemId</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">itemId</span><span class="si">}</span>
<span class="na">onChange</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">handleVisibilityChange</span><span class="si">}</span>
<span class="p">/></span>
<span class="p">);</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="p"><</span><span class="nc">NewsFeedItem</span>
<span class="na">contents</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">contents</span><span class="si">}</span>
<span class="na">renderVisibilitySensor</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">renderVisibilitySensor</span><span class="si">}</span>
<span class="p">/>;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Because the bound function only gets created once, the same function instance will be passed as props to <code class="language-plaintext highlighter-rouge">NewsFeedItem</code>.</p>
<h2 id="second-roadblock-inline-object-created-during-render">Second roadblock: Inline object created during render</h2>
<p>We had some code that was creating a new instance of a url helper in each render:</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">getUrlHelper</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="k">new</span> <span class="nx">NewsFeedUrlHelper</span><span class="p">(</span>
<span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">moreItemsUrlTemplate</span><span class="p">,</span>
<span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">pollItemsUrlTemplate</span><span class="p">,</span>
<span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">updateItemsUrlTemplate</span><span class="p">,</span>
<span class="p">);</span>
<span class="p"><</span><span class="nc">NewsFeedItemWithHandlers</span>
<span class="na">contents</span><span class="p">=</span><span class="si">{</span><span class="nx">item</span><span class="p">.</span><span class="nx">contents</span><span class="si">}</span>
<span class="na">urlHelper</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">getUrlHelper</span><span class="p">()</span><span class="si">}</span> <span class="c1">// new object created with each method call</span>
<span class="p">/></span>
</code></pre></div></div>
<p>Since <code class="language-plaintext highlighter-rouge">getUrlHelper</code> is computed from props, there’s no point in creating more than one instance if we can cache the previous result and re-use that. We used <a href="https://github.com/alexreardon/memoize-one"><code class="language-plaintext highlighter-rouge">memoize-one</code></a> to solve this problem:</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">memoizeOne</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">memoize-one</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">memoizedUrlHelper</span> <span class="o">=</span> <span class="nx">memoizeOne</span><span class="p">(</span>
<span class="p">(</span><span class="nx">moreItemsUrlTemplate</span><span class="p">,</span> <span class="nx">pollItemsUrlTemplate</span><span class="p">,</span> <span class="nx">updateItemsUrlTemplate</span><span class="p">)</span> <span class="o">=></span>
<span class="k">new</span> <span class="nx">NewsFeedUrlHelper</span><span class="p">({</span>
<span class="nx">moreItemsUrlTemplate</span><span class="p">,</span>
<span class="nx">pollItemsUrlTemplate</span><span class="p">,</span>
<span class="nx">updateItemsUrlTemplate</span><span class="p">,</span>
<span class="p">}),</span>
<span class="p">);</span>
<span class="c1">// in the component</span>
<span class="nx">getUrlHelper</span> <span class="o">=</span> <span class="nx">memoizedUrlHelper</span><span class="p">(</span>
<span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">moreItemsUrlTemplate</span><span class="p">,</span>
<span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">pollItemsUrlTemplate</span><span class="p">,</span>
<span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">updateItemsUrlTemplate</span>
<span class="p">);</span>
</code></pre></div></div>
<p>Now we will create a new url helper only when the dependent props change.</p>
<h2 id="measuring-the-difference">Measuring the difference</h2>
<p>The profiler now shows much better results: rendering NewsFeed is now down from ~50ms to ~5ms!</p>
<p><img src="https://files.logoscdn.com/v1/files/14522909/content.png?signature=FzQeT-m0XOUSU2hSJ7x2cYMqNlo" alt="Better renders screenshot" /></p>
<h2 id="purecomponent-may-make-your-performance-worse">PureComponent may make your performance worse</h2>
<p>As with any performance optimization, it’s critical to measure the how changes impact performance.</p>
<p><code class="language-plaintext highlighter-rouge">PureComponent</code> is not an optimization that can blindly be applied to all components in your application. It’s good for components in a list with deep render trees, which was the case in this example. If you’re using arrow functions as props, inline objects, or inline arrays as props with a <code class="language-plaintext highlighter-rouge">PureComponent</code>, both <code class="language-plaintext highlighter-rouge">shouldComponentUpdate</code> <em>and</em> <code class="language-plaintext highlighter-rouge">render</code> will always get called, because new instances of those props will get created each time! Measure the performance of your changes to be sure they are an improvement.</p>
<p>It may be perfectly fine for your team to use inline arrow functions on simple components, such as binding onClick handlers on <code class="language-plaintext highlighter-rouge">button</code> elements inside a loop. Prioritize readability of your code first, then measure and add performance optimizations where it makes sense.</p>
<h2 id="bonus-experiment">Bonus experiment</h2>
<p>Since the pattern of creating components just to bind callbacks to props is pretty common in our codebase, we wrote a helper for generating components with pre-bound functions. Check it out <a href="https://github.com/Faithlife/react-util/tree/dcdeed8e2b24562766150661aaae8cfcd07c2de8/src/withEventHandlers">on our Github repo</a>.</p>
<p>You can also use windowing libraries, such as <a href="https://github.com/bvaughn/react-virtualized">react-virtualized</a> to avoid rendering components that aren’t in view.</p>
<p>Thanks to Ian Mundy, Patrick Nausha, and Auresa Nyctea for providing feedback on early drafts of this post.</p>
Dustin MastersConverting BibleWorks Hebrew2018-08-15T08:00:00+00:00http://faithlife.codes/blog/2018/08/converting-bibleworks-hebrew/<p>We <a href="https://community.logos.com/forums/t/169913.aspx">recently announced</a>
that we’ll support importing BibleWorks notes into Logos 7. This is mostly
a matter of converting RTF into our internal format, a fairly well-understood
process.</p>
<p>The one wrinkle is supporting <a href="https://www.bibleworks.com/fonts.html">BibleWorks Greek and Hebrew fonts</a>.
BibleWorks didn’t support Unicode for many years; instead it used the Bwhebl
and Bwgrkn 8-bit fonts to simulate Greek and Hebrew characters.</p>
<p>In Unicode, b and β and ב are all separate characters (that in some cases are
all supported by a single font). With 8-bit fonts, one uses Latin characters
(a, b, c) but changes the font so that “b” looks like β or ב.
Behind-the-scenes, κύριος is stored as ku,rioj and אֲדֹנָ֣י as yn’ådoa}. This
makes text processing more difficult as you can no longer perform a search
for Greek or Hebrew using the Unicode versions of those characters. It also
means the user must have these specific fonts installed and can’t change
their preferred Greek or Hebrew font. For a good customer experience, we
needed to convert the characters for users who had BibleWorks notes predating
Unicode support.</p>
<p>The Greek was relatively straightforward, but Hebrew presented a bigger
challenge. Not only is BibleWorks Hebrew stored using Latin characters, it’s
also stored in display (i.e., left-to-right) order. In Unicode, characters
are stored in logical order (which is right-to-left for fragments of Hebrew
text); the display system will lay them out correctly. The string needs to
be reversed, but with a catch: in both BibleWorks Hebrew and in Unicode,
Hebrew vowels and accents are entered after the character that they’re
positioned on top of. We can’t naively reverse the entire string; we have
to reverse it one grapheme cluster at a time.</p>
<p>Moreover, Unicode has a concept of <a href="http://unicode.org/reports/tr9/#Mirroring">bidirectional mirroring</a>
in which “neutral” characters are replaced by their mirrored versions in a
RTL run; for example ( will be displayed as ) in right-to-left text. When
reversing the string, these characters need to be replaced by their mirrored
version.</p>
<p>Finally, <a href="https://www.bibleworks.com/bw9help/bwh24_Keyboards.htm#BWHebAccentCharacterCodes">the documentation</a>
we found gave BibleWorks Hebrew characters as decimal numbers representing
entries in an 8-bit font; due to the way we were reading the RTF source of BW
Notes, these bytes had already gone through a Windows-1252 to Unicode
conversion, so our character map had to be based off the Unicode characters
that corresponded to Windows 1252 bytes.</p>
<table>
<thead>
<tr>
<th>Step</th>
<th> </th>
<th> </th>
<th> </th>
<th> </th>
<th> </th>
<th> </th>
<th> </th>
<th> </th>
<th> </th>
<th> </th>
</tr>
</thead>
<tbody>
<tr>
<td>Initial input</td>
<td>191</td>
<td>121</td>
<td>110</td>
<td>39</td>
<td>229</td>
<td>100</td>
<td>111</td>
<td>97</td>
<td>125</td>
<td>192</td>
</tr>
<tr>
<td>Decode Windows-1252 to Unicode</td>
<td>¿</td>
<td>y</td>
<td>n</td>
<td>’</td>
<td>å</td>
<td>d</td>
<td>o</td>
<td>a</td>
<td>}</td>
<td>À</td>
</tr>
<tr>
<td>Untransliterate</td>
<td>(</td>
<td>י</td>
<td>נ</td>
<td>ָ</td>
<td>֣</td>
<td>ד</td>
<td>ֹ</td>
<td>א</td>
<td>ֲ</td>
<td>)</td>
</tr>
<tr>
<td>Reverse grapheme clusters</td>
<td>)</td>
<td>א</td>
<td>ֲ</td>
<td>ד</td>
<td>ֹ</td>
<td>נ</td>
<td>ָ</td>
<td>֣</td>
<td>י</td>
<td>(</td>
</tr>
<tr>
<td>Flip punctuation</td>
<td>(</td>
<td>א</td>
<td>ֲ</td>
<td>ד</td>
<td>ֹ</td>
<td>נ</td>
<td>ָ</td>
<td>֣</td>
<td>י</td>
<td>)</td>
</tr>
</tbody>
</table>
<p>The final result: (אֲדֹנָ֣י)</p>
<p>Our complete BibleWorks Hebrew mapping table is <a href="https://gist.github.com/bgrainger/391c98fb1a75a2be002ea9216c68cf67#file-bibleworkshebrew-md">available here</a>.</p>
Bradley GraingerFaster API calls with JWT access tokens2018-08-06T08:00:00+00:00http://faithlife.codes/blog/2018/08/using-oauth2-tokens-for-faster-api-calls/<p>At Faithlife, we’ve been using OAuth 1.0a to handle authentication between services. Instead of designing our apps as monoliths, we’ve been perferring to build lightweight frontend applications that call RESTful microservices, returning entities as JSON. These frontend applications don’t touch our databases directly. Among other benefits, this allows us to better allocate hardware resources (CPU, RAM, disk) to applications that need them.</p>
<p>A typical request to Faithlife might look something like this:</p>
<p><img src="https://files.logoscdn.com/v1/files/11324789/content.png?signature=NsghsFbYY2pML81LZQlS4bKt7wo" alt="mermaid
sequenceDiagram
participant Frontend
participant Accounts
participant Community Newsfeed
participant Amber API
participant Notifications API
participant OAuth
Frontend->> OAuth: Authenticate user
Frontend->> Accounts: List groups
Frontend->> Community Newsfeed: Fetch newsfeed
Community Newsfeed->> OAuth: Authenticate user
Community Newsfeed->> Amber API: Get post images
Amber API->> OAuth: Authenticate user
Frontend->> Notifications API: Get notifications
Notifications API->> OAuth: Authenticate user
" /></p>
<p>At the beginning of the request, Faithlife makes a call to OAuth API to ensure the current OAuth access token and secret are still valid. After that check passes, the current user’s OAuth credentials are also passed to all downstream services that require auth.</p>
<p>An authorization header presented to a downstream API looks something like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Authorization: OAuth oauth_consumer_key="1E18E56BD0C3A51A945D98136D6462FCEAE65199",oauth_signature="0B847E32C6DE692A7BA899DF67EF5C1BCCAEFA89%262D3F6B2BD18B2DD85821EFF0F07EB130AD46E5C5",oauth_signature_method="PLAINTEXT",oauth_version="1.0",oauth_token="FE009074810F3D2E3A2EB6BF5603B1CA08082AB7"
</code></pre></div></div>
<p>However, this poses a problem - our microservices do not have access to the OAuth database directly, and can’t validate the current user’s authorization header without first calling OAuth API. These calls are not free - on average, we measured the time taking from 10-35 ms for apps within the same data center, depending on a variety of factors. As we add more API calls to Faithlife, it gets progressively worse:</p>
<ul>
<li>Pages take longer to load, as each API dependency needs to call OAuth API.</li>
<li>APIs may need to fetch data from other downstream APIs, and each API needs to validate the authenticated user.</li>
<li>APIs hosted outside the datacenter (Azure, GKE, etc) can magnify this problem significantly if the app is not hosted geographically close to where OAuth API lives.</li>
<li>Locally caching the oauth validation state on a web node only solves part of the problem. Many APIs are backed by multiple web nodes, with round robin request balancing, so there could be a cache miss.</li>
</ul>
<h2 id="jwt-access-tokens">JWT Access Tokens</h2>
<p>What we needed was a way to pass a token to downstream APIs that identifies the current user. OAuth 1.0a was suitable for a long time, and several years ago we decided to hold off on migrating to OAuth 2 because the need was not strong enough. OAuth 2 adds a few steps to the authentication flow:</p>
<ol>
<li>When signing a user in, obtain a refresh token and an access token.</li>
<li>Use the access token for API calls. The OAuth 2 and OpenID Connect standards do not define the format that these access tokens have to be in, but OpenID Connect mandates JSON web tokens (JWTs) for identity tokens, and identity tokens can be used as access tokens. To future proof our implementation, we chose to use JWTs signed with ES256. <a href="https://developer.okta.com/blog/2017/07/25/oidc-primer-part-1">This blog post explains the token differences in greater detail.</a>.</li>
<li>When the access token expires, use the refresh token to obtain a new access token</li>
</ol>
<p>JWT access tokens have a few desirable properties for our use case. Tokens contain claims about the current user (such as the user ID and current roles), an expiration date, and are signed with a public/private key pair. Downstream APIs can validate their integrity using the public key, but only the signing authority can issue new ones. For our use case, we established some requirements for all JWT access tokens:</p>
<ul>
<li>Only OAuth API has access to the private key and is solely responsible for issuing JWT access tokens. The public key is available via a public API.</li>
<li>JWT access tokens can only be created from plaintext OAuth 1 access tokens. Creating a JWT access token from a previous JWT access token is not allowed.</li>
<li>JWT access tokens are signed with ES256. All other signatures must be rejected.</li>
<li>Expiration date is 10 minutes from the current time, and not valid before 10 minutes prior to current time.</li>
<li>JWT access tokens contain claims for the current user ID, frontend app consumer ID, and any other properties that would be normally obtained when validating the current user via OAuth API.</li>
</ul>
<p>JWT access tokens are presented to the downstream APIs that Faithlife calls. On the very first request, the current public key is requested from OAuth API. Using this public key, tokens can now be validated locally. All future tokens for the lifetime of the app are validated with this public key. Because the token has claims stored within it, we now have no need to call OAuth API for successful authentication attempts. If the token can’t be validated locally, either because the token appears to be expired due to clock skew, or because the signing key was changed, the downstream API makes a validation call to OAuth API (just like it did before).</p>
<p>We did not end up implementing the full OAuth 2 authorization flow when adding support for JWT access tokens. Instead, we used the OAuth 1 credentials in place of OAuth 2 refresh tokens to obtain access tokens.</p>
<h2 id="how-is-this-approach-different-from-oauth-1">How is this approach different from OAuth 1?</h2>
<p>This approach follows the full OAuth 1.0a authorization flow, but replaces OAuth 1 plaintext tokens with JWT access tokens when communicating with downstream APIs. An OAuth 1 plaintext token is still obtained and stored by the frontend web application (in an encrypted cookie), and then upgraded to an JWT access token at the beginning of a frontend request. We could have migrated our auth services to a full OAuth 2 implementation, but this would be a non-trivial amount of work, and was more than we wanted to take on in this iteration. We were mainly interested in the scalability wins of using JWT access tokens, and leaving open the future possibility of using the full OAuth 2 authorization flow in the future.</p>
<h2 id="tokens-in-action">Tokens in action</h2>
<p>When Faithlife gets a web request, it makes a call to OAuth API:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GET /oauth/v1/users/current
Authorization: OAuth oauth_consumer_key="1E18E56BD0C3A51A945D98136D6462FCEAE65199",oauth_signature="0B847E32C6DE692A7BA899DF67EF5C1BCCAEFA89%262D3F6B2BD18B2DD85821EFF0F07EB130AD46E5C5",oauth_signature_method="PLAINTEXT",oauth_version="1.0",oauth_token="FE009074810F3D2E3A2EB6BF5603B1CA08082AB7"
</code></pre></div></div>
<p>And gets back a JWT:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>X-Bearer-Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyOTg2Njg5IiwiY29uc3VtZXJOYW1lIjoiRmFpdGhsaWZlIiwiY29uc3VtZXJUb2tlbiI6IjRFNTdGQTk1MDE1MTJDMUM0RjdFMzQ1NzE0NjNDMjI0QjBCMzc1NEQiLCJpc0FkbWluQ29uc3VtZXIiOiJ0cnVlIiwibmJmIjoxNTMyOTgyMDQ2LCJleHAiOjE1MzI5ODMyNDYsImlhdCI6MTUzMjk4MjY0NiwiaXNzIjoiYXV0aC5mYWl0aGxpZmUuY29tIiwiYXVkIjoiZmFpdGhsaWZlLWJhY2tlbmQtYXBpcyJ9.7GSdItnnCr8QOLS3uCbJMY0X-D7jTjp_XUAp8clo9LY4X5Zlf_5I7RSMZr3J6kOihwbHjEbuh0AFMXmF5YQZLg
</code></pre></div></div>
<p>Which decodes to:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"sub"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2986689"</span><span class="p">,</span><span class="w">
</span><span class="nl">"consumerName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Faithlife"</span><span class="p">,</span><span class="w">
</span><span class="nl">"consumerToken"</span><span class="p">:</span><span class="w"> </span><span class="s2">"4E57FA9501512C1C4F7E34571463C224B0B3754D"</span><span class="p">,</span><span class="w">
</span><span class="nl">"isAdminConsumer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"true"</span><span class="p">,</span><span class="w">
</span><span class="nl">"nbf"</span><span class="p">:</span><span class="w"> </span><span class="mi">1532982046</span><span class="p">,</span><span class="w">
</span><span class="nl">"exp"</span><span class="p">:</span><span class="w"> </span><span class="mi">1532983246</span><span class="p">,</span><span class="w">
</span><span class="nl">"iat"</span><span class="p">:</span><span class="w"> </span><span class="mi">1532982646</span><span class="p">,</span><span class="w">
</span><span class="nl">"iss"</span><span class="p">:</span><span class="w"> </span><span class="s2">"auth.faithlife.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"aud"</span><span class="p">:</span><span class="w"> </span><span class="s2">"faithlife-backend-apis"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>This JWT is passed to all downstream services in the <code class="language-plaintext highlighter-rouge">Authorization</code> header. Our request graph now looks much better:</p>
<p><img src="https://files.logoscdn.com/v1/files/11324795/content.png?signature=7-Y6TmsR44C2y4ESXq0ayDcGcBA" alt="mermaid
sequenceDiagram
participant Frontend
participant Accounts
participant Community Newsfeed
participant Amber API
participant Notifications API
participant OAuth
Frontend->> OAuth: Authenticate user and obtain JWT
Frontend->> Accounts: List groups
Frontend->> Community Newsfeed: Fetch newsfeed
Community Newsfeed->> Amber API: Get post images
Frontend->> Notifications API: Get notifications
" /></p>
<p>On average we measured validating this token taking less than 4 ms per request. We’re happy with the results so far and are in the process of rolling support out to all of our APIs.</p>
<h2 id="alternate-strategies">Alternate strategies</h2>
<p>There is more than one way to solve this problem. A few other strategies we considered:</p>
<ul>
<li>
<p>Using a shared service account to communicate with downstream services. This increases the chance that a frontend regression reveals access to data that the user is not allowed to see (e.g. posts to a secret group). There are many different teams in charge of the APIs that Faithlife calls (e.g. commerce APIs are separated from community APIs), and validation at the API layer is much easier to enforce across team boundaries.</p>
</li>
<li>
<p>Using a shared key and pass a signed token or cookie. This presents several security problems. Rotating the shared token would have been very complicated, and having the signing key on all of our web nodes increases the attack surface. We wanted a solution that was standardized and would scale well into the future. Some of these solutions are also tighly coupled to the web application stack (ASPXAUTH cookies for example), and our auth solution needs to work on multiple API platforms.</p>
</li>
</ul>
<h2 id="technologies-used">Technologies used</h2>
<p>We primarily use ASP.NET here at Faithlife, although we also host services using NodeJS and .NET Core. A sample .NET Core app that demonstrates both signing and validating ES256 tokens is hosted here:</p>
<p>https://github.com/Faithlife/ES256-Demo</p>
<p>This demo uses these NuGet packages:</p>
<ul>
<li><a href="https://www.nuget.org/packages/Portable.BouncyCastle/">Portable.Bouncycastle</a></li>
<li><a href="https://www.nuget.org/packages/System.IdentityModel.Tokens.Jwt/">System.IdentityModel.Tokens.Jwt</a></li>
</ul>
<h2 id="thanks-for-reading">Thanks for reading!</h2>
<p>If you’re intersted in working on projects like this, come <a href="https://faithlife.com/careers">work with us!</a>
Thanks to Robert Bolender, <a href="/blog/authors/justin-brooks/">Justin Brooks</a>, and <a href="/blog/authors/bradley-grainger/">Bradley Grainger</a> for giving feedback on early drafts of this post.</p>
Dustin Masters