<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Adam Reeve</title><link href="https://adamreeve.co.nz/" rel="alternate"></link><link href="https://adamreeve.co.nz/feeds/all.atom.xml" rel="self"></link><id>https://adamreeve.co.nz/</id><updated>2015-09-14T00:00:00+12:00</updated><entry><title>Continuous deployment of Python packages with Wercker</title><link href="https://adamreeve.co.nz/blog/pypi-cd-wercker.html" rel="alternate"></link><published>2015-09-14T00:00:00+12:00</published><updated>2015-09-14T00:00:00+12:00</updated><author><name>Adam Reeve</name></author><id>tag:adamreeve.co.nz,2015-09-14:/blog/pypi-cd-wercker.html</id><summary type="html">&lt;p class="first last"&gt;How to set up continuous deployment of a Python package to PyPI using Wercker.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Continuous delivery isn't just for web applications,
it's valuable to be able to quickly get feedback from real users
for any kind of software project.
Also, I'm lazy, so automating the release process for any software to the point that I
only have to merge a branch into master is great.&lt;/p&gt;
&lt;p&gt;I recently set up continuous deployment of my
&lt;a class="reference external" href="https://github.com/adamreeve/npTDMS"&gt;npTDMS&lt;/a&gt; Python package
to PyPI (the Python package index) using Wercker.
Wercker is a continuous integration and deployment solution built on Docker.
You build your code inside a Docker container and can specify a Docker image
to use that contains the build dependencies for your project.
This Docker image can come from any public or private registry, so you
can use your own Docker image and have complete control over the build environment.
I created a Python image containing everything needed to build and test
my library and hosted it on &lt;a class="reference external" href="https://hub.docker.com/r/adreeve/python-numpy/"&gt;Docker Hub&lt;/a&gt;.
The Dockerfile looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ubuntu:14.04&lt;/span&gt;

&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sudo apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        sudo apt-get install -y &lt;span class="se"&gt;\&lt;/span&gt;
            python3 python3-numpy python3-setuptools python3-nose python3-pandas &lt;span class="se"&gt;\&lt;/span&gt;
            python python-numpy python-setuptools python-nose python-pandas &lt;span class="se"&gt;\&lt;/span&gt;
            pep8 python-pip python-wheel
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This Dockerfile lives in a git repository on &lt;a class="reference external" href="https://github.com/adamreeve/python-numpy-docker"&gt;GitHub&lt;/a&gt;.
Any commits to this repository trigger a new Docker image build using Docker Hub's auto-build feature.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;wercker.yml&lt;/code&gt; I set the Docker image to use, pointing it at my
image on Docker Hub:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;box&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;adreeve/python-numpy&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The test section in my &lt;code&gt;wercker.yml&lt;/code&gt; defines what to run to build and test my code,
and looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;PEP8 Check&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="no"&gt;pep8 ./nptdms&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Test on Python 2.7&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;python2.7 setup.py install&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;nosetests&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Test on Python 3&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;python3 setup.py install&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;nosetests3&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This tells Wercker to run pep8 to check code style, then run tests on Python 2.7 and Python 3 using nose.&lt;/p&gt;
&lt;p&gt;Wercker lets you define deployment steps and you can optionally enable
automatic deployment from specific branches after successful builds.
The deployment step in my &lt;code&gt;wercker.yml&lt;/code&gt; file is pretty simple, it looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Deploy to PyPI&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;echo &amp;quot;[pypirc]&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;servers = pypi&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;[server-login]&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;username:$PYPI_USER&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;password:$PYPI_PASSWORD&amp;quot; &amp;gt; ~/.pypirc&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;python setup.py sdist upload&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="no"&gt;python setup.py bdist_wheel upload&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This simply configures my PyPI credentials then runs the commands to upload a new release
as a source package and a Python wheel.
The &lt;code&gt;PYPI_USER&lt;/code&gt; and &lt;code&gt;PYPI_PASSWORD&lt;/code&gt; variables are configured in the Wercker UI and are kept secret.
Now after any successful build on master, Wercker will automatically deploy a new release to PyPI!&lt;/p&gt;
&lt;p&gt;One thing to note with this approach is that PyPI will reject an upload of a new release
if the version number has not changed, so if you enable automatic deployment from your master branch,
you should ensure any pull request into that branch increments the version number.
Remember to follow &lt;a class="reference external" href="http://semver.org/"&gt;semantic versioning&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;The complete &lt;code&gt;wercker.yml&lt;/code&gt; file for npTDMS can be found in the
&lt;a class="reference external" href="https://github.com/adamreeve/npTDMS/blob/9b3d46cf22d6d3a059607b4ead2813a6a2821f99/wercker.yml"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
</content><category term="misc"></category><category term="Python"></category><category term="Docker"></category></entry><entry><title>A CRDT for a to-do list</title><link href="https://adamreeve.co.nz/blog/todo-crdt.html" rel="alternate"></link><published>2015-03-07T00:00:00+13:00</published><updated>2015-03-07T00:00:00+13:00</updated><author><name>Adam Reeve</name></author><id>tag:adamreeve.co.nz,2015-03-07:/blog/todo-crdt.html</id><summary type="html">&lt;p class="first last"&gt;This post looks at how a conflict-free replicated data
type can be used to describe a to-do list.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Many web applications need to deal with multiple users editing the
same document concurrently.
Even if only one user can edit a document, they might work from both
their phone and laptop, and want to be able to work when they don't have
a internet connection.
If they make some changes on their phone when they don't have an internet
connection, then make some other changes on their laptop,
the web application needs to be able to handle merging these two sets
of changes.&lt;/p&gt;
&lt;p&gt;One approach to allowing concurrent editing of a document is to
describe the document using a conflict-free replicated data type (CRDT).
As the name suggests, these data types are structured so that conflicting changes
are impossible.
In this blog post, we'll look at how a CRDT can be used to describe a
to-do list.&lt;/p&gt;
&lt;p&gt;A CRDT is based on the idea of creating a data structure where
all operations are idempotent and commutative.
An idempotent operation is one that can be applied multiple times and have
the same effect as if it was applied only once.
A commutative operation is one in which the order of operations doesn't matter.
The most simple CRDT is a grow-only set (G-Set), in which only element additions
are allowed.
Addition of an element to a set is idempotent, as adding the same element again
has no effect if the element already exists in the set.
Addition of an element to a set is also commutative, as it doesn't matter
what order elements are added, the end-result will always be the same.&lt;/p&gt;
&lt;p&gt;It is easy to see how a G-Set can handle concurrent operations.
If each machine in a network has their own copy of the G-Set, they
can apply operations locally, adding elements to the set,
and asynchronously send updates to
other machines in the network
when a network connection is available.&lt;/p&gt;
&lt;p&gt;From the point of view of the CAP theorem, a CRDT provides high availability
and strong network partition tolerance, while only providing eventual consistency.
This means that a document may be different across machines in a network, but
as long as all machines eventually form connections with each other, the
document will eventually become consistent.&lt;/p&gt;
&lt;div class="section" id="a-basic-to-do-list"&gt;
&lt;h2&gt;A basic to-do list&lt;/h2&gt;
&lt;p&gt;As a starting point for representing a to-do list, we will use a G-Set.
Each item in our to-do list will have a unique identifier and a text
field containing the item's title.
An example JSON representation of this to-do list structure is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;items&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2b74e20b-18d5-461d-9763-351ec3356b59&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Buy milk&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fccb6547-f3aa-4f8f-97e1-3c1e5dbf85f9&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Mow the lawns&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;449beefe-f190-4fad-bf92-77ac7c1775eb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Iron shirts&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fb212751-a86d-4d41-b912-db3ec6ecdbac&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Wash the car&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And the resulting list is:&lt;/p&gt;
&lt;ul class="task-list" style="list-style-type: none; margin-left: 1em; padding-left: 0;"&gt;
    &lt;li class="task-list-item"&gt;
        &lt;input type="checkbox" class="task-list-item-checkbox" disabled&gt;
        Buy milk
    &lt;/li&gt;
    &lt;li class="task-list-item"&gt;
        &lt;input type="checkbox" class="task-list-item-checkbox" disabled&gt;
        Mow the lawns
    &lt;/li&gt;
    &lt;li class="task-list-item"&gt;
        &lt;input type="checkbox" class="task-list-item-checkbox" disabled&gt;
        Iron shirts
    &lt;/li&gt;
    &lt;li class="task-list-item"&gt;
        &lt;input type="checkbox" class="task-list-item-checkbox" disabled&gt;
        Wash the car
    &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Adding an item to the list will only have an effect if an item with
a matching &lt;code&gt;id&lt;/code&gt; is not already present.
Different users can concurrently add items to the list, and they will all
be merged into one consistent representation eventually.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="marking-items-as-done-and-removing-items"&gt;
&lt;h2&gt;Marking items as done and removing items&lt;/h2&gt;
&lt;p&gt;Now that we have a to-do list that users can add items too, they might also want
to mark an item as done or remove it from the list.&lt;/p&gt;
&lt;p&gt;This can be achieved by maintaining separate sets of done and removed items.
An item is present in the to-do list only if is present in the &lt;code&gt;items&lt;/code&gt; set
and is not present in the &lt;code&gt;removed&lt;/code&gt; set.
If it is present in the &lt;code&gt;done&lt;/code&gt; set, then it is marked as done.
When an item is removed, the original item remains in the &lt;code&gt;items&lt;/code&gt; set
and is known as a &amp;quot;tombstone&amp;quot;.&lt;/p&gt;
&lt;p&gt;An example of this updated to-do list structure is shown below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;items&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2b74e20b-18d5-461d-9763-351ec3356b59&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Buy milk&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fccb6547-f3aa-4f8f-97e1-3c1e5dbf85f9&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Mow the lawns&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;449beefe-f190-4fad-bf92-77ac7c1775eb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Iron shirts&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fb212751-a86d-4d41-b912-db3ec6ecdbac&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Wash the car&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;removed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;449beefe-f190-4fad-bf92-77ac7c1775eb&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;done&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fccb6547-f3aa-4f8f-97e1-3c1e5dbf85f9&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;ul class="task-list" style="list-style-type: none; margin-left: 1em; padding-left: 0;"&gt;
    &lt;li class="task-list-item"&gt;
        &lt;input type="checkbox" class="task-list-item-checkbox" disabled&gt;
        Buy milk
    &lt;/li&gt;
    &lt;li class="task-list-item"&gt;
        &lt;input type="checkbox" class="task-list-item-checkbox" disabled checked&gt;
        Mow the lawns
    &lt;/li&gt;
    &lt;li class="task-list-item"&gt;
        &lt;input type="checkbox" class="task-list-item-checkbox" disabled&gt;
        Wash the car
    &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;In this example, we've mowed the lawns so checked that item, and
decided that our shirts don't need ironing after all, so
we've removed that from the list.&lt;/p&gt;
&lt;p&gt;In the simple case of only being able to remove items from the set, this
data structure is known as a two-phase or 2P-Set.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="modification"&gt;
&lt;h2&gt;Modification&lt;/h2&gt;
&lt;p&gt;Now we have a to-do list that users can add and remove items from, as well as mark
them as done, but what if they want to modify an existing item?
We could use a CRDT to represent the text within an item itself (for an example, see &lt;a class="reference external" href="https://hal.inria.fr/inria-00445975"&gt;TreeDoc&lt;/a&gt;)
or use other concurrency techniques such as &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Operational_transformation"&gt;operational transformation&lt;/a&gt; (OT).
However, a simpler approach is to treat modification of an item as
a deletion of the existing item and creation of a new item.
For our particular case of a to-do list, this approach seems like it should work well.
If two users concurrently edit the same item, the resulting to-do list will no longer
contain the original item and will have two separate, new items.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="unchecking"&gt;
&lt;h2&gt;Unchecking&lt;/h2&gt;
&lt;p&gt;We may also want to allow users to uncheck items that were previously marked as done.
The current data structure does not allow this, as items can't be removed from the &lt;code&gt;done&lt;/code&gt; set.
One possible approach we could use to uncheck an item is to create a new unchecked clone of
the item with a different &lt;code&gt;id&lt;/code&gt;, and then delete the original item.&lt;/p&gt;
&lt;p&gt;Alternatively, we can use what is known as a last write wins (LWW) set.
With this approach, we maintain a set of state changes, where each
element in the set has an associated timestamp.
We can say that when an item does not have an associated entry in this set it is not checked.
Otherwise, whether or not an item is checked or not is based on the entry with the latest timestamp.
For cases where two timestamps are identical, we select either the &lt;code&gt;checked&lt;/code&gt; or &lt;code&gt;unchecked&lt;/code&gt;
operation to win.
The listing below shows our to-do list updated to use a last write wins set of
state changes&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;items&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2b74e20b-18d5-461d-9763-351ec3356b59&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Buy milk&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fccb6547-f3aa-4f8f-97e1-3c1e5dbf85f9&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Mow the lawns&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;449beefe-f190-4fad-bf92-77ac7c1775eb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Iron shirts&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fb212751-a86d-4d41-b912-db3ec6ecdbac&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Wash the car&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;removed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;449beefe-f190-4fad-bf92-77ac7c1775eb&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fccb6547-f3aa-4f8f-97e1-3c1e5dbf85f9&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;checked&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1424165000&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fccb6547-f3aa-4f8f-97e1-3c1e5dbf85f9&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;unchecked&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1424166000&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2b74e20b-18d5-461d-9763-351ec3356b59&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;checked&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1424167000&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;ul class="task-list" style="list-style-type: none; margin-left: 1em; padding-left: 0;"&gt;
    &lt;li class="task-list-item"&gt;
        &lt;input type="checkbox" class="task-list-item-checkbox" disabled checked&gt;
        Buy milk
    &lt;/li&gt;
    &lt;li class="task-list-item"&gt;
        &lt;input type="checkbox" class="task-list-item-checkbox" disabled&gt;
        Mow the lawns
    &lt;/li&gt;
    &lt;li class="task-list-item"&gt;
        &lt;input type="checkbox" class="task-list-item-checkbox" disabled&gt;
        Wash the car
    &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;In this example our &amp;quot;Buy milk&amp;quot; item is checked, but &amp;quot;Mow the lawns&amp;quot; is not, as there
is an &amp;quot;unchecked&amp;quot; state change with a later timestamp than the &amp;quot;checked&amp;quot; state change
for this item.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="ordering-items"&gt;
&lt;h2&gt;Ordering items&lt;/h2&gt;
&lt;p&gt;So far we have assumed that our to-do list items are not in any particular order,
but users may want to arrange items to place more important items near the top of the list.
This is where things start to get a bit tricky.
Handling re-ordering of items can be achieved with CRDTs,
but the data structures become much more complex.
One example of an ordered CRDT is the &lt;a class="reference external" href="https://hal.inria.fr/inria-00445975"&gt;TreeDoc&lt;/a&gt; data structure used for collaborative text editing.&lt;/p&gt;
&lt;p&gt;TreeDoc assigns a path identifier to each node in the CRDT,
where each node represents a letter in a text document.
These position identifiers organise the document into a binary tree.
In a standard binary tree structure the root node has an empty position identifier, and
each node in the document has a position identifier equal to that of its parent concatenated with
a 0 if it is to the left of its parent, or a 1 if it is to the right.
However, TreeDoc has to allow for concurrent inserts at the same position.
To deal with this situation, each entry in the position identifier includes
an identifier corresponding to the site where the insert was initiated, and
these site identifiers are ordered.&lt;/p&gt;
&lt;p&gt;For our to-do list application, ordering is not as important as in a
text document, so we can afford to be less precise with our ordering approach.
One approach we can use is to maintain a last write wins set of sort-keys, where
each sort-key is a floating point value.
By using floating point sort-keys, we can always find a new sort-key between any
two existing items in our list.
If two users concurrently add different items at the same position, they
will have the same sort key, but we can choose to then order
these items based on their id.
The listing below shows our to-do list with a set of sort-keys applied:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;items&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2b74e20b-18d5-461d-9763-351ec3356b59&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Buy milk&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fccb6547-f3aa-4f8f-97e1-3c1e5dbf85f9&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Mow the lawns&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;449beefe-f190-4fad-bf92-77ac7c1775eb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Iron shirts&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fb212751-a86d-4d41-b912-db3ec6ecdbac&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Wash the car&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;removed&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;449beefe-f190-4fad-bf92-77ac7c1775eb&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fccb6547-f3aa-4f8f-97e1-3c1e5dbf85f9&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;checked&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1424165000&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fccb6547-f3aa-4f8f-97e1-3c1e5dbf85f9&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;unchecked&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1424166000&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2b74e20b-18d5-461d-9763-351ec3356b59&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;checked&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1424167000&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;sort_keys&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2b74e20b-18d5-461d-9763-351ec3356b59&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;sort&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1424163000&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fccb6547-f3aa-4f8f-97e1-3c1e5dbf85f9&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;sort&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1424163000&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;449beefe-f190-4fad-bf92-77ac7c1775eb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;sort&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1424163000&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fb212751-a86d-4d41-b912-db3ec6ecdbac&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;sort&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-3.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1424163000&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fccb6547-f3aa-4f8f-97e1-3c1e5dbf85f9&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;sort&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1424164000&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;ul class="task-list" style="list-style-type: none; margin-left: 1em; padding-left: 0;"&gt;
    &lt;li class="task-list-item"&gt;
        &lt;input type="checkbox" class="task-list-item-checkbox" disabled&gt;
        Mow the lawns
    &lt;/li&gt;
    &lt;li class="task-list-item"&gt;
        &lt;input type="checkbox" class="task-list-item-checkbox" disabled checked&gt;
        Buy milk
    &lt;/li&gt;
    &lt;li class="task-list-item"&gt;
        &lt;input type="checkbox" class="task-list-item-checkbox" disabled&gt;
        Wash the car
    &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;In this example, the original order of items was &amp;quot;Buy milk&amp;quot;, &amp;quot;Mow the lawns&amp;quot;, &amp;quot;Iron shirts&amp;quot;, then &amp;quot;Wash the car&amp;quot;,
but
&amp;quot;Mow the lawns&amp;quot; has been moved to the top of the list (and Iron shirts was removed).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We now have a data structure representing a to-do list that can be
replicated across multiple machines.
Each machine can independently apply operations on the to-do list and easily
merge any change from others without any chance of a conflict.&lt;/p&gt;
&lt;p&gt;One thing you've probably noticed is that the CRDT describing our to-do list
could end up being quite large as time progresses and many changes are made.
The last write wins sets can be easily cleaned up by removing
any entries that are invalidated by more recent entries.
Cleaning up a 2P-Set is somewhat more complex and requires synchronisation
across machines.
For more information on Garbage collection techniques for CRDTs
see the paper by &lt;a class="reference external" href="https://hal.inria.fr/inria-00555588"&gt;Shapiro et al. (2011)&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="CRDT"></category><category term="distributed systems"></category></entry><entry><title>SVG for Icons on the Web</title><link href="https://adamreeve.co.nz/blog/svg-web-icons.html" rel="alternate"></link><published>2014-10-31T00:00:00+13:00</published><updated>2014-10-31T00:00:00+13:00</updated><author><name>Adam Reeve</name></author><id>tag:adamreeve.co.nz,2014-10-31:/blog/svg-web-icons.html</id><summary type="html">&lt;p class="first last"&gt;This post describes the approach I used for implementing SVG based
icons on my website, and discusses some of the advantage and disadvantages
compared to font based icons.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;When updating my website recently, I decided to use SVG (Scalable Vector Graphics)
for icons.
It's currently very popular to use an icon font, for example,
&lt;a class="reference external" href="http://fortawesome.github.io/Font-Awesome/"&gt;Font Awesome&lt;/a&gt;, which was developed for use with &lt;a class="reference external" href="http://getbootstrap.com/"&gt;Twitter Bootstrap&lt;/a&gt;.
Using an icon font has a lot of benefits:
you get scalable vector images, you can easily style icons using CSS,
and your icons are all bundled into a single file that will be cached by browsers
so only needs to be downloaded once.
Icon fonts are also well supported by many browsers.&lt;/p&gt;
&lt;p&gt;However, icon fonts are a bit of a hack, and using them comes with some downsides:
it is trickier to position glyphs exactly using CSS,
and icons can only contain one colour.
Icons are also rendered as if they were text, which can result in unwanted
anti-aliasing.&lt;/p&gt;
&lt;p&gt;SVG based icons are a great alternative.
With SVG, you aren't constrained by the limitations of a font,
and the icons are just an image so you don't have to hassle with
text positioning.
It is also possible to style individual components within an SVG image
using CSS and add interactivity with JavaScript,
which opens up a lot of possibilities.&lt;/p&gt;
&lt;p&gt;There are a number of ways SVG images can be used in a web page.
The simplest is to use an &lt;code&gt;img&lt;/code&gt; tag, just as you would with
a JPG or PNG file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;path/to/my/icon.svg&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This approach requires
the browser to make an HTTP request for each SVG icon, so may not be
appropriate for websites with a lot of icons.&lt;/p&gt;
&lt;p&gt;Alternatively, SVG images can be applied as a background to
HTML elements using CSS.
A great resource for working with SVG icons that uses this approach is &lt;a class="reference external" href="http://grumpicon.com/"&gt;Grumpicon&lt;/a&gt;.
Grumpicon
generates CSS files that include SVG data inline, as well
as PNG fallbacks for browsers that don't support SVG.
However, the downside to this approach is that you can't
style the SVG icons using CSS.
For example, you might want the
icon colour to change on hover if it's part of a hyperlink.
In order to be able to style SVG graphics using CSS, the SVG
document has to be part of the HTML DOM.&lt;/p&gt;
&lt;p&gt;The approach I used for this website is based on
&lt;a class="reference external" href="http://css-tricks.com/svg-symbol-good-choice-icons/"&gt;this CSS-Tricks article&lt;/a&gt;.
All the icons are put into a single SVG document, and each icon
is contained in an SVG &lt;code&gt;symbol&lt;/code&gt; element.
This SVG document is included inline in the HTML, after the opening &lt;code&gt;body&lt;/code&gt; tag.
A complete example including two icons, one for a circle and one for a square, is shown below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://www.w3.org/2000/svg&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;display: none;&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1.1&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;symbol&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;icon-square&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0 0 28 28&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;fill:currentColor; fill-opacity:1; fill-rule:nonzero; stroke:none;&amp;quot;&lt;/span&gt;
      &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;M 4 4 L 24 4 L 24 24 L 4 24 Z&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/symbol&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;symbol&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;icon-circle&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0 0 28 28&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;fill:currentColor; fill-opacity:1; fill-rule:nonzero; stroke:none;&amp;quot;&lt;/span&gt;
      &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;M 23.97 14 C 23.98 17.57 22.09 20.88 19 22.67 C 15.91 24.46 12.09 24.46 9 22.67 C 5.91 20.88 4.02 17.57 4.04 14 C 4.02 10.43 5.91 7.12 9.00 5.33 C 12.09 3.54 15.91 3.54 19 5.33 C 22.09 7.12 23.98 10.43 23.97 14 Z&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/symbol&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that the SVG element has inline CSS applied so that it is not displayed.
Each symbol has it's own &lt;code&gt;viewBox&lt;/code&gt; attribute
applied, which defines the extent of the x and y coordinates in the symbol.
The &lt;code&gt;path&lt;/code&gt; for each icon has also been styled so that the fill is set to &lt;code&gt;currentColor&lt;/code&gt;.
This means that the fill colour will be set to the current font colour
used in the HTML document.&lt;/p&gt;
&lt;p&gt;To then use an icon in the document, you simply reference the symbol by its &lt;code&gt;id&lt;/code&gt;.
For example, this will display the square icon:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;svg&amp;gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;xlink:href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;#icon-square&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The main downside to this approach is that
all icons must be included on every HTML page,
increasing the size of each page.
The way around this is to use references to icons in an external resource.
In this case, the icon definitions are included in a separate SVG file, and are referenced like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;svg&amp;gt;&amp;lt;use&lt;/span&gt; &lt;span class="na"&gt;xlink:href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;svg-definitions.svg#icon-square&amp;quot;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The external SVG file can then be cached by the browser so only has to be downloaded once.
This isn't supported in any current version of Internet Explorer (up to IE 11), but
luckily, there is &lt;a class="reference external" href="https://github.com/jonathantneal/svg4everybody"&gt;svg4everybody&lt;/a&gt; available, which is a polyfill that provides support for
external SVG files in Internet Explorer.
Svg4Everybody also supports PNG images as a fallback for Internet Explorer versions
less than IE 9.
For more information on browser support for inline SVG, check out the &lt;a class="reference external" href="http://caniuse.com/#feat=svg-html5"&gt;Can I Use&lt;/a&gt; page.&lt;/p&gt;
</content><category term="misc"></category><category term="SVG"></category><category term="CSS"></category></entry><entry><title>Git Tutorial</title><link href="https://adamreeve.co.nz/blog/git-tutorial.html" rel="alternate"></link><published>2014-04-10T00:00:00+12:00</published><updated>2014-04-10T00:00:00+12:00</updated><author><name>Adam Reeve</name></author><id>tag:adamreeve.co.nz,2014-04-10:/blog/git-tutorial.html</id><summary type="html">&lt;p class="first last"&gt;My Git tutorial for the ABI summer software tutorial series.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;As part of the ABI summer software tutorial series, I developed a tutorial
for the Git distributed version control software.&lt;/p&gt;
&lt;p&gt;The tutorial is hosted at &lt;a class="reference external" href="http://abi-git-tutorial.readthedocs.org/"&gt;Read the Docs&lt;/a&gt;
and the presentation slides can be downloaded here:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://adamreeve.co.nz/files/git-tutorial.pdf"&gt;Presentation slides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://abi-git-tutorial.readthedocs.org/"&gt;Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="misc"></category><category term="git"></category></entry></feed>