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

Reading

Tip

Examples in multiple languages can be found at following links: immudb SDKs examples

Most of the methods in SDKs have Verified equivalent, i.e. Get and VerifiedGet. The only difference is that with Verified methods proofs needed to mathematically verify that the data was not tampered are returned by the server and the verification is done automatically by SDKs. Note that generating that proof has a slight performance impact, so primitives are allowed without the proof. It is still possible to get the proofs for a specific item at any time, so the decision about when or how frequently to do checks (with the Verify version of a method) is completely up to the user. It’s possible also to use dedicated auditors to ensure the database consistency, but the pattern in which every client is also an auditor is the more interesting one.

Get and Set#

Get/VerifiedGet and Set/VerifiedSet methods allow for basic operations on a Key Value level. In addition, GetAll and SetAll methods allow for adding and reading in a single transaction. See transactions chapter for more details.

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	// Without verification
30	tx, err := client.Set(
31		context.TODO(),
32		[]byte(`x`),
33		[]byte(`y`),
34	)
35	if err != nil {
36		log.Fatal(err)
37	}
38	log.Printf("Set: tx: %d", tx.Id)
39
40	entry, err := client.Get(
41		context.TODO(),
42		[]byte(`x`),
43	)
44	if err != nil {
45		log.Fatal(err)
46	}
47	log.Printf("Get: %v", entry)
48
49	tx, err = client.SetAll(context.TODO(), &schema.SetRequest{
50		KVs: []*schema.KeyValue{
51			{Key: []byte(`1`), Value: []byte(`test1`)},
52			{Key: []byte(`2`), Value: []byte(`test2`)},
53			{Key: []byte(`3`), Value: []byte(`test3`)},
54		},
55	})
56	if err != nil {
57		log.Fatal(err)
58	}
59	log.Printf("SetAll: tx: %d", tx.Id)
60
61	entries, err := client.GetAll(
62		context.TODO(),
63		[][]byte{
64			[]byte(`1`),
65			[]byte(`2`),
66			[]byte(`3`),
67		},
68	)
69	if err != nil {
70		log.Fatal(err)
71	}
72	log.Printf("GetAll: %+v", entries)
73
74	// With verification
75	tx, err = client.VerifiedSet(
76		context.TODO(),
77		[]byte(`xx`),
78		[]byte(`yy`),
79	)
80	if err != nil {
81		log.Fatal(err)
82	}
83	log.Printf("VerifiedSet: tx: %d", tx.Id)
84
85	entry, err = client.Get(
86		context.TODO(),
87		[]byte(`xx`),
88	)
89	if err != nil {
90		log.Fatal(err)
91	}
92	log.Printf("VerifiedGet: %v", entry)
93}

Python

 1from immudb import ImmudbClient
 2import json
 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 encode(what: str):
10    return what.encode("utf-8")
11
12def decode(what: bytes):
13    return what.decode("utf-8")
14
15def main():
16    client = ImmudbClient(URL)
17    client.login(LOGIN, PASSWORD, database = DB)
18    
19    # You have to operate on bytes
20    setResult = client.set(b'x', b'y')
21    print(setResult)            # immudb.datatypes.SetResponse
22    print(setResult.id)         # id of transaction
23    print(setResult.verified)   # in this case verified = False
24                                # see Tamperproof reading and writing
25
26    # Also you get response in bytes
27    retrieved = client.get(b'x')
28    print(retrieved)        # immudb.datatypes.GetResponse
29    print(retrieved.key)    # Value is b'x'
30    print(retrieved.value)  # Value is b'y'
31    print(retrieved.tx)     # Transaction number
32
33    print(type(retrieved.key))      # <class 'bytes'>
34    print(type(retrieved.value))    # <class 'bytes'>
35
36    # Operating with strings
37    encodedHello = encode("Hello")
38    encodedImmutable = encode("Immutable")
39    client.set(encodedHello, encodedImmutable)
40    retrieved = client.get(encodedHello)
41
42    print(decode(retrieved.value) == "Immutable")   # Value is True
43
44    notExisting = client.get(b'asdasd')
45    print(notExisting)                              # Value is None
46
47    # JSON example
48    toSet = {"hello": "immutable"}
49    encodedToSet = encode(json.dumps(toSet))
50    client.set(encodedHello, encodedToSet)
51
52    retrieved = json.loads(decode(client.get(encodedHello).value))
53    print(retrieved)    # Value is {"hello": "immutable"}
54
55    # setAll example - sets all keys to value from dictionary
56    toSet = {
57        b'1': b'test1',
58        b'2': b'test2',
59        b'3': b'test3'
60    }
61
62    client.setAll(toSet)
63    retrieved = client.getAll(list(toSet.keys()))
64    print(retrieved) 
65    # Value is {b'1': b'test1', b'2': b'test2', b'3': b'test3'}
66
67if __name__ == "__main__":
68    main()

Java

 1package io.codenotary.immudb.helloworld;
 2
 3import io.codenotary.immudb4j.Entry;
 4import io.codenotary.immudb4j.FileImmuStateHolder;
 5import io.codenotary.immudb4j.ImmuClient;
 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            client.set("myKey", "myValue".getBytes());
28
29            Entry entry = client.get("myKey");
30
31            byte[] value = entry.getValue();
32
33            System.out.format("('%s', '%s')\n", "myKey", new String(value));
34
35            client.closeSession();
36
37        } catch (Exception e) {
38            e.printStackTrace();
39        } finally {
40            if (client != null) {
41                try {
42                    client.shutdown();
43                } catch (InterruptedException e) {
44                    e.printStackTrace();
45                }
46            }
47        }
48
49    }
50
51}

Note that value is a primitive byte array. You can set the value of a String using:
"some string".getBytes(StandardCharsets.UTF_8)

Also, set method is overloaded to allow receiving the key parameter as a byte[] data type.

.NET

1var client = new ImmuClient();
2await client.Open("immudb", "immudb", "defaultdb");
3
4await client.Set("k123", "v123");
5string v = await client.Get("k123").ToString();
6
7await client.Close();

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 setReq: Parameters.Set = { key: 'hello', value: 'world' }
15    const setRes = await cl.set(setReq)
16    console.log('success: set', setRes)
17
18    const getReq: Parameters.Get = { key: 'hello' }
19    const getRes = await cl.get(getReq)
20    console.log('success: get', getRes)
21})()

Others

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

Get at and since a transaction#

You can retrieve a key on a specific transaction with GetAt/VerifiedGetAt. If you need to check the last value of a key after given transaction (which represent state of the indexer), you can use GetSince/VerifiedGetSince.

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	key := []byte(`123123`)
 29	var txIDs []uint64
 30	for _, v := range [][]byte{
 31		[]byte(`111`),
 32		[]byte(`222`),
 33		[]byte(`333`),
 34	} {
 35		txID, err := client.Set(
 36			context.TODO(),
 37			key,
 38			v,
 39		)
 40		if err != nil {
 41			log.Fatal(err)
 42		}
 43		txIDs = append(txIDs, txID.Id)
 44	}
 45
 46	otherTxID, err := client.Set(
 47		context.TODO(),
 48		[]byte(`other`),
 49		[]byte(`other`),
 50	)
 51	if err != nil {
 52		log.Fatal(err)
 53	}
 54
 55	// Without verification
 56	entry, err := client.GetSince(
 57		context.TODO(),
 58		key,
 59		txIDs[0],
 60	)
 61	if err != nil {
 62		log.Fatal(err)
 63	}
 64	log.Printf("GetSince first: %+v", entry)
 65
 66	// With verification
 67	entry, err = client.VerifiedGetSince(
 68		context.TODO(),
 69		key,
 70		txIDs[0]+1,
 71	)
 72	if err != nil {
 73		log.Fatal(err)
 74	}
 75	log.Printf("VerifiedGetSince second: %+v", entry)
 76
 77	// GetAt txID after inserting other data
 78	_, err = client.GetAt(
 79		context.TODO(),
 80		key,
 81		otherTxID.Id,
 82	)
 83	if err == nil {
 84		log.Fatalf("This should not happen, %+v", entry)
 85	}
 86
 87	// Without verification
 88	entry, err = client.GetAt(
 89		context.TODO(),
 90		key,
 91		txIDs[1],
 92	)
 93	if err != nil {
 94		log.Fatal(err)
 95	}
 96	log.Printf("GetAt second: %+v", entry)
 97
 98	// With verification
 99	entry, err = client.VerifiedGetAt(
100		context.TODO(),
101		key,
102		txIDs[2],
103	)
104	if err != nil {
105		log.Fatal(err)
106	}
107	log.Printf("VerifiedGetAt third: %+v", entry)
108
109	// VerifiedGetAt txID after inserting other data
110	entry, err = client.VerifiedGetAt(
111		context.TODO(),
112		key,
113		otherTxID.Id,
114	)
115	if err == nil {
116		log.Fatalf("This should not happen, %+v", entry)
117	}
118}

Python

 1from grpc import RpcError
 2from immudb import ImmudbClient
 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    first = client.set(b'justfirsttransaction', b'justfirsttransaction')
13
14    key = b'123123'
15
16    first = client.set(key, b'111')
17    firstTransaction = first.id
18
19    second = client.set(key, b'222')
20    secondTransaction = second.id
21
22    third = client.set(key, b'333')
23    thirdTransaction = third.id
24
25    print(client.verifiedGetSince(key, firstTransaction))   # b"111"
26    print(client.verifiedGetSince(key, firstTransaction + 1))   # b"222"
27
28    try:
29        # This key wasn't set on this transaction
30        print(client.verifiedGetAt(key, firstTransaction - 1))
31    except RpcError as exception:
32        print(exception.debug_error_string())
33        print(exception.details())
34
35    verifiedFirst = client.verifiedGetAt(key, firstTransaction) 
36                                    # immudb.datatypes.SafeGetResponse
37    print(verifiedFirst.id)         # id of transaction
38    print(verifiedFirst.key)        # Key that was modified
39    print(verifiedFirst.value)      # Value after this transaction
40    print(verifiedFirst.refkey)     # Reference key
41									# (Queries And History -> setReference)
42    print(verifiedFirst.verified)   # Response is verified or not
43    print(verifiedFirst.timestamp)  # Time of this transaction
44
45    print(client.verifiedGetAt(key, secondTransaction))
46    print(client.verifiedGetAt(key, thirdTransaction))
47
48    try:
49        # Transaction doesn't exists yet
50        print(client.verifiedGetAt(key, thirdTransaction + 1))
51    except RpcError as exception:
52        print(exception.debug_error_string())
53        print(exception.details())
54
55if __name__ == "__main__":
56    main()

Java

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

.NET

1var client = new ImmuClient();
2await client.Open("immudb", "immudb", "defaultdb");
3
4byte[] v2 = new byte[] { 0, 1, 2, 3 };
5
6TxHeader hdr2 = await client.VerifiedSet("k2", v2);
7Entry ventry2 = await client.VerifiedGet("k2");
8Entry e = await client.GetSinceTx("k2", hdr2.Id);
9Console.WriteLine(e.ToString());

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    const { id } = await cl.set({ key: 'key', value: 'value' })
14
15    const verifiedGetAtReq: Parameters.VerifiedGetAt = {
16        key: 'key',
17        attx: id
18    }
19    const verifiedGetAtRes = await cl.verifiedGetAt(verifiedGetAtReq)
20    console.log('success: verifiedGetAt', verifiedGetAtRes)
21
22    for (let i = 0; i < 4; i++) {
23        await cl.set({ key: 'key', value: `value-${i}` })
24    }
25
26    const verifiedGetSinceReq: Parameters.VerifiedGetSince = {
27        key: 'key',
28        sincetx: 2
29    }
30    const verifiedGetSinceRes = await cl.verifiedGetSince(verifiedGetSinceReq)
31    console.log('success: verifiedGetSince', verifiedGetSinceRes)
32})()

Others

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

Get at revision#

Each historical value for a single key is attached a revision number. Revision numbers start with 1 and each overwrite of the same key results in a new sequential revision number assignment.

A negative revision number can also be specified which means the nth historical value, e.g. -1 is the previous value, -2 is the one before and so on.

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	// Use dedicated API call
29	entry, err := client.GetAtRevision(
30		context.TODO(),
31		[]byte("key"),
32		-1,
33	)
34	if err != nil {
35		log.Fatal(err)
36	}
37	log.Printf(
38		"Retrieved entry at revision %d: %s",
39		entry.Revision,
40		string(entry.Value),
41	)
42
43	// Use additional get option
44	entry, err = client.Get(
45		context.TODO(),
46		[]byte("key"),
47		immudb.AtRevision(-2),
48	)
49	if err != nil {
50		log.Fatal(err)
51	}
52	log.Printf(
53		"Retrieved entry at revision %d: %s",
54		entry.Revision,
55		string(entry.Value),
56	)
57}

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.nio.charset.StandardCharsets;
 4import java.util.Arrays;
 5
 6import io.codenotary.immudb4j.Entry;
 7import io.codenotary.immudb4j.FileImmuStateHolder;
 8import io.codenotary.immudb4j.ImmuClient;
 9import io.codenotary.immudb4j.TxHeader;
10
11public class App {
12
13    public static void main(String[] args) {
14
15        ImmuClient client = null;
16
17        try {
18
19            FileImmuStateHolder stateHolder = FileImmuStateHolder.newBuilder()
20                    .withStatesFolder("./immudb_states")
21                    .build();
22
23            client = ImmuClient.newBuilder()
24                    .withServerUrl("127.0.0.1")
25                    .withServerPort(3322)
26                    .withStateHolder(stateHolder)
27                    .build();
28
29            client.openSession("defaultdb", "immudb", "immudb");
30
31            byte[] key = "myKey1".getBytes(StandardCharsets.UTF_8);
32            byte[] value1 = new byte[]{1, 2, 3, 4, 5};
33            byte[] value2 = new byte[]{5, 4, 3, 2, 1};
34
35            client.set(key, value1);
36            client.set(key, value2);
37
38            Entry entry1 = client.getAtRevision(key, 1);
39            Entry entry2 = client.getAtRevision(key, 2);
40
41            System.out.format("('%s', '%s')@rev%d\n", new String(key), Arrays.toString(entry1.getValue()), 1);
42            System.out.format("('%s', '%s')@rev%d\n", new String(key), Arrays.toString(entry2.getValue()), 2);
43
44            client.closeSession();
45
46        } catch (Exception e) {
47            e.printStackTrace();
48        } finally {
49            if (client != null) {
50                try {
51                    client.shutdown();
52                } catch (InterruptedException e) {
53                    e.printStackTrace();
54                }
55            }
56        }
57
58    }
59
60}

.NET

 1var client = new ImmuClient();
 2await client.Open("immudb", "immudb", "defaultdb");
 3
 4string key = "hello";
 5
 6try
 7{
 8    await client.VerifiedSet(key, "immutable world!");
 9    Entry entry1 = await client.VerifiedGetAtRevision(key, 0);
10    Console.WriteLine(entry1.ToString());
11    await client.VerifiedSet(key, "immutable world again!");
12    Entry entry2 = await client.VerifiedGetAtRevision(key, -1);
13    Console.WriteLine(entry2.ToString());
14}
15catch (VerificationException e)
16{
17    // VerificationException means Data Tampering detected!
18    // This means the history of changes has been tampered.
19    Console.WriteLine(e.ToString());
20}
21await client.Close();

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.

Get at TXID#

It’s possible to retrieve all the keys inside a specific transaction.

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	setTxFirst, err := client.SetAll(context.TODO(),
30		&schema.SetRequest{KVs: []*schema.KeyValue{
31			{Key: []byte("key1"), Value: []byte("val1")},
32			{Key: []byte("key2"), Value: []byte("val2")},
33		}})
34	if err != nil {
35		log.Fatal(err)
36	}
37	log.Printf("First txID: %d", setTxFirst.Id)
38
39	// Set keys in another transaction
40	setTxSecond, err := client.SetAll(
41		context.TODO(),
42		&schema.SetRequest{KVs: []*schema.KeyValue{
43			{Key: []byte("key1"), Value: []byte("val11")},
44			{Key: []byte("key2"), Value: []byte("val22")},
45		}})
46	if err != nil {
47		log.Fatal(err)
48	}
49	log.Printf("Second txID: %d", setTxSecond.Id)
50
51	// Without verification
52	tx, err := client.TxByID(
53		context.TODO(),
54		setTxFirst.Id,
55	)
56	if err != nil {
57		log.Fatal(err)
58	}
59
60	for _, entry := range tx.Entries {
61		item, err := client.GetAt(
62			context.TODO(),
63			entry.Key,
64			setTxFirst.Id,
65		)
66		if err != nil {
67			log.Fatal(err)
68		}
69		log.Printf("retrieved: %+v", item)
70	}
71
72	// With verification
73	tx, err = client.VerifiedTxByID(
74		context.TODO(),
75		setTxSecond.Id,
76	)
77	if err != nil {
78		log.Fatal(err)
79	}
80
81	for _, entry := range tx.Entries {
82		item, err := client.VerifiedGetAt(
83			context.TODO(),
84			entry.Key,
85			setTxSecond.Id,
86		)
87		if err != nil {
88			log.Fatal(err)
89		}
90		log.Printf("retrieved: %+v", item)
91	}
92}

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    keyFirst = b'333'
13    keySecond = b'555'
14
15    first = client.set(keyFirst, b'111')
16    firstTransaction = first.id
17
18    second = client.set(keySecond, b'222')
19    secondTransaction = second.id
20
21    toSet = {
22        b'1': b'test1',
23        b'2': b'test2',
24        b'3': b'test3'
25    }
26
27    third = client.setAll(toSet)
28    thirdTransaction = third.id
29
30    keysAtFirst = client.txById(firstTransaction)
31    keysAtSecond = client.txById(secondTransaction)
32    keysAtThird = client.txById(thirdTransaction)
33
34    print(keysAtFirst)  # [b'333']
35    print(keysAtSecond) # [b'555']
36    print(keysAtThird)  # [b'1', b'2', b'3']
37
38if __name__ == "__main__":
39    main()

Java

 1package io.codenotary.immudb.helloworld;
 2
 3import java.nio.charset.StandardCharsets;
 4import java.util.Arrays;
 5
 6import io.codenotary.immudb4j.TxEntry;
 7import io.codenotary.immudb4j.Entry;
 8import io.codenotary.immudb4j.FileImmuStateHolder;
 9import io.codenotary.immudb4j.ImmuClient;
10import io.codenotary.immudb4j.KVListBuilder;
11import io.codenotary.immudb4j.Tx;
12import io.codenotary.immudb4j.TxHeader;
13
14public class App {
15
16    public static void main(String[] args) {
17
18        ImmuClient client = null;
19
20        try {
21
22            FileImmuStateHolder stateHolder = FileImmuStateHolder.newBuilder()
23                    .withStatesFolder("./immudb_states")
24                    .build();
25
26            client = ImmuClient.newBuilder()
27                    .withServerUrl("127.0.0.1")
28                    .withServerPort(3322)
29                    .withStateHolder(stateHolder)
30                    .build();
31
32            client.openSession("defaultdb", "immudb", "immudb");
33
34            byte[] key1 = "myKey1".getBytes(StandardCharsets.UTF_8);
35            byte[] value1 = new byte[]{1, 2, 3};
36
37            byte[] key2 = "myKey2".getBytes(StandardCharsets.UTF_8);
38            byte[] value2 = new byte[]{4, 5, 6};
39
40            KVListBuilder kvListBuilder = KVListBuilder.newBuilder().
41                add(key1, value1).
42                add(key2, value2);
43
44            TxHeader hdr = client.setAll(kvListBuilder.entries());
45
46            Tx tx = client.txById(hdr.getId());
47
48            for (TxEntry txEntry : tx.getEntries()) {
49                System.out.format("'%s'\n", new String(txEntry.getKey()));
50            }
51            
52            client.closeSession();
53
54        } catch (Exception e) {
55            e.printStackTrace();
56        } finally {
57            if (client != null) {
58                try {
59                    client.shutdown();
60                } catch (InterruptedException e) {
61                    e.printStackTrace();
62                }
63            }
64        }
65
66    }
67
68}

.NET

 1
 2var client = new ImmuClient();
 3await client.Open("immudb", "immudb", "defaultdb");
 4
 5TxMetadata txMd = null;
 6try 
 7{
 8    txMd = immuClient.VerifiedSet(key, val);
 9}
10catch (VerificationException e) 
11{
12    Console.WriteLine("A VerificationException occurred.")
13}
14try 
15{
16    Tx tx = immuClient.TxById(txMd.id);
17} 
18catch (Exception e) 
19{
20    Console.WriteLine("An exception occurred.")
21}
22
23await client.Close();

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    const { id } = await cl.set({ key: 'key', value: 'value' })
14
15    const txByIdReq: Parameters.TxById = { tx: id }
16    const txByIdRes = await cl.txById(txByIdReq)
17    console.log('success: txById', txByIdRes)
18})()

Others

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

Conditional writes#

immudb can check additional preconditions before the write operation is made. Precondition is checked atomically with the write operation. It can be then used to ensure consistent state of data inside the database.

Following preconditions are supported:

  • MustExist - precondition checks if given key exists in the database, this precondition takes into consideration logical deletion and data expiration, if the entry was logically deleted or has expired, MustExist precondition for such entry will fail
  • MustNotExist - precondition checks if given key does not exist in the database, this precondition also takes into consideration logical deletion and data expiration, if the entry was logically deleted or has expired, MustNotExist precondition for such entry will succeed
  • NotModifiedAfterTX - precondition checks if given key was not modified after given transaction id, local deletion and setting entry with expiration data is also considered modification of the entry

In many cases, keys used for constraints will be the same as keys for written entries. A good example here is a situation when a value is set only if that key does not exist. This is not strictly required - keys used in constraints do not have to be the same or even overlap with keys for modified entries. An example would be if only one of two keys should exist in the database. In such case, the first key will be modified and the second key will be used for MustNotExist constraint.

A write operation using precondition can not be done in an asynchronous way. Preconditions are checked twice when processing such requests - first check is done against the current state of internal index, the second check is done just before persisting the write and requires up-to-date index.

Preconditions are available on SetAll, Reference and ExecAll operations.

Go

In go sdk, the schema package contains convenient wrappers for creating constraint objects, such as schema.PreconditionKeyMustNotExist.

  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	immuerrors "github.com/codenotary/immudb/pkg/client/errors"
 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	_, err = client.Set(context.TODO(), []byte("key"), []byte("value"))
 31	if err != nil {
 32		log.Fatal(err)
 33	}
 34
 35	// ensure modification is done atomically when there are concurrent writers
 36
 37	entry, err := client.Get(context.TODO(), []byte("key"))
 38	if err != nil {
 39		log.Fatal(err)
 40	}
 41
 42	_, err = client.SetAll(
 43		context.TODO(),
 44		&schema.SetRequest{
 45			KVs: []*schema.KeyValue{{
 46				Key:   []byte("key"),
 47				Value: []byte("value2"),
 48			}},
 49			Preconditions: []*schema.Precondition{
 50				schema.PreconditionKeyNotModifiedAfterTX(
 51					[]byte("key"),
 52					entry.Tx,
 53				),
 54			},
 55		},
 56	)
 57	if err != nil {
 58		log.Fatal(err)
 59	}
 60
 61	// allow setting the key only once
 62
 63	_, err = client.SetAll(context.TODO(), &schema.SetRequest{
 64		KVs: []*schema.KeyValue{
 65			{Key: []byte("key-once"), Value: []byte("val")},
 66		},
 67		Preconditions: []*schema.Precondition{
 68			schema.PreconditionKeyMustNotExist([]byte("key-once")),
 69		},
 70	})
 71	if err != nil {
 72		log.Fatal(err)
 73	}
 74
 75	// set only one key in a group of keys
 76
 77	_, err = client.SetAll(context.TODO(), &schema.SetRequest{
 78		KVs: []*schema.KeyValue{
 79			{Key: []byte("key-group-1"), Value: []byte("val1")},
 80		},
 81		Preconditions: []*schema.Precondition{
 82			schema.PreconditionKeyMustNotExist([]byte("key-group-2")),
 83			schema.PreconditionKeyMustNotExist([]byte("key-group-3")),
 84			schema.PreconditionKeyMustNotExist([]byte("key-group-4")),
 85		},
 86	})
 87	if err != nil {
 88		log.Fatal(err)
 89	}
 90
 91	// check if returned error indicates precondition failure
 92
 93	_, err = client.SetAll(context.TODO(), &schema.SetRequest{
 94		KVs: []*schema.KeyValue{
 95			{Key: []byte("key-missing"), Value: []byte("val")},
 96		},
 97		Preconditions: []*schema.Precondition{
 98			schema.PreconditionKeyMustExist([]byte("key-missing")),
 99		},
100	})
101	immuErr := immuerrors.FromError(err)
102	if immuErr != nil &&
103		immuErr.Code() == immuerrors.CodIntegrityConstraintViolation {
104		log.Println("Constraint validation failed")
105	}
106
107}

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

.NET

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