Categorygithub.com/andrewkav/go-tarantool
modulepackage
0.0.0-20190330192518-aaa93c4bdc35
Repository: https://github.com/andrewkav/go-tarantool.git
Documentation: pkg.go.dev

# README

Client in Go for Tarantool 1.6+

The go-tarantool package has everything necessary for interfacing with Tarantool 1.6+.

The advantage of integrating Go with Tarantool, which is an application server plus a DBMS, is that Go programmers can handle databases and perform on-the-fly recompilations of embedded Lua routines, just as in C, with responses that are faster than other packages according to public benchmarks.

Table of contents

Installation

We assume that you have Tarantool version 1.6 and a modern Linux or BSD operating system.

You will need a current version of go, version 1.3 or later (use go version to check the version number). Do not use gccgo-go.

Note: If your go version is younger than 1.3, or if go is not installed, download the latest tarball from golang.org and say:

$ sudo tar -C /usr/local -xzf go1.7.5.linux-amd64.tar.gz
$ export PATH=$PATH:/usr/local/go/bin
$ export GOPATH="/usr/local/go/go-tarantool"
$ sudo chmod -R a+rwx /usr/local/go </pre>

The go-tarantool package is in tarantool/go-tarantool repository. To download and install, say:

$ go get github.com/tarantool/go-tarantool

This should bring source and binary files into subdirectories of /usr/local/go, making it possible to access by adding github.com/tarantool/go-tarantool in the import {...} section at the start of any Go program.

Hello World

In the "Connectors" chapter of the Tarantool manual, there is an explanation of a very short (18-line) program written in Go. Follow the instructions at the start of the "Connectors" chapter carefully. Then cut and paste the example into a file named example.go, and run it. You should see: nothing.

If that is what you see, then you have successfully installed go-tarantool and successfully executed a program that manipulated the contents of a Tarantool database.

API reference

Read the Tarantool manual to find descriptions of terms like "connect", "space", "index", and the requests for creating and manipulating database objects or Lua functions.

The source files for the requests library are:

  • connection.go for the Connect() function plus functions related to connecting, and
  • request.go for data-manipulation functions and Lua invocations.

See comments in those files for syntax details:

Ping
closeConnection
Select
Insert
Replace
Delete
Update
Upsert
Call
Call17
Eval

The supported requests have parameters and results equivalent to requests in the Tarantool manual. There are also Typed and Async versions of each data-manipulation function.

The source file for error-handling tools is errors.go, which has structure definitions and constants whose names are equivalent to names of errors that the Tarantool server returns.

Walking-through example in Go

We can now have a closer look at the example.go program and make some observations about what it does.

package main

import (
     "fmt"
     "github.com/tarantool/go-tarantool"
)

func main() {
   opts := tarantool.Opts{User: "guest"}
   conn, err := tarantool.Connect("127.0.0.1:3301", opts)
   // conn, err := tarantool.Connect("/path/to/tarantool.socket", opts)
   if err != nil {
       fmt.Println("Connection refused:", err)
   }
   resp, err := conn.Insert(999, []interface{}{99999, "BB"})
   if err != nil {
     fmt.Println("Error", err)
     fmt.Println("Code", resp.Code)
   }
}

Observation 1: the line "github.com/tarantool/go-tarantool" in the import(...) section brings in all Tarantool-related functions and structures.

Observation 2: the line beginning with "Opts :=" sets up the options for Connect(). In this example, there is only one thing in the structure, a user name. The structure can also contain:

  • Pass (password),
  • Timeout (maximum number of milliseconds to wait before giving up),
  • Reconnect (number of seconds to wait before retrying if a connection fails),
  • MaxReconnect (maximum number of times to retry).

Observation 3: the line containing "tarantool.Connect" is essential for beginning any session. There are two parameters:

  • a string with host:port format, and
  • the option structure that was set up earlier.

Observation 4: the err structure will be nil if there is no error, otherwise it will have a description which can be retrieved with err.Error().

Observation 5: the Insert request, like almost all requests, is preceded by "conn." which is the name of the object that was returned by Connect(). There are two parameters:

  • a space number (it could just as easily have been a space name), and
  • a tuple.

Help

To contact go-tarantool developers on any problems, create an issue at tarantool/go-tarantool.

The developers of the Tarantool server will also be happy to provide advice or receive feedback.

Usage

package main

import (
	"github.com/tarantool/go-tarantool"
	"log"
	"time"
)

func main() {
	spaceNo := uint32(512)
	indexNo := uint32(0)

	server := "127.0.0.1:3013"
	opts := tarantool.Opts{
		Timeout:       500 * time.Millisecond,
		Reconnect:     1 * time.Second,
		MaxReconnects: 3,
		User:          "test",
		Pass:          "test",
	}
	client, err := tarantool.Connect(server, opts)
	if err != nil {
		log.Fatalf("Failed to connect: %s", err.Error())
	}

	resp, err := client.Ping()
	log.Println(resp.Code)
	log.Println(resp.Data)
	log.Println(err)

	// insert new tuple { 10, 1 }
	resp, err = client.Insert(spaceNo, []interface{}{uint(10), 1})
    // or
	resp, err = client.Insert("test", []interface{}{uint(10), 1})
	log.Println("Insert")
	log.Println("Error", err)
	log.Println("Code", resp.Code)
	log.Println("Data", resp.Data)

	// delete tuple with primary key { 10 }
	resp, err = client.Delete(spaceNo, indexNo, []interface{}{uint(10)})
    // or
	resp, err = client.Delete("test", "primary", []interface{}{uint(10)})
	log.Println("Delete")
	log.Println("Error", err)
	log.Println("Code", resp.Code)
	log.Println("Data", resp.Data)

	// replace tuple with { 13, 1 }
	resp, err = client.Replace(spaceNo, []interface{}{uint(13), 1})
    // or
	resp, err = client.Replace("test", []interface{}{uint(13), 1})
	log.Println("Replace")
	log.Println("Error", err)
	log.Println("Code", resp.Code)
	log.Println("Data", resp.Data)

	// update tuple with primary key { 13 }, incrementing second field by 3
	resp, err = client.Update(spaceNo, indexNo, []interface{}{uint(13)}, []interface{}{[]interface{}{"+", 1, 3}})
    // or
	resp, err = client.Update("test", "primary", []interface{}{uint(13)}, []interface{}{[]interface{}{"+", 1, 3}})
	log.Println("Update")
	log.Println("Error", err)
	log.Println("Code", resp.Code)
	log.Println("Data", resp.Data)

	// insert tuple {15, 1} or increment second field by 1
	resp, err = client.Upsert(spaceNo, []interface{}{uint(15), 1}, []interface{}{[]interface{}{"+", 1, 1}})
    // or
	resp, err = client.Upsert("test", []interface{}{uint(15), 1}, []interface{}{[]interface{}{"+", 1, 1}})
	log.Println("Upsert")
	log.Println("Error", err)
	log.Println("Code", resp.Code)
	log.Println("Data", resp.Data)

	// select just one tuple with primay key { 15 }
	resp, err = client.Select(spaceNo, indexNo, 0, 1, tarantool.IterEq, []interface{}{uint(15)})
    // or
	resp, err = client.Select("test", "primary", 0, 1, tarantool.IterEq, []interface{}{uint(15)})
	log.Println("Select")
	log.Println("Error", err)
	log.Println("Code", resp.Code)
	log.Println("Data", resp.Data)

	// select tuples by condition ( primay key > 15 ) with offset 7 limit 5
	// BTREE index supposed
	resp, err = client.Select(spaceNo, indexNo, 7, 5, tarantool.IterGt, []interface{}{uint(15)})
    // or
	resp, err = client.Select("test", "primary", 7, 5, tarantool.IterGt, []interface{}{uint(15)})
	log.Println("Select")
	log.Println("Error", err)
	log.Println("Code", resp.Code)
	log.Println("Data", resp.Data)

	// call function 'func_name' with arguments
	resp, err = client.Call("func_name", []interface{}{1, 2, 3})
	log.Println("Call")
	log.Println("Error", err)
	log.Println("Code", resp.Code)
	log.Println("Data", resp.Data)

	// run raw lua code
	resp, err = client.Eval("return 1 + 2", []interface{}{})
	log.Println("Eval")
	log.Println("Error", err)
	log.Println("Code", resp.Code)
	log.Println("Data", resp.Data)
}

Schema

    // save Schema to local variable to avoid races
    schema := client.Schema

    // access Space objects by name or id
    space1 := schema.Spaces["some_space"]
    space2 := schema.SpacesById[20] // it's a map
    fmt.Printf("Space %d %s %s\n", space1.Id, space1.Name, space1.Engine)
    fmt.Printf("Space %d %d\n", space1.FieldsCount, space1.Temporary)

    // access index information by name or id
    index1 := space1.Indexes["some_index"]
    index2 := space1.IndexesById[2] // it's a map
    fmt.Printf("Index %d %s\n", index1.Id, index1.Name)

    // access index fields information by index
    indexField1 := index1.Fields[0] // it's a slice
    indexField2 := index1.Fields[1] // it's a slice
    fmt.Printf("IndexFields %s %s\n", indexField1.Name, indexField1.Type)

    // access space fields information by name or id (index)
    spaceField1 := space.Fields["some_field"]
    spaceField2 := space.FieldsById[3]
    fmt.Printf("SpaceField %s %s\n", spaceField1.Name, spaceField1.Type)

Custom (un)packing and typed selects and function calls

You can specify custom pack/unpack functions for your types. This will allow you to store complex structures inside a tuple and may speed up you requests.

Alternatively, you can just instruct the msgpack library to encode your structure as an array. This is safe "magic". It will be easier to implement than a custom packer/unpacker, but it will work slower.

import (
	"github.com/tarantool/go-tarantool"
	"gopkg.in/vmihailenco/msgpack.v2"
)

type Member struct {
	Name  string
	Nonce string
	Val   uint
}

type Tuple struct {
	Cid     uint
	Orig    string
	Members []Member
}

/* same effect in a "magic" way, but slower */
type Tuple2 struct {
	_msgpack struct{} `msgpack:",asArray"`

	Cid     uint
	Orig    string
	Members []Member
}

func (m *Member) EncodeMsgpack(e *msgpack.Encoder) error {
	if err := e.EncodeSliceLen(2); err != nil {
		return err
	}
	if err := e.EncodeString(m.Name); err != nil {
		return err
	}
	if err := e.EncodeUint(m.Val); err != nil {
		return err
	}
	return nil
}

func (m *Member) DecodeMsgpack(d *msgpack.Decoder) error {
	var err error
	var l int
	if l, err = d.DecodeSliceLen(); err != nil {
		return err
	}
	if l != 2 {
		return fmt.Errorf("array len doesn't match: %d", l)
	}
	if m.Name, err = d.DecodeString(); err != nil {
		return err
	}
	if m.Val, err = d.DecodeUint(); err != nil {
		return err
	}
	return nil
}

func (c *Tuple) EncodeMsgpack(e *msgpack.Encoder) error {
	if err := e.EncodeSliceLen(3); err != nil {
		return err
	}
	if err := e.EncodeUint(c.Cid); err != nil {
		return err
	}
	if err := e.EncodeString(c.Orig); err != nil {
		return err
	}
	if err := e.EncodeSliceLen(len(c.Members)); err != nil {
		return err
	}
	for _, m := range c.Members {
		e.Encode(m)
	}
	return nil
}

func (c *Tuple) DecodeMsgpack(d *msgpack.Decoder) error {
	var err error
	var l int
	if l, err = d.DecodeSliceLen(); err != nil {
		return err
	}
	if l != 3 {
		return fmt.Errorf("array len doesn't match: %d", l)
	}
	if c.Cid, err = d.DecodeUint(); err != nil {
		return err
	}
	if c.Orig, err = d.DecodeString(); err != nil {
		return err
	}
	if l, err = d.DecodeSliceLen(); err != nil {
		return err
	}
	c.Members = make([]Member, l)
	for i := 0; i < l; i++ {
		d.Decode(&c.Members[i])
	}
	return nil
}

func main() {
	// establish connection ...

	tuple := Tuple{777, "orig", []Member{{"lol", "", 1}, {"wut", "", 3}}}
	_, err = conn.Replace(spaceNo, tuple)  // NOTE: insert structure itself
	if err != nil {
		t.Errorf("Failed to insert: %s", err.Error())
		return
	}

	var tuples []Tuple
	err = conn.SelectTyped(spaceNo, indexNo, 0, 1, IterEq, []interface{}{777}, &tuples)
	if err != nil {
		t.Errorf("Failed to SelectTyped: %s", err.Error())
		return
	}

	// same result in a "magic" way
	var tuples2 []Tuple2
	err = conn.SelectTyped(spaceNo, indexNo, 0, 1, IterEq, []interface{}{777}, &tuples2)
	if err != nil {
		t.Errorf("Failed to SelectTyped: %s", err.Error())
		return
	}

	// call function 'func_name' returning a table of custom tuples
	var tuples3 []Tuple
	err = client.CallTyped("func_name", []interface{}{1, 2, 3}, &tuples3)
	if err != nil {
		t.Errorf("Failed to CallTyped: %s", err.Error())
		return
	}
}

/*
// Old way to register types
func init() {
	msgpack.Register(reflect.TypeOf(Tuple{}), encodeTuple, decodeTuple)
	msgpack.Register(reflect.TypeOf(Member{}), encodeMember, decodeMember)
}

func encodeMember(e *msgpack.Encoder, v reflect.Value) error {
	m := v.Interface().(Member)
	// same code as in EncodeMsgpack
	return nil
}

func decodeMember(d *msgpack.Decoder, v reflect.Value) error {
	m := v.Addr().Interface().(*Member)
	// same code as in DecodeMsgpack
	return nil
}

func encodeTuple(e *msgpack.Encoder, v reflect.Value) error {
	c := v.Interface().(Tuple)
	// same code as in EncodeMsgpack
	return nil
}

func decodeTuple(d *msgpack.Decoder, v reflect.Value) error {
	c := v.Addr().Interface().(*Tuple)
	// same code as in DecodeMsgpack
	return nil
}
*/

Options

  • Timeout - timeout for any particular request. If Timeout is zero request, any request may block infinitely.
  • Reconnect - timeout between reconnect attempts. If Reconnect is zero, no reconnects will be performed.
  • MaxReconnects - maximal number of reconnect failures; after that we give it up. If MaxReconnects is zero, the client will try to reconnect endlessly.
  • User - user name to log into Tarantool.
  • Pass - user password to log into Tarantool.

Working with queue

package main
import (
	"gopkg.in/vmihailenco/msgpack.v2"
	"github.com/tarantool/go-tarantool"
	"github.com/tarantool/go-tarantool/queue"
	"time"
	"fmt"
	"log"
)

type customData struct{
	Dummy bool
}

func (c *customData) DecodeMsgpack(d *msgpack.Decoder) error {
	var err error
	if c.Dummy, err = d.DecodeBool(); err != nil {
		return err
	}
	return nil
}

func (c *customData) EncodeMsgpack(e *msgpack.Encoder) error {
	return e.EncodeBool(c.Dummy)
}

func main() {
	opts := tarantool.Opts{
		Timeout: time.Second,
		Reconnect: time.Second,
		MaxReconnects: 5,
		User: "user",
		Pass: "pass",
		// ...
	}
	conn, err := tarantool.Connect("127.0.0.1:3301", opts)

	if err != nil {
		log.Fatalf("connection: %s", err)
		return
	}

	cfg := queue.Cfg{
		Temporary:  true,
		IfNotExists: true,
		Kind:       queue.FIFO,
		Opts: queue.Opts{
			Ttl:   10 * time.Second,
			Ttr:   5 * time.Second,
			Delay: 3 * time.Second,
			Pri:   1,
		},
	}

	que := queue.New(conn, "test_queue")
	if err = que.Create(cfg); err != nil {
		log.Fatalf("queue create: %s", err)
		return
	}

	// put data
	task, err := que.Put("test_data")
	if err != nil {
		log.Fatalf("put task: %s", err)
	}
	fmt.Println("Task id is", task.Id())

	// take data
	task, err = que.Take() //blocking operation
	if err != nil {
		log.Fatalf("take task: %s", err)
	}
	fmt.Println("Data is", task.Data())
	task.Ack()

	// take typed example
	putData := customData{}
	// put data
	task, err = que.Put(&putData)
	if err != nil {
		log.Fatalf("put typed task: %s", err)
	}
	fmt.Println("Task id is ", task.Id())

	takeData := customData{}
	//take data
	task, err = que.TakeTyped(&takeData) //blocking operation
	if err != nil {
		log.Fatalf("take take typed: %s", err)
	}
	fmt.Println("Data is ", takeData)
	// same data
	fmt.Println("Data is ", task.Data())

	task, err = que.Put([]int{1, 2, 3})
	task.Bury()

	task, err = que.TakeTimeout(2 * time.Second)
	if task == nil {
		fmt.Println("Task is nil")
	}

	que.Drop()
}

Features of the implementation:

  • If you use connection timeout and call TakeWithTimeout with parameter greater than the connection timeout then parameter reduced to it
  • If you use connection timeout and call Take then we return a error if we can not take task from queue in a time equal to the connection timeout

Alternative connectors

# Packages

No description provided by the author
No description provided by the author

# Functions

Connect creates and configures new Connection Address could be specified in following ways: TCP connections: - tcp://192.168.1.1:3013 - tcp://my.host:3013 - tcp:192.168.1.1:3013 - tcp:my.host:3013 - 192.168.1.1:3013 - my.host:3013 Unix socket: - unix:///abs/path/tnt.sock - unix:path/tnt.sock - /abs/path/tnt.sock - first '/' indicates unix socket - ./rel/path/tnt.sock - first '.' indicates unix socket - unix/:path/tnt.sock - 'unix/' acts as a "host" and "/path..." as a port Note: - If opts.Reconnect is zero (default), then connection either already connected or error is returned.

# Constants

No description provided by the author
No description provided by the author
call in 1.6 format */.
Either reconnect attempts exhausted, or explicit Close is called.
Connect signals that connection is established or reestablished.
No description provided by the author
Disconnect signals that connection is broken.
%s access denied for user '%s'.
Operation is not permitted when there is an active transaction.
Can't modify space '%s': %s.
Argument type in operation '%c' on field %u does not match field type: expected a %s.
Attempt to modify a tuple field which is part of index '%s' in space '%s'.
Incorrect value for option '%s': %s.
Can't reset cluster id: it is already assigned.
Cluster id of the replica %s doesn't match cluster id of the master %s.
Tarantool client error codes.
Tarantool client error codes.
Failed to create function '%s': %s.
Failed to create role '%s': %s.
Failed to create space '%s': %s.
Failed to create user '%s': %s.
A multi-statement transaction can not use multiple storage engines.
Can't drop function %u: %s.
Can't drop primary key in space '%s' while secondary keys exist.
Can't drop space '%s': %s.
Failed to drop user '%s': %s.
Invalid key part count in an exact match (expected %u, got %u).
Can not create a new fiber: recursion limit reached.
Tuple field %u type does not match one required by operation: expected %s.
Ambiguous field type in index '%s', key part %u.
%s access denied for user '%s' to function '%s'.
Function '%s' already exists.
Unsupported language '%s' specified for function '%s'.
A limit on the total number of functions has been reached: %u.
Incorrect grant arguments: %s.
Setting password for guest user has no effect.
Invalid identifier '%s' (expected letters, digits or an underscore).
Illegal parameters, %s.
Index '%s' already exists.
Tuple field count %u is less than required by a defined index (expected %u).
Unsupported index type supplied for index '%s' in space '%s'.
Error injection '%s'.
Invalid MsgPack - %s.
Invalid LSN order for server %u: previous LSN = %llu, new lsn = %llu.
Invalid UUID: %s.
Failed to read xlog: %lld.
Invalid xlog name: expected %lld got %lld.
Invalid xlog order: %lld and %lld.
Unknown iterator type '%s'.
Invalid key part count (expected [0..%u], got %u).
Supplied key type of part %u does not match index part type: expected %s.
Can't drop the primary key in a system space, space '%s'.
Failed to dynamically load function '%s': %s.
Local server is not active.
Failed to allocate %u bytes in %s for %s.
Missing mandatory field '%s' in request.
Can't find snapshot.
Can't create or modify index '%s' in space '%s': %s.
More than one tuple found by get().
Operation is not permitted when there is no active transaction.
Connection is not established.
Can't modify data on a replication slave.
Space engine '%s' does not exist.
Field %d was not found in the tuple.
Function '%s' does not exist.
No index #%u is defined in space '%s'.
Procedure '%.*s' is not defined.
Role '%s' is not found.
Space '%s' does not exist.
Trigger is not found.
User '%s' is not found.
No description provided by the author
Incorrect password supplied for user '%s'.
User '%s' already has %s access on %s '%s'.
User '%s' does not have %s access on %s '%s'.
???.
%s.
msgpack.encode: can not encode Lua type '%s'.
%s.
Tarantool client error codes.
Tarantool client error codes.
Can't modify data because this server is in read-only mode.
Can't set option '%s' dynamically.
Replica count limit reached: %u.
Reserved66.
Role '%s' already exists.
User '%s' already has role '%s'.
Granting role '%s' to role '%s' would create a loop.
User '%s' does not have role '%s'.
RTree: %s must be an array with %u (point) or %u (rectangle/box) numeric coordinates.
Can't initialize server id with a reserved value %u.
Failed to allocate %u bytes for tuple in the slab allocator: tuple is too large.
%s.
%s access denied for user '%s' to space '%s'.
Space '%s' already exists.
Tuple field count %u does not match space '%s' field count %u.
SPLICE error on field %u: %s.
Timeout exceeded.
Tarantool client error codes.
Transaction has been aborted by conflict.
Tuple format limit reached: %u.
Duplicate key exists in unique index '%s' in space '%s'.
Tuple is too long %u.
Tuple/Key must be MsgPack array.
Tuple doesn't exist in index '%s' in space '%s'.
Tuple reference counter overflow.
Unknown error.
Unknown request type %u.
Unknown RTREE index distance type %s.
Unknown object type '%s'.
Server %s is not registered with the cluster.
Unknown UPDATE operation.
%s does not support %s.
Unsupported role privilege '%s'.
Field %u UPDATE error: %s.
Integer overflow when performing '%c' operation on field %u.
Space %s has a unique secondary index and does not support UPSERT.
User '%s' already exists.
A limit on the total number of users has been reached: %u.
Failed to write to disk.
Wrong index options (field %u): %s.
Wrong index parts (field %u): %s; expected field1 id (number), field1 type (string), ...
Wrong record in _index space: got {%s}, expected {%s}.
Wrong schema version, current: %d, in request: %u.
No description provided by the author
No description provided by the author
all tuples.
all bits are not set.
all bits from x are set in key.
at least one x's bit is set.
key == x ASC order.
key >= x.
key > x.
key <= x.
key < x.
key == x DESC order.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
LogLastReconnectFailed is logged when last reconnect attempt failed, connection will be closed after that.
LogReconnectFailed is logged when reconnect attempt failed.
LogUnexpectedResultId is logged when response with unknown id were received.
No description provided by the author
No description provided by the author
No description provided by the author
ReconnectFailed signals that attempt to reconnect has failed.
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Structs

ClientError is connection produced by this client, ie connection failures or timeouts.
No description provided by the author
ConnEvent is sent throw Notify channel specified in Opts.
No description provided by the author
Error is wrapper around error returned by Tarantool.
No description provided by the author
Future is a handle for asynchronous request.
Greeting is a message sent by tarantool on connect.
Index contains information about index.
No description provided by the author
IntIntKey is utility type for passing two integer keys to Select*, Update* and Delete* It serializes to array with two integer elements.
IntKey is utility type for passing integer key to Select*, Update* and Delete* It serializes to array with single integer element.
Op - is update operation.
No description provided by the author
Opts is a way to configure Connection.
No description provided by the author
Schema contains information about spaces and indexes.
Space contains information about tarantool space.
UintKey is utility type for passing string key to Select*, Update* and Delete* It serializes to array with single string element.
UintKey is utility type for passing unsigned integer key to Select*, Update* and Delete* It serializes to array with single integer element.

# Interfaces

No description provided by the author
Logger is logger type expected to be passed in options.

# Type aliases

No description provided by the author
No description provided by the author