Scala connector for Tarantool.
- Installation
- Getting started
- Configuration
- API reference
- Schema
- Codecs
- Update operations
- Project status
libraryDependencies ++= Seq(
"com.nryanov.zio-tarantool" %% "zio-tarantool-core" % "[version]"
)
import zio._
import zio.clock.Clock
import zio.tarantool._
import zio.tarantool.codec.auto._
import zio.tarantool.protocol.IteratorCode
/**
* > box.schema.create_space('users', {if_not_exists = true})
* > box.space.users:create_index('primary', {if_not_exists = true, unique = true, parts = {1, 'number'} })
*/
object HelloWorld extends zio.App {
final case class Address(street: String, number: Int)
final case class User(id: Long, name: String, age: Int, address: Address)
override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = (for {
_ <- TarantoolClient.insert("users", User(1, "Name1", 10, Address("street1", 1)))
user <- TarantoolClient
.select("users", "primary", 1, 0, IteratorCode.Eq, Tuple1(1L)) // response is Promise[TarantoolError, TarantoolResponse]
.flatMap(_.await)
.flatMap(_.head[User])
_ <- zio.console.putStrLn(s"User: $user")
} yield ExitCode.success).provideLayer(tarantoolLayer()).orDie
def tarantoolLayer() = {
val config = ZLayer.succeed(TarantoolConfig(host = "localhost", port = 3301))
(Clock.live ++ config) >>> TarantoolClient.live ++ zio.console.Console.live
}
}
More examples can be found in the examples folder.
TarantoolConfiguration
consists of:
ConnectionConfig
host
-- tarantool instance host.port
-- tarantool instance port.connectionTimeoutMillis
-- maximum number of milliseconds to wait before giving up to connect (default: 3000).retryTimeoutMillis
-- number of milliseconds to wait before retrying if a connection fails (default: 1000).retries
-- maximum number of times to retry (default: 3).
ClientConfig
requestQueueSize
-- internal queue size for sent requests (default: 64).useSchemaMetaCache
-- fetched space and index info will be saved in in-memory cache (default: true) .schemaRequestTimeoutMillis
-- maximum number of milliseconds to wait before giving up to fetch space and index info (default: 10000).
AuthInfo
(Without AuthInfoguest
user will be used)username
-- username to log into tarantool.password
-- password to log into tarantool.
In the most cases defaults should be ok, and you can use simplified constructors which require only host, port and optionally auth info:
TarantoolConfig(host, port) // default user: guest
TarantoolConfig(host, port, AuthInfo(username, password))
Ping
-- pingInsert
-- insert a tupleSelect
-- search for a tuple or a set of tuples in the given spaceUpdate
-- update tupleUpsert
-- insert or update tupleReplace
-- replace tupleDelete
-- delete a tupleEval
-- evaluates and executes the expression in Lua-string, which may be any statement or series of statementsCall
-- remote stored-procedure callExecute
-- execute the SQL statement contained in the sql-statement parameterPrepare
-- prepare the SQL statement contained in the sql-statement parameterRefreshMeta
-- force schema cache update
All operations return Promise[TarantoolError, TarantoolResponse]
. TarantoolResponse
has methods for accessing the actual data:
resultSet[A]
head[A]
headOption[A]
raw
Type parameter A
with implicit TupleEncoder[A]
is needed to be able to decode raw messagepack into an actual object.
If ClientConfig.useSchemaMetaCache
is set to true
then space and index metas will be stored in in-memory cache.
This info will be used in cases where space name and index name are passed instead of their ids.
Schema will be fetched at the beginning after connection is established, but there is an option to force update it.
To update the schema cache use TarantoolClient.refreshMeta
.
The core encoder type class is TupleEncoder[A]
.
For tuples which consists only of a single primitive type it will be derived from Encoder[A]
.
For case classes it also will be derived automatically whenever special import is added:
import zio.tarantool.codec.auto._
The data will be encoded as a MpArray
. The nested types will be flattened and encoded as flat MpArray
.
But you always can create your own TupleEncoder[_]
:
final case class TestTuple(f1: String, f2: Int, f3: Long)
implicit val tupleEncoder: TupleEncoder[TestTuple] = new TupleEncoder[TestTuple] {
override def decode(v: ArrayValue, idx: Int): TestTuple = {
val f1Mp = Encoder[String].decode(v.get(idx))
val f2Mp = Encoder[Int].decode(v.get(idx + 1))
val f3Mp = Encoder[Long].decode(v.get(idx + 2))
TestTuple(f1Mp, f2Mp, f3Mp)
}
override def encode(v: TestTuple): Vector[Value] = {
val f1Mp = Encoder[String].encode(v.f1)
val f2Mp = Encoder[Int].encode(v.f2)
val f3Mp = Encoder[Long].encode(v.f3)
Vector(f1Mp, f2Mp, f3Mp)
}
}
EXPERIMENTAL
Instead of manual construction of update operation you can generate it from special implicit builder:
import zio.tarantool.codec.auto._
final case class A(f1: String, f2: Int, f3: Long)
val a = A("1", 2, 3)
val updateOps: Attempt[UpdateOperations] = user.builder.assign("f1", "test").plus("f2", 5).build()
Currently, only fields which contains primitive data can be updated. If field does not exist then Attempt.failure
will be returned.
Also, keep in mind that there is no additional checks for types which are passed into update operations, so there is no error for this:
user.builder.assign("f1", 12345).build() // tarantool will update field f1 and set value to 12345
But there are checks for operations and types:
user.builder.minus("myField", "lalala").build() // Attempt.failure because string value cannot be used in numeric operations
Currently, project is under development, so don't expect full stability and that there are no bugs :) Also, there may be API changes in the future.