Octopus Deploy uses RavenDB for data storage. Projects, releases, deployments, deployment logs, and so on are all kept in an embedded RavenDB database. The UI for Octopus is written in ASP.NET MVC, with most of the data rendering done using Razor.
These technology choices actually create a bit of a problem: RavenDB indexes are asynchronous, but we really want to render the most up-to-date information that we can when we render a page. RavenDB applications should be designed for eventual consistency, but standard ASP.NET MVC views/controllers lead you to assume you're always rendering the most up-to-date information.
We encountered this kind of bug report a lot when we first released the RavenDB version of Octopus:
I went to the "add machine" page, entered the machine information, clicked save, but when I went to the environments page, my machine wasn't listed. A few seconds later I hit refresh and there it was.
To fix these issues, I scattered this on almost all of the queries:
.Customize(x => x.WaitForNonStaleResultsAsOfNow())
In fact I did it so much that I created an extension method to make it easier:
.WaitForAges()
The upside of doing this is that every page always showed up to date information. The downside is that, once a server has too many documents, requests start timing out because the indexes are out of date. This is becoming a problem.
Designing for eventual consistency
Instead of the eventual consistency model being a weakness, for Octopus Deploy V2 I'd like to make it a strength.
When we render a page, we'll avoid doing any queries to RavenDB. Instead we'll render down the HTML/CSS/JS needed for the page. For each of the areas on the page, we'll use SignalR to fetch the data asynchronously. The result will include whether the index is stale, and if so, we'll let the user know ("this data may be out of date - last updated on XYZ").
Then, using SignalR, we'll subscribe to change notifications in RavenDB, and relay them back to the application to tell it that the data has changed.
Once an area of the page knows that there's new data available, it might:
- Show the updates immediately
- Show a message/refresh button
This approach should hopefully make the UI feel faster (since we can render information quickly without waiting for the indexes to be up to date) as well as feeling more real-time (no more hitting F5).