TRY ME

Try Valo for free

We want to show you something amazing.

We'll send you a link to download a fully functional Valo copy to play with.



Great! Check your email and enjoy Valo



Apologies, we seem to be having a problem processing your input, please try again

Version headers

Many services in the Valo API store their configuration in the internal key value store. Since Valo is a distributed system, it is possible for the same document to be changed independently on multiple cluster nodes.

To mitigate these issues, special Valo-Config-Version headers are returned. These must be quoted back in subsequent updates to the document.

When conflicting updates occur, all versions of the document are returned. The user must merge them together and re-submit the request quoting all the versions in the header.

This workflow applies to the following services:

Creating a new document

No additional information is required when creating the document for the first time. The response will include a Valo-Config-Version. The client should record this and include it in subsequent requests.

PUT /service/tenant/collection/document HTTP/1.1

{
    [ document contents ]
}
HTTP/1.1 200 OK
Valo-Config-Version: Y2BgYGDNy09JdWSAAEYA64lGJBI=

{
    [ document contents ]
}

If the document already exists, a 409 Conflict will be returned with the current document and version.

PUT /service/tenant/collection/document HTTP/1.1

{
    [ document contents ]
}
HTTP/1.1 409 Conflict
Valo-Config-Version: Y2BgYGDNy09JdWSAAEYA64lGJBI=

{
    [ document contents ]
}

The version is also included in the response to simple GET requests against the document:

GET /service/tenant/collection/document HTTP/1.1
HTTP/1.1 200 OK
Valo-Config-Version: Y2BgYGDNy09JdWSAAEYA64lGJBI=

{
    [ document contents ]
}

Just the version is returned in response to a HEAD request:

HEAD /service/tenant/collection/document HTTP/1.1
HTTP/1.1 200 OK
Valo-Config-Version: Y2BgYGDNy09JdWSAAEYA64lGJBI=

Updating an existing document

To update an existing document, the previous Valo-Config-Version must be included in the request. A new version will be returned. Subsequent updates should include the new version.

PUT /service/tenant/collection/document HTTP/1.1
Valo-Config-Version: Y2BgYGDNy09JdWSAAEYA64lGJBI=

{
    [ document contents ]
}
HTTP/1.1 200 OK
Valo-Config-Version: Y2BgYGBMZIAARhBOgnEAX36voxs=

{
    [ document contents ]
}

Deleting Documents

When a document is deleted, a “tombstone” version header is returned. If the same document is resubmitted later, the “tombstone” version must be included in the request.

DELETE /service/tenant/collection/document HTTP/1.1
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAM=
HTTP/1.1 200 OK
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQ=

{
    [ response to deletion from the service (if any) ]
}

If a GET request is issued for a deleted document, a 404 Not Found is returned with the tombstone version in the headers.

GET /service/tenant/collection/document HTTP/1.1

HTTP/1.1 404 Not Found
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQ=

{
    [ message indicating not found from the service (if any) ]
}

Handling document conflicts

Conflicts can arise when modifications are made whilst some of the nodes in the cluster are unavailable or the cluster is experiencing “split-brain” syndrome.

As an example, imagine we have a eight node cluster. Four of the nodes are running in one server rack and the others in another. If the network link between the racks is interrupted, both sides of the cluster will continue running independently, accepting operations and returning new version headers:

../_images/split_brain.png

We now have two divergent versions for the same piece of data. Once the partition in the cluster has healed and we attempt to make further updates using either the version from the left or right, a conflict response will be returned.

This contains the two (or more) divergent copies of document in question as well as the version for each, returned as a multipart message conforming to RFC 1521. There is a body entry for each version of the document stored.

PUT /service/tenant/collection/document HTTP/1.1
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQ=

{
    [ document contents ]
}
HTTP/1.1 409 Conflict
Content-Type: multipart/mixed; boundary="---------------------------03871c34122e45d4b572b76fb30bb37a"

---------------------------03871c34122e45d4b572b76fb30bb37a
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQ=

[ document contents ]

---------------------------03871c34122e45d4b572b76fb30bb37a
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQ=

[ document contents ]

The same will also occur for any attempt to GET the current document:

GET /service/tenant/collection/document HTTP/1.1
HTTP/1.1 409 Conflict
Content-Type: multipart/mixed; boundary="---------------------------03871c34122e45d4b572b76fb30bb37a"

---------------------------03871c34122e45d4b572b76fb30bb37a
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQ=

[ document contents ]

---------------------------03871c34122e45d4b572b76fb30bb37a
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQ=

[ document contents ]

HEAD requests return only the version headers:

HEAD /service/tenant/collection/document HTTP/1.1
HTTP/1.1 409 Conflict
Content-Type: multipart/mixed; boundary="---------------------------03871c34122e45d4b572b76fb30bb37a"

---------------------------03871c34122e45d4b572b76fb30bb37a
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQ=

---------------------------03871c34122e45d4b572b76fb30bb37a
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQ=

The client must inspect each document and resolve the conflicts. How to do this correctly depends on the service in question. A common strategy would be to recursively merge the fields in the document. If the documents have different values for the same field then the user must pick which one they want to keep.

Once the conflicts have been resolved, the new document should be updated again. This time we quote back all the individual version hashes we received in the conflict response previously. Valo returns a new version that can be used for subsequent requests.

PUT /service/tenant/collection/document HTTP/1.1
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQ=, AQcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmAAAAAAAAAAQ=

{
    [ document contents ]
}
HTTP/1.1 200 OK
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJgAAAAAAAAAE

{
    [ document contents ]
}

Conflicts between deletes and writes

Just as there can be conflicts between updates to a resource, we can also have conflicts between updates and deletion. In such cases, the tombstone version is also presented, identified as a deletion by an empty body:

GET /service/tenant/collection/document HTTP/1.1
HTTP/1.1 409 Conflict

HTTP/1.1 409 Conflict
Content-Type: multipart/mixed; boundary="---------------------------03871c34122e45d4b572b76fb30bb37a"

---------------------------03871c34122e45d4b572b76fb30bb37a
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQ=

---------------------------03871c34122e45d4b572b76fb30bb37a
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQ=

[ document contents ]

To resolve the conflict by keeping the resource and not deleting it, simply submit the tombstone version as one of the versions using the method above.

To resolve the conflict by deleting the resource, quote back all versions (including the tombstone) in a DELETE request:

DELETE /service/tenant/collection/document HTTP/1.1
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQ=, QcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmAAAAAAAAAAQ=
HTTP/1.1 200 OK
Valo-Config-Version: AQABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fAAAAAAAAAAQHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJgAAAAAAAAAE

{
    [ response to deletion from the service (if any) ]
}