RedisJSON v2.2 adds support for JSON in Active-Active Redis Enterprise databases.

The design is based on A Conflict-Free Replicated JSON Datatype by Kleppmann and Beresford, but the implementation includes some changes. Several conflict resolution rule examples were adapted from this paper as well.

Create an Active-Active JSON database

To use JSON in an Active-Active database, you must enable JSON during database creation.

Active-Active Redis Cloud databases add JSON by default. See Create an Active-Active database in the Redis Cloud documentation for details.

In Redis Enterprise Software, JSON is not enabled by default for Active-Active databases. To create an Active-Active JSON database in Redis Enterprise Software:

  1. See Create an Active-Active geo-replicated database in the Redis Enterprise Software documentation for prerequisites and detailed steps.

  2. In the Capabilities section of the Create Active-Active database screen, select JSON:

    Select JSON from the Capabilities section.
    Note:
    When you select JSON, Search and Query is also selected by default to allow you to index and query JSON documents. If you do not want to use these additional features, you can clear the Search and Query check box.
  3. Configure additional database settings.

  4. Select Create.

Command differences

Some JSON commands work differently for Active-Active databases.

JSON.CLEAR

JSON.CLEAR resets JSON arrays and objects. It supports concurrent updates to JSON documents from different instances in an Active-Active database and allows the results to be merged.

Conflict resolution rules

With Active-Active databases, it’s possible for two different instances to try to run write operations on the same data at the same time. If this happens, conflicts can arise when the replicas attempt to sync these changes with each other. Conflict resolution rules determine how the database handles conflicting operations.

There are two types of conflict resolution:

  1. Merge:

    • The operations are associative.

    • Merges the results of both operations.

  2. Win over:

    • The operations are not associative.

    • One operation wins the conflict and sets the value.

    • Ignores the losing operation.

The following conflict resolution rules show how Active-Active databases resolve conflicts for various JSON commands.

Assign different types to a key

Conflict

Two instances concurrently assign values of different types to the same key within a JSON document.

For example:

Instance 1 assigns an object to a key within a JSON document.

Instance 2 assigns an array to the same key.

Resolution type

Win over

Resolution rule

The instance with the smaller ID wins, so the key becomes an object in the given example.

Example

TimeDescriptionInstance 1Instance 2
t1Set the same key to an object or an arrayJSON.SET doc $.a ‘{}’JSON.SET doc $.a ‘[]’
t2Add data to the object and arrayJSON.SET doc $.a.x ‘“y”’

Result:
{“a”: {“x”: “y”}}
JSON.SET doc $.a ‘[“z”]’

Result:
{“a”: [“z”]}
t3Active-Active synchronization– Sync –– Sync –
t4Instance 1 winsJSON.GET doc $

Result:
{“a”: {“x”: “y”}}
JSON.GET doc $

Result:
{“a”: {“x”: “y”}}

Create versus create

Conflict

Two instances concurrently use JSON.SET to assign a new JSON document to the same key.

Resolution type

Win over

Resolution rule

The instance with the smaller ID wins.

Example

TimeDescriptionInstance 1Instance 2
t1Create a new JSON documentJSON.SET doc $ ‘{“field”: “a”}’JSON.SET doc $ ‘{“field”: “b”}’
t2Active-Active synchronization– Sync –– Sync –
t3Instance 1 winsJSON.GET doc $

Result:
{“field”: “a”}
JSON.GET doc $

Result:
{“field”: “a”}

Create versus update

Conflict

Instance 1 creates a new document and assigns it to an existing key with JSON.SET.

Instance 2 updates the existing content of the same key with JSON.SET.

Resolution type

Win over

Resolution rule

The operation that creates a new document wins.

Example

TimeDescriptionInstance 1Instance 2
t1The document exists on both instancesJSON.GET doc $

Result:
{“field1”: “value1”}
JSON.GET doc $

Result:
{“field1”: “value1”}
t2Instance 1 creates a new document; instance 2 updates the existing documentJSON.SET doc $ ‘{“field2”: “value2”}’JSON.SET doc $.field1 ‘[1, 2, 3]’
t3Active-Active synchronization– Sync –– Sync –
t4Instance 1 winsJSON.GET doc .

Result:
{“field2”: “value2”}
JSON.GET doc .

Result:
{“field2”: “value2”}

Delete versus create

Conflict

Instance 1 deletes a JSON document with JSON.DEL.

Instance 2 uses JSON.SET to create a new JSON document and assign it to the key deleted by instance 1.

Resolution type

Win over

Resolution rule

Document creation wins over deletion.

Example

TimeDescriptionInstance 1Instance 2
t1The document exists on both instancesJSON.GET doc $

Result:
{“field1”: “value1”}
JSON.GET doc $

Result:
{“field1”: “value1”}
t2Instance 1 deletes the document; instance 2 creates a new documentJSON.DEL docJSON.SET doc $ ‘{“field1”: “value2”}’
t3Active-Active synchronization– Sync –– Sync –
t4Instance 2 winsJSON.GET doc $

Result:
{“field1”: “value2”}
JSON.GET doc $

Result:
{“field1”: “value2”}

Delete versus update

Conflict

Instance 1 deletes a JSON document with JSON.DEL.

Instance 2 updates the content of the same document with JSON.SET.

Resolution type

Win over

Resolution rule

Document deletion wins over updates.

Example

TimeDescriptionInstance 1Instance 2
t1The document exists on both instancesJSON.GET doc $

Result:
{“field1”: “value1”}
JSON.GET doc $

Result:
{“field1”: “value1”}
t2Instance 1 deletes the document; instance 2 updates itJSON.DEL docJSON.SET doc $.field1 ‘[1, 2, 3]’
t3Active-Active synchronization– Sync –– Sync –
t4Instance 1 winsJSON.GET doc $

Result:
(nil)
JSON.GET doc $

Result:
(nil)

Update versus update

Conflict

Instance 1 updates a field inside a JSON document with JSON.SET.

Instance 2 updates the same field with a different value.

Resolution type

Win over

Resolution rule

The instance with the smallest ID wins.

Example

TimeDescriptionInstance 1Instance 2
t1The document exists on both instancesJSON.GET doc $

Result:
{“field”: “a”}
JSON.GET doc $

Result:
{“field”: “a”}
t2Update the same field with different dataJSON.SET doc $.field “b”JSON.SET doc $.field “c”
t3Active-Active synchronization– Sync –– Sync –
t4Instance 1 winsJSON.GET doc $

Result:
{“field”: “b”}
JSON.GET doc $

Result:
{“field”: “b”}

Update versus clear

The version of RedisJSON prior to v2.2 has two different ways to reset the content of a JSON object:

  • Assign a new empty JSON object:

    JSON.SET doc $.colors '{}'
    

    If you use this method, it cannot be merged with a concurrent update.

  • For each key, remove it with JSON.DEL:

    JSON.DEL doc $.colors.blue
    

    With this method, it can merge the reset with concurrent updates.

As of RedisJSON v2.2, you can use the JSON.CLEAR command to reset the JSON document without removing each key manually. This method also lets concurrent updates be merged.

Assign an empty object

Conflict

Instance 1 adds “red” to the existing “colors” object with JSON.SET.

Instance 2 assigns a new empty object for “colors”.

Resolution type

Win over

Resolution rule

Document creation wins over the update, so the result will be an empty object.

Example

TimeDescriptionInstance 1Instance 2
t1The document exists on both instancesJSON.GET doc $

Result:
{“colors”: {“blue”: “#0000ff”}}
JSON.GET doc $

Result:
{“colors”: {“blue”: “#0000ff”}}
t2Instance 1 adds a new color; instance 2 resets colors to an empty objectJSON.SET doc $.colors.red ‘#ff0000’JSON.SET doc $.colors ‘{}’
t3Instance 2 adds a new colorJSON.SET doc $.colors.green ‘#00ff00’
t4JSON.GET doc $

Result:
{“colors”: {“blue”: “#0000ff”, “red”: “#ff0000”}}
JSON.GET doc $

Result:
{“colors”: {“green”: “#00ff00”}}
t5Active-Active synchronization– Sync –– Sync –
t6Instance 2 winsJSON.GET doc $

Result:
{“colors”: {“green”: “#00ff00”}}
JSON.GET doc $

Result:
{“colors”: {“green”: “#00ff00”}}

Use JSON.CLEAR

Conflict

Instance 1 adds “red” to the existing “colors” object with JSON.SET.

Instance 2 clears the “colors” object with JSON.CLEAR and adds “green” to “colors”.

Resolution type

Merge

Resolution rule

Merges the results of all operations.

Example

TimeDescriptionInstance 1Instance 2
t1The document exists on both instancesJSON.GET doc $

Result:
{“colors”: {“blue”: “#0000ff”}}
JSON.GET doc $

Result:
{“colors”: {“blue”: “#0000ff”}}
t2Instance 1 adds a new color; instance 2 resets the colorsJSON.SET doc $.colors.red ‘#ff0000’JSON.CLEAR doc $.colors
t3JSON.GET doc $

Result:
{“colors”: {“blue”: “#0000ff”, “red”: “#ff0000”}}
JSON.GET doc $

Result:
{“colors”: {}}
t4Instance 2 adds a new colorJSON.SET doc $.colors.green ‘#00ff00’
t5JSON.GET doc $

Result:
{“colors”: {“green”: “#00ff00”}}
t6Active-Active synchronization– Sync –– Sync –
t7Merges the results of both instancesJSON.GET doc $

Result:
{“colors”: {“red”: “#ff0000”, “green”: “#00ff00”}}
JSON.GET doc $

Result:
{“colors”: {“red”: “#ff0000”, “green”: “#00ff00”}}

Update versus update array

Conflict

Two instances update the same existing array with different content.

Resolution type

Merge

Resolution rule

Merges the results of all operations on the array. Preserves the original element order from each instance.

Example

TimeDescriptionInstance 1Instance 2
t1The document exists on both instancesJSON.GET doc $

Result:
‘[“a”, “b”, “c”]’
JSON.GET doc $

Result:
‘[“a”, “b”, “c”]’
t2Instance 1 removes an array element; instance 2 adds oneJSON.ARRPOP doc $ 1

Result:
[“a”, “c”]
JSON.ARRINSERT doc $ 0 ‘“y”’

Result:
[“y”, “a”, “b”, “c”]
t3Both instances add another element to the arrayJSON.ARRINSERT doc $ 1 ‘“x”’

Result:
[“a”, “x”, “c”]
JSON.ARRINSERT doc $ 2 ‘“z”’

Result:
[“y”, “a”, “z”, “b”, “c”]
t4Active-Active synchronization– Sync –– Sync –
t5Merge results from both instancesJSON.GET doc $

Result:
[“y”, “a”, “x”, “z”, “c”]
JSON.GET doc $

Result:
[“y”, “a”, “x”, “z”, “c”]

Update versus delete array element

Conflict

Instance 1 removes an element from a JSON array with JSON.ARRPOP.

Instance 2 updates the same element that instance 1 removes.

Resolution type

Win over

Resolution rule

Deletion wins over updates.

Example

TimeDescriptionInstance 1Instance 2
t1The document exists on both instancesJSON.GET doc $

Result:
{“todo”: [{“title”: “buy milk”, “done”: false}]}
JSON.GET doc $

Result:
{“todo”: [{“title”: “buy milk”, “done”: false}]}
t2Instance 1 removes an array element; instance 2 updates the same elementJSON.ARRPOP doc $.todo 0JSON.SET doc ‘$.todo[0][“done”]’ ’true’’
t3JSON.GET doc $

Result:
{“todo”: []}
JSON.GET doc $

Result:
[{“title”: “buy milk”, “done”: true}]}
t4Active-Active synchronization– Sync –– Sync –
t5Instance 1 winsJSON.GET doc $

Result:
doc = {“todo”: []}
JSON.GET doc $

Result:
doc = {“todo”: []}

Update versus update object

Conflict

Both instances update the same existing object with different content.

Resolution type

Merge

Resolution rule

Merges the results of all operations on the object.

Example

TimeDescriptionInstance 1Instance 2
t1The document exists on both instancesJSON.GET doc $

Result:
‘{“grocery”: []}’
JSON.GET doc $

Result:
‘{“grocery”: []}’
t2Add new elements to the arrayJSON.ARRAPPEND doc $.grocery ‘“eggs”’JSON.ARRAPPEND doc $.grocery ‘“milk”’
t3Add new elements to the arrayJSON.ARRAPPEND doc $.grocery ‘“ham”’JSON.ARRAPPEND doc $.grocery ‘“flour”’
t4JSON.GET doc $

Result:
{“grocery”:[“eggs”, “ham”]}
JSON.GET doc $

Result:
{“grocery”:[“milk”, “flour”]}
t5Active-Active synchronization– Sync –– Sync –
t6Merges the results from both instancesJSON.GET doc .

Result:
{“grocery”:[“eggs”,“ham”,“milk”, “flour”]}
JSON.GET doc .

Result:
{“grocery”:[“eggs”,“ham”,“milk”, “flour” ]}