Skip to content
immudb docker run -d --net host -it --name immudb codenotary/immudb:latest

Transactions

GetAll, SetAll and ExecAll are the foundation of transactions at key value level in immudb. They allow the execution of a group of commands in a single step, with two important guarantees:

  • All the commands in a transaction are serialized and executed sequentially. No request issued by another client can ever interrupt the execution of a transaction. This guarantees that the commands are executed as a single isolated operation.
  • Either all of the commands are processed, or none are, so the transaction is also atomic.

Go

 1package main
 2
 3import (
 4	"context"
 5	"log"
 6
 7	immudb "github.com/codenotary/immudb/pkg/client"
 8)
 9
10func main() {
11	opts := immudb.DefaultOptions().
12		WithAddress("localhost").
13		WithPort(3322)
14
15	client := immudb.NewClient().WithOptions(opts)
16	err := client.OpenSession(
17		context.TODO(),
18		[]byte(`immudb`),
19		[]byte(`immudb`),
20		"defaultdb",
21	)
22	if err != nil {
23		log.Fatal(err)
24	}
25
26	defer client.CloseSession(context.TODO())
27
28	_, err = client.Set(context.TODO(), []byte(`key1`), []byte(`val1`))
29	if err != nil {
30		log.Fatal(err)
31	}
32	_, err = client.Set(context.TODO(), []byte(`key2`), []byte(`val2`))
33	if err != nil {
34		log.Fatal(err)
35	}
36
37	itList, err := client.GetAll(context.TODO(), [][]byte{
38		[]byte("key1"),
39		[]byte("key2"),
40		[]byte("key3"), // does not exist, no value returned
41	})
42	if err != nil {
43		log.Fatal(err)
44	}
45
46	log.Printf("Set: tx: %+v", itList)
47}

Python

 1from immudb import ImmudbClient
 2
 3URL = "localhost:3322"  # immudb running on your machine
 4LOGIN = "immudb"        # Default username
 5PASSWORD = "immudb"     # Default password
 6DB = b"defaultdb"       # Default database name (must be in bytes)
 7
 8def main():
 9    client = ImmudbClient(URL)
10    client.login(LOGIN, PASSWORD, database = DB)
11
12    client.set(b'key1', b'value1')
13    client.set(b'key2', b'value2')
14    client.set(b'key3', b'value3')
15    
16    response = client.getAll([b'key1', b'key2', b'key3'])
17    print(response) # The same as dictToSetGet, retrieved in one step
18
19if __name__ == "__main__":
20    main()

Java

 1package io.codenotary.immudb.helloworld;
 2
 3import java.util.Arrays;
 4import java.util.List;
 5
 6import io.codenotary.immudb4j.Entry;
 7import io.codenotary.immudb4j.FileImmuStateHolder;
 8import io.codenotary.immudb4j.ImmuClient;
 9
10public class App {
11
12    public static void main(String[] args) {
13
14        ImmuClient client = null;
15
16        try {
17
18            FileImmuStateHolder stateHolder = FileImmuStateHolder.newBuilder()
19                    .withStatesFolder("./immudb_states")
20                    .build();
21
22            client = ImmuClient.newBuilder()
23                    .withServerUrl("127.0.0.1")
24                    .withServerPort(3322)
25                    .withStateHolder(stateHolder)
26                    .build();
27
28            client.openSession("defaultdb", "immudb", "immudb");
29
30            byte[] value1 = { 0, 1, 2, 3 };
31            byte[] value2 = { 4, 5, 6, 7 };
32
33            client.set("key1", value1);
34            client.set("key2", value2);
35
36            List<String> keys = Arrays.asList("key1", "key2");
37            List<Entry> entries = client.getAll(keys);
38
39            for (Entry entry : entries) {
40                System.out.format("('%s', '%s')\n", new String(entry.getKey()), Arrays.toString(entry.getValue()));
41            }
42
43            client.closeSession();
44
45        } catch (Exception e) {
46            e.printStackTrace();
47        } finally {
48            if (client != null) {
49                try {
50                    client.shutdown();
51                } catch (InterruptedException e) {
52                    e.printStackTrace();
53                }
54            }
55        }
56
57    }
58
59}

Node.js

 1import ImmudbClient from 'immudb-node'
 2import Parameters from 'immudb-node/types/parameters'
 3
 4const IMMUDB_HOST = '127.0.0.1'
 5const IMMUDB_PORT = '3322'
 6const IMMUDB_USER = 'immudb'
 7const IMMUDB_PWD = 'immudb'
 8
 9const cl = new ImmudbClient({ host: IMMUDB_HOST, port: IMMUDB_PORT });
10
11(async () => {
12 await cl.login({ user: IMMUDB_USER, password: IMMUDB_PWD })
13
14 const getAllReq: Parameters.GetAll = {
15  keysList: ['key1', 'key2', 'key3'],
16  sincetx: 0
17 }
18 const getAllRes = await cl.getAll(getAllReq)
19 console.log('success: getAll', getAllRes)
20})()

Others

If you’re using another development language, please refer to the immugw option.

SetAll#

A more versatile atomic multi set operation

Go

 1package main
 2
 3import (
 4	"context"
 5	"log"
 6
 7	"github.com/codenotary/immudb/pkg/api/schema"
 8	immudb "github.com/codenotary/immudb/pkg/client"
 9)
10
11func main() {
12	opts := immudb.DefaultOptions().
13		WithAddress("localhost").
14		WithPort(3322)
15
16	client := immudb.NewClient().WithOptions(opts)
17	err := client.OpenSession(
18		context.TODO(),
19		[]byte(`immudb`),
20		[]byte(`immudb`),
21		"defaultdb",
22	)
23	if err != nil {
24		log.Fatal(err)
25	}
26
27	defer client.CloseSession(context.TODO())
28
29	tx, err := client.SetAll(context.TODO(), &schema.SetRequest{
30		KVs: []*schema.KeyValue{
31			{Key: []byte(`1`), Value: []byte(`key1`)},
32			{Key: []byte(`2`), Value: []byte(`key2`)},
33			{Key: []byte(`3`), Value: []byte(`key3`)},
34		},
35	})
36	if err != nil {
37		log.Fatal(err)
38	}
39	log.Printf("SetAll: tx: %d", tx.Id)
40}

Python

 1from immudb import ImmudbClient
 2
 3URL = "localhost:3322"  # immudb running on your machine
 4LOGIN = "immudb"        # Default username
 5PASSWORD = "immudb"     # Default password
 6DB = b"defaultdb"       # Default database name (must be in bytes)
 7
 8def main():
 9    client = ImmudbClient(URL)
10    client.login(LOGIN, PASSWORD, database = DB)
11    dictToSetGet = {
12        b'key1': b'value1',
13        b'key2': b'value2',
14        b'key3': b'value3'
15    }
16    response = client.setAll(dictToSetGet)
17    print(response.id) # All in one transaction
18
19    response = client.getAll([b'key1', b'key2', b'key3'])
20    print(response) # The same as dictToSetGet, retrieved in one step
21
22if __name__ == "__main__":
23    main()

Java

 1package io.codenotary.immudb.helloworld;
 2
 3import io.codenotary.immudb4j.FileImmuStateHolder;
 4import io.codenotary.immudb4j.ImmuClient;
 5import io.codenotary.immudb4j.KVListBuilder;
 6
 7public class App {
 8
 9    public static void main(String[] args) {
10
11        ImmuClient client = null;
12
13        try {
14
15            FileImmuStateHolder stateHolder = FileImmuStateHolder.newBuilder()
16                    .withStatesFolder("./immudb_states")
17                    .build();
18
19            client = ImmuClient.newBuilder()
20                    .withServerUrl("127.0.0.1")
21                    .withServerPort(3322)
22                    .withStateHolder(stateHolder)
23                    .build();
24
25            client.openSession("defaultdb", "immudb", "immudb");
26
27            byte[] value1 = { 0, 1, 2, 3 };
28            byte[] value2 = { 4, 5, 6, 7 };
29
30            KVListBuilder kvListBuilder = KVListBuilder.newBuilder().
31                add("key1", value1).
32                add("key2", value2);
33
34            client.setAll(kvListBuilder.entries());
35
36            client.closeSession();
37
38        } catch (Exception e) {
39            e.printStackTrace();
40        } finally {
41            if (client != null) {
42                try {
43                    client.shutdown();
44                } catch (InterruptedException e) {
45                    e.printStackTrace();
46                }
47            }
48        }
49
50    }
51
52}

Node.js

 1import ImmudbClient from 'immudb-node'
 2import Parameters from 'immudb-node/types/parameters'
 3
 4const IMMUDB_HOST = '127.0.0.1'
 5const IMMUDB_PORT = '3322'
 6const IMMUDB_USER = 'immudb'
 7const IMMUDB_PWD = 'immudb'
 8
 9const cl = new ImmudbClient({ host: IMMUDB_HOST, port: IMMUDB_PORT });
10
11(async () => {
12 await cl.login({ user: IMMUDB_USER, password: IMMUDB_PWD })
13
14 const setAllReq: Parameters.SetAll = {
15  kvsList: [
16   { key: '1,2,3', value: '3,2,1' },
17   { key: '4,5,6', value: '6,5,4' },
18  ]
19 }
20 const setAllRes = await cl.setAll(setAllReq)
21 console.log('success: setAll', setAllRes)
22})()

Others

If you’re using another development language, please refer to the immugw option.

ExecAll#

ExecAll allows multiple insertions at once. The difference is that it is possible to specify a list of mixes of key/value sets, references and zAdd insertions. The argument of a ExecAll is an array of the following types:

  • Op_Kv: ordinary key value item
  • Op_ZAdd: ZAdd option element
  • Op_Ref: Reference option element

It’s possible to persist and reference items that are already persisted on disk. In that case is mandatory to provide the index of the referenced item. This has to be done for:

  • Op_ZAdd
  • Op_Ref If zAdd or reference is not yet persisted on disk it’s possible to add it as a regular key value and the reference is done only. In that case if BoundRef is true the reference is bounded to the current transaction values.

Go

 1package main
 2
 3import (
 4	"context"
 5	"encoding/json"
 6	"log"
 7
 8	"github.com/codenotary/immudb/pkg/api/schema"
 9	immudb "github.com/codenotary/immudb/pkg/client"
10)
11
12func main() {
13	opts := immudb.DefaultOptions().
14		WithAddress("localhost").
15		WithPort(3322)
16
17	client := immudb.NewClient().WithOptions(opts)
18	err := client.OpenSession(
19		context.TODO(),
20		[]byte(`immudb`),
21		[]byte(`immudb`),
22		"defaultdb",
23	)
24	if err != nil {
25		log.Fatal(err)
26	}
27
28	defer client.CloseSession(context.TODO())
29
30	idx, err := client.Set(
31		context.TODO(),
32		[]byte(`persistedKey`),
33		[]byte(`persistedVal`),
34	)
35	if err != nil {
36		log.Fatal(err)
37	}
38
39	aOps := &schema.ExecAllRequest{
40		Operations: []*schema.Op{
41			{
42				Operation: &schema.Op_Kv{
43					Kv: &schema.KeyValue{
44						Key:   []byte(`notPersistedKey`),
45						Value: []byte(`notPersistedVal`),
46					},
47				},
48			},
49			{
50				Operation: &schema.Op_ZAdd{
51					ZAdd: &schema.ZAddRequest{
52						Set:   []byte(`mySet`),
53						Score: 0.4,
54						Key:   []byte(`notPersistedKey`)},
55				},
56			},
57			{
58				Operation: &schema.Op_ZAdd{
59					ZAdd: &schema.ZAddRequest{
60						Set:      []byte(`mySet`),
61						Score:    0.6,
62						Key:      []byte(`persistedKey`),
63						AtTx:     idx.Id,
64						BoundRef: true,
65					},
66				},
67			},
68		},
69	}
70
71	idx, err = client.ExecAll(context.TODO(), aOps)
72	if err != nil {
73		log.Fatal(err)
74	}
75
76	list, err := client.ZScan(context.TODO(), &schema.ZScanRequest{
77		Set:     []byte(`mySet`),
78		SinceTx: idx.Id,
79		NoWait:  true,
80	})
81	if err != nil {
82		log.Fatal(err)
83	}
84	s, _ := json.MarshalIndent(list, "", "\t")
85	log.Print(string(s))
86}

Python

 1from immudb import ImmudbClient
 2from immudb.datatypes import KeyValue, ZAddRequest, ReferenceRequest
 3
 4URL = "localhost:3322"  # immudb running on your machine
 5LOGIN = "immudb"        # Default username
 6PASSWORD = "immudb"     # Default password
 7DB = b"defaultdb"       # Default database name (must be in bytes)
 8
 9def main():
10    client = ImmudbClient(URL)
11    client.login(LOGIN, PASSWORD, database = DB)
12
13    toExecute = [
14        KeyValue(b'key', b'value'), 
15        ZAddRequest(b'testscore', 100, b'key'),
16        KeyValue(b'key2', b'value2'), 
17        ZAddRequest(b'testscore', 150, b'key2'),
18        ReferenceRequest(b'reference1', b'key')
19    ]
20    info = client.execAll(toExecute)
21    print(info.id) # All in one transaction
22
23    print(client.zScan(b'testscore', b'', 0, 0, True, 10, True, 0, 200)) # Shows these entries
24    print(client.get(b'reference1'))
25
26if __name__ == "__main__":
27    main()

Java

This feature is not yet supported or not documented. Do you want to make a feature request or help out? Open an issue on Java sdk github project

Node.js

 1import ImmudbClient from 'immudb-node'
 2import Parameters from 'immudb-node/types/parameters'
 3
 4const IMMUDB_HOST = '127.0.0.1'
 5const IMMUDB_PORT = '3322'
 6const IMMUDB_USER = 'immudb'
 7const IMMUDB_PWD = 'immudb'
 8
 9const cl = new ImmudbClient({ host: IMMUDB_HOST, port: IMMUDB_PORT });
10
11(async () => {
12 await cl.login({ user: IMMUDB_USER, password: IMMUDB_PWD })
13
14 const { id } = await cl.set({ key: 'persistedKey', value: 'persistedVal' })
15
16 const setOperation = { kv: { key: 'notPersistedKey', value: 'notPersistedVal' } }
17 const zAddOperation = {
18  zadd: {
19   set: 'mySet',
20   score: 0.6,
21   key: 'notPersistedKey',
22   attx: 0,
23   boundref: true
24  }
25 }
26 const zAddOperation1 = {
27  zadd: {
28   set: 'mySet',
29   score: 0.6,
30   key: 'persistedKey',
31   attx: id,
32   boundref: true
33  }
34 }
35 const execAllReq: Parameters.ExecAll = {
36  operationsList: [
37   setOperation,
38   zAddOperation,
39   zAddOperation1,
40  ]
41 }
42 const execAllRes = await cl.execAll(execAllReq)
43 console.log('success: execAll', execAllRes)
44})()

Others

If you’re using another development language, please refer to the immugw option.

TxScan#

TxScan permits iterating over transactions.

The argument of a TxScan is an array of the following types:

  • InitialTx: initial transaction id
  • Limit: number of transactions returned
  • Desc: order of returned transacations

Go

 1package main
 2
 3import (
 4	"context"
 5	"log"
 6
 7	"github.com/codenotary/immudb/pkg/api/schema"
 8	immudb "github.com/codenotary/immudb/pkg/client"
 9)
10
11func main() {
12	opts := immudb.DefaultOptions().
13		WithAddress("localhost").
14		WithPort(3322)
15
16	client := immudb.NewClient().WithOptions(opts)
17	err := client.OpenSession(
18		context.TODO(),
19		[]byte(`immudb`),
20		[]byte(`immudb`),
21		"defaultdb",
22	)
23	if err != nil {
24		log.Fatal(err)
25	}
26
27	defer client.CloseSession(context.TODO())
28
29	tx, err := client.Set(
30		context.TODO(),
31		[]byte("key1"),
32		[]byte("val1"),
33	)
34	if err != nil {
35		log.Fatal(err)
36	}
37	_, err = client.Set(
38		context.TODO(),
39		[]byte("key2"),
40		[]byte("val2"),
41	)
42	if err != nil {
43		log.Fatal(err)
44	}
45	_, err = client.Set(
46		context.TODO(),
47		[]byte("key3"),
48		[]byte("val3"),
49	)
50	if err != nil {
51		log.Fatal(err)
52	}
53
54	txs, err := client.TxScan(context.TODO(), &schema.TxScanRequest{
55		InitialTx: tx.Id,
56		Limit:     3,
57		Desc:      true,
58	})
59	if err != nil {
60		log.Fatal(err)
61	}
62
63	// Then it's possible to retrieve entries of every transactions:
64	for _, tx := range txs.GetTxs() {
65		for _, entry := range tx.Entries {
66			item, err := client.GetAt(
67				context.TODO(),
68				entry.Key[1:],
69				tx.Header.Id,
70			)
71			if err != nil {
72				item, err = client.GetAt(
73					context.TODO(),
74					entry.Key,
75					tx.Header.Id,
76				)
77				if err != nil {
78					log.Fatal(err)
79				}
80			}
81			log.Printf("retrieved key %s and val %s\n", item.Key, item.Value)
82		}
83	}
84}

Remember to strip the first byte in the key (key prefix). Remember that a transaction could contain sorted sets keys that should not be skipped.

Python

This feature is not yet supported or not documented. Do you want to make a feature request or help out? Open an issue on Python sdk github project

Java

 1package io.codenotary.immudb.helloworld;
 2
 3import java.util.List;
 4
 5import io.codenotary.immudb4j.FileImmuStateHolder;
 6import io.codenotary.immudb4j.ImmuClient;
 7import io.codenotary.immudb4j.Tx;
 8import io.codenotary.immudb4j.TxHeader;
 9
10public class App {
11
12    public static void main(String[] args) {
13
14        ImmuClient client = null;
15
16        try {
17
18            FileImmuStateHolder stateHolder = FileImmuStateHolder.newBuilder()
19                    .withStatesFolder("./immudb_states")
20                    .build();
21
22            client = ImmuClient.newBuilder()
23                    .withServerUrl("127.0.0.1")
24                    .withServerPort(3322)
25                    .withStateHolder(stateHolder)
26                    .build();
27
28            client.openSession("defaultdb", "immudb", "immudb");
29
30            byte[] value1 = { 0, 1, 2, 3 };
31            byte[] value2 = { 4, 5, 6, 7 };
32
33            TxHeader hdr = client.set("key1", value1);
34            client.set("key2", value2);
35
36            List<Tx> txs = client.txScanAll(hdr.getId());
37
38            for (Tx tx : txs) {
39                System.out.format("tx '%d'\n", tx.getHeader().getId());
40            }
41
42            client.closeSession();
43
44        } catch (Exception e) {
45            e.printStackTrace();
46        } finally {
47            if (client != null) {
48                try {
49                    client.shutdown();
50                } catch (InterruptedException e) {
51                    e.printStackTrace();
52                }
53            }
54        }
55
56    }
57
58}

Node.js

 1import ImmudbClient from 'immudb-node'
 2import Parameters from 'immudb-node/types/parameters'
 3
 4const IMMUDB_HOST = '127.0.0.1'
 5const IMMUDB_PORT = '3322'
 6const IMMUDB_USER = 'immudb'
 7const IMMUDB_PWD = 'immudb'
 8
 9const cl = new ImmudbClient({ host: IMMUDB_HOST, port: IMMUDB_PORT });
10
11(async () => {
12 await cl.login({ user: IMMUDB_USER, password: IMMUDB_PWD })
13
14 for (let i = 0; i < 3; i++) {
15  await cl.set({ key: `key${i}`, value: `val${i}` })
16 }
17
18 const txScanReq: Parameters.TxScan = {
19  initialtx: 2,
20    limit: 3,
21    desc: false
22 }
23 const txScanRes = await cl.txScan(txScanReq)
24 console.log('success: txScan', txScanRes)
25})()

Others

If you’re using another development language, please refer to the immugw option.

Filter Transactions#

The transaction entries are generated by writing key-value pairs, referencing keys, associating scores to key-value pairs (with ZAdd operation), and by mapping SQL data model into key-value model.

With TxScan or TxByIDWithSpec operations it’s possible to retrieve entries of certain types, either retrieving the digest of the value assigned to the key (EntryTypeAction_ONLY_DIGEST), the raw value (EntryTypeAction_RAW_VALUE) or the structured value (EntryTypeAction_RESOLVE).

Go

  1package main
  2
  3import (
  4	"context"
  5	"log"
  6
  7	"github.com/codenotary/immudb/pkg/api/schema"
  8	immudb "github.com/codenotary/immudb/pkg/client"
  9)
 10
 11func main() {
 12	opts := immudb.DefaultOptions().
 13		WithAddress("localhost").
 14		WithPort(3322)
 15
 16	client := immudb.NewClient().WithOptions(opts)
 17	err := client.OpenSession(
 18		context.TODO(),
 19		[]byte(`immudb`),
 20		[]byte(`immudb`),
 21		"defaultdb",
 22	)
 23	if err != nil {
 24		log.Fatal(err)
 25	}
 26
 27	defer client.CloseSession(context.TODO())
 28
 29	hdr, err := client.ExecAll(
 30		context.TODO(),
 31		&schema.ExecAllRequest{
 32			Operations: []*schema.Op{
 33				{
 34					Operation: &schema.Op_Kv{
 35						Kv: &schema.KeyValue{
 36							Key:   []byte("key1"),
 37							Value: []byte("value1"),
 38						},
 39					},
 40				},
 41				{
 42					Operation: &schema.Op_Ref{
 43						Ref: &schema.ReferenceRequest{
 44							Key:           []byte("ref1"),
 45							ReferencedKey: []byte("key1"),
 46						},
 47					},
 48				},
 49				{
 50					Operation: &schema.Op_ZAdd{
 51						ZAdd: &schema.ZAddRequest{
 52							Set:   []byte("set1"),
 53							Score: 10,
 54							Key:   []byte("key1"),
 55						},
 56					},
 57				},
 58			},
 59		},
 60	)
 61	if err != nil {
 62		log.Fatal(err)
 63	}
 64
 65	// fetch kv and sorted-set entries as structured values
 66	// while skipping sql-related entries
 67	tx, err := client.TxByIDWithSpec(
 68		context.TODO(),
 69		&schema.TxRequest{
 70			Tx: hdr.Id,
 71			EntriesSpec: &schema.EntriesSpec{
 72				KvEntriesSpec: &schema.EntryTypeSpec{
 73					Action: schema.EntryTypeAction_RESOLVE,
 74				},
 75				ZEntriesSpec: &schema.EntryTypeSpec{
 76					Action: schema.EntryTypeAction_RESOLVE,
 77				},
 78				// explicit exclusion is optional
 79				SqlEntriesSpec: &schema.EntryTypeSpec{
 80					// resolution of sql entries is not supported
 81					Action: schema.EntryTypeAction_EXCLUDE,
 82				},
 83			},
 84		},
 85	)
 86	if err != nil {
 87		log.Fatal(err)
 88	}
 89
 90	for _, entry := range tx.KvEntries {
 91		log.Printf(
 92			"retrieved key %s and val %s",
 93			entry.Key,
 94			entry.Value,
 95		)
 96	}
 97
 98	for _, entry := range tx.ZEntries {
 99		log.Printf(
100			"retrieved set %s key %s and score %v",
101			entry.Set,
102			entry.Key,
103			entry.Score,
104		)
105	}
106
107	// scan over unresolved entries
108	// either EntryTypeAction_ONLY_DIGEST or
109	// EntryTypeAction_RAW_VALUE options
110	for _, entry := range tx.Entries {
111		log.Printf(
112			"retrieved key %s and digest %v",
113			entry.Key,
114			entry.HValue,
115		)
116	}
117}

Python

This feature is not yet supported or not documented. Do you want to make a feature request or help out? Open an issue on Python sdk github project

Java

This feature is not yet supported or not documented. Do you want to make a feature request or help out? Open an issue on Java sdk github project

Node.js

This feature is not yet supported or not documented. Do you want to make a feature request or help out? Open an issue on Node.js sdk github project

Others

If you’re using another development language, please refer to the immugw option.

Edit this page on GitHub Last updated