Vendia Transactions

Vendia Share uses transactions as its data processing unit. Each transaction contains one or more updates to a data value in a Vendia Share Uni. Vendia Transactions enable users to group multiple changes on multiple data values in a single transaction and guarantees that all updates are processed serially, in order, as an atomic unit. This feature is analogous to relational database transactions. Vendia Transactions can also be used when querying data to ensure that all nodes see a consistent value even if an in-flight transaction is actively being processed across nodes.

When Vendia Transactions are used when updating data in a Uni, you can easily manage complex workflows that require coordinated updates on multiple data items. Vendia Transactions also provide a transactional query mechanism to prevent returning inconsistent data among nodes.

If Vendia Transactions are not used when updating data in a Uni, there is no guarantee of transaction order. There is no way to group transactions together as an atomic unit where all data updates complete successfully or none do. The same query may return different results among nodes as data is updated.

Vendia Share supports transactions through a custom GraphQL directive to decorate the mutations and queries: @vendia_transaction

Mutations without Vendia Transactions

A single mutation can include multiple operations. For example, if we wanted to define multiple update_Inventory operations in a single mutation m, we could use the following syntax:

mutation m {
  warehouse1: update_Inventory(id: "017d92a7-0ab5-5513-fac1-c50be330f057", syncMode: NODE_LEDGERED, input: {quantityWarehouse1: 90, lastUpdated: "2021-12-06T18:30:00Z"}) {
    result {
      _id
    }
  }
  warehouse2: update_Inventory(id: "017d92a7-0ab5-5513-fac1-c50be330f057", syncMode: NODE_LEDGERED, input: {quantityWarehouse2: 10, lastUpdated: "2021-12-06T18:30:00Z"}) {
    result {
      _id
    }
  }
}

The Vendia ledger shows that these two mutations happened across two separate transactions.

Sample listVendia_BlockItems query and result

Query:

query listBlocks {
  listVendia_BlockItems {
    Vendia_BlockItems {
      blockId
      transactions {
        _id
        mutations
      }
    }
  }
}

Result:

...
{
  "blockId": "000000000000004",
  "transactions": [
    {
      "_id": "017d931b-0f5b-4252-f2aa-7ecd54227482",
      "mutations": [
        "updateSelf_Inventory(id:\"017d92a7-0ab5-5513-fac1-c50be330f057\",input: {lastUpdated: \"2021-12-06T18:30:00Z\", quantityWarehouse1: 90}){error}"
      ]
    }
  ]
},
{
  "blockId": "000000000000005",
  "transactions": [
    {
      "_id": "017d931b-0ff5-652b-7b88-4936914ea73b",
      "mutations": [
        "updateSelf_Inventory(id:\"017d92a7-0ab5-5513-fac1-c50be330f057\",input: {lastUpdated: \"2021-12-06T18:30:00Z\", quantityWarehouse2: 10}){error}"
      ]
    }
  ]
}
...

There is no guarantee of order for these two operations. The update_Inventory operation in warehouse2 may complete before the operation in warehouse1. Each operation will occur independently; meaning some may succeed while others fail.

For some business cases this may be acceptable. For others - managing financial transactions or warehouse inventory - it may not. Vendia Transactions should be used in those cases where multiple operations must be processed in order and as a unit where all operations succeed or fail together.

Mutations with Vendia Transactions

In next mutation, we will run the same mutation but include the @vendia_transaction GraphQL decorator.

mutation m @vendia_transaction {
  warehouse1: update_Inventory(id: "017d92a7-0d60-39c9-def9-650929aefc28", syncMode: NODE_LEDGERED, input: {quantityWarehouse1: 100, lastUpdated: "2021-12-06T18:30:00Z"}) {
    result {
      _id
    }
  }
  warehouse2: update_Inventory(id: "017d92a7-0d60-39c9-def9-650929aefc28", syncMode: NODE_LEDGERED, input: {quantityWarehouse2: 400, lastUpdated: "2021-12-06T18:30:00Z"}) {
    result {
      _id
    }
  }
}

The Vendia ledger shows that these two operations happened in a single transaction.

Sample listVendia_BlockItems query and result when using a Vendia Transaction

Query:

query listBlocks {
  listVendia_BlockItems {
    Vendia_BlockItems {
      blockId
      transactions {
        _id
        mutations
      }
    }
  }
}

Result demonstrating our two update mutations were part of the same transaction:

...
{
  "blockId": "000000000000006",
  "transactions": [
    {
      "_id": "017d9327-fe6f-3b34-f507-3e227963aad6",
      "mutations": [
        "mutation m{warehouse1: updateSelf_Inventory(id:\"017d92a7-0d60-39c9-def9-650929aefc28\",input: {lastUpdated: \"2021-12-06T18:30:00Z\", quantityWarehouse1: 100}){error}\nwarehouse2: updateSelf_Inventory(id:\"017d92a7-0d60-39c9-def9-650929aefc28\",input: {lastUpdated: \"2021-12-06T18:30:00Z\", quantityWarehouse2: 400}){error}}"
      ]
    }
  ]
}
...

If we attempt to run an invalid operation (e.g. violating the constraint of a field) in a Vendia Transaction then all operations will fail. For example, if there was a constraint on quantityWarehouse2 that stated the value could not be negative, the following mutation m would fail.

mutation m @vendia_transaction {
  warehouse1: update_Inventory(id: "017d92a7-0ab5-5513-fac1-c50be330f057", syncMode: NODE_LEDGERED, input: {quantityWarehouse1: 800, lastUpdated: "2021-12-06T18:30:00Z"}) {
    result {
      _id
    }
  }
  warehouse2: update_Inventory(id: "017d92a7-0ab5-5513-fac1-c50be330f057", syncMode: NODE_LEDGERED, input: {quantityWarehouse2: -10, lastUpdated: "2021-12-06T18:30:00Z"}) {
    result {
      _id
    }
  }
}
Sample Vendia Transaction error
{
  "data": {
    "warehouse1": {
      "error": "{\"operation\": \"updateSelf_Inventory\", \"key\": \"warehouse1\", \"action\": \"update\", \"data_type\": \"Self_Inventory\", \"data\": {\"id\": \"017d92a7-0ab5-5513-fac1-c50be330f057\", \"input\": {\"lastUpdated\": \"2021-12-06T18:30:00Z\", \"quantityWarehouse1\": 80}, \"_id\": \"017d92a7-0ab5-5513-fac1-c50be330f057\"}}",
      "result": null
    },
    "warehouse2": null
  },
  "errors": [
    {
      "message": "-10 is less than the minimum of 0\n\nFailed validating 'minimum' in schema['properties']['Inventory']['items']['properties']['quantityWarehouse2']:\n    {'description': 'Available quanitity of item in Warehouse1',\n     'minimum': 0,\n     'type': 'integer'}\n\nOn instance['Inventory'][0]['quantityWarehouse2']:\n    -10",
      "locations": [
        {
          "line": 23,
          "column": 3
        }
      ],
      "path": [
        "warehouse2"
      ]
    }
  ]
}

Neither of the operations in the mutation m are applied.

Queries with Vendia Transactions

A query using the @vendia-transaction GraphQL directive ensures that all nodes will see the same, consistent value until all nodes have the update applied. Due to the asynchronous nature of Vendia Share, it may take a brief amount of time for updated values to be reflected on all nodes in a Uni.

query q @vendia_transaction {
  get_Inventory(id: "017d92a7-0ab5-5513-fac1-c50be330f057") {
    _id
    itemName
    itemNumber
    quantityWarehouse1
    quantityWarehouse2
  }
}