diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..8b897090 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +.github +.vs +.vscode +Scripts +tests +**/bin +**/obj \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 7ef7253a..a705ceee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,16 @@ -FROM progaudi/dotnet:1.0.1-xenial +FROM microsoft/dotnet:2.1-sdk as sdk WORKDIR /app -ENV PATH $PATH:/root/.dotnet # copy csproj and restore as distinct layers COPY . . -RUN /app/scripts/build-netcore.sh \ No newline at end of file +RUN dotnet build -c Release progaudi.tarantool.sln +RUN dotnet msbuild /t:publish /p:NoBuild=True /p:Configuration=Release samples/insert-performance/insert-performance.csproj + +FROM microsoft/dotnet:2.1-runtime as runtime + +WORKDIR /app + +COPY --from=sdk /app/samples/insert-performance/bin/Release/netcoreapp2.1/publish . + +CMD ["dotnet", "insert-performance.dll"] diff --git a/Dockerfile.alpine b/Dockerfile.alpine new file mode 100644 index 00000000..4a5340e0 --- /dev/null +++ b/Dockerfile.alpine @@ -0,0 +1,16 @@ +FROM microsoft/dotnet:2.1-sdk-alpine as sdk + +WORKDIR /app + +# copy csproj and restore as distinct layers +COPY . . +RUN dotnet build -c Release progaudi.tarantool.sln +RUN dotnet msbuild /t:publish /p:NoBuild=True /p:Configuration=Release samples/insert-performance/insert-performance.csproj + +FROM microsoft/dotnet:2.1-runtime-alpine as runtime + +WORKDIR /app + +COPY --from=sdk /app/samples/insert-performance/bin/Release/netcoreapp2.1/publish . + +CMD ["dotnet", "insert-performance.dll"] diff --git a/Scripts/run-insert-benchmark.sh b/Scripts/run-insert-benchmark.sh new file mode 100644 index 00000000..3f8de007 --- /dev/null +++ b/Scripts/run-insert-benchmark.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -evx + +pushd ${BASH_SOURCE%/*}/.. + +docker-compose down && docker-compose up -d + +./Scripts/build-netcore.sh + +pushd samples/insert-performance/bin/Release/netcoreapp2.0/ + +dotnet insert-performance.dll + +popd +popd \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 9f227fde..681056de 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,40 +1,60 @@ version: '3.2' services: - tarantool_1_7: - image: progaudi/tarantool:1.7.5-184-g5be3a82be # same version as tarantool in homebrew on mac os - command: tarantool /usr/local/share/tarantool/tarantool.docker.lua - volumes: - - $PWD/tarantool:/usr/local/share/tarantool - ports: - - "3301:3301" - environment: - TARANTOOL_USER_NAME: admin - TARANTOOL_USER_PASSWORD: adminPassword - TARANTOOL_SLAB_ALLOC_ARENA: 0.1 + # tarantool_1_7: + # image: progaudi/tarantool:1.7.5-184-g5be3a82be # same version as tarantool in homebrew on mac os + # command: tarantool /usr/local/share/tarantool/tarantool.docker.lua + # volumes: + # - $PWD/tarantool:/usr/local/share/tarantool + # ports: + # - "3301:3301" + # environment: + # TARANTOOL_USER_NAME: admin + # TARANTOOL_USER_PASSWORD: adminPassword + # TARANTOOL_SLAB_ALLOC_ARENA: 2 tarantool_1_8: - image: progaudi/tarantool:1.8.2-288-g99128d7d3 + image: progaudi/tarantool:1.9.0-47-gfabbcfa68 command: tarantool /usr/local/share/tarantool/tarantool.docker.lua volumes: - $PWD/tarantool:/usr/local/share/tarantool ports: - - "3302:3301" + - "3301:3301" environment: TARANTOOL_USER_NAME: admin TARANTOOL_USER_PASSWORD: adminPassword - TARANTOOL_SLAB_ALLOC_ARENA: 0.1 + TARANTOOL_SLAB_ALLOC_ARENA: 2 - redis: - image: redis:3.0-alpine - command: redis-server - ports: - - 6379:6379 + # redis: + # image: redis:3.0-alpine + # command: redis-server + # ports: + # - 6379:6379 - admin: - image: quay.io/basis-company/tarantool-admin - ports: - - 8888:80 + # admin: + # image: quay.io/basis-company/tarantool-admin + # ports: + # - 8888:80 + # depends_on: + # - tarantool_1_7 + # - tarantool_1_8 + + go: + build: + context: samples/go-insert + depends_on: + - tarantool_1_8 + + net-alpine: + build: + context: . + dockerfile: Dockerfile.alpine + depends_on: + - tarantool_1_8 + + net: + build: + context: . + dockerfile: Dockerfile depends_on: - - tarantool_1_7 - tarantool_1_8 diff --git a/global.json b/global.json index 4d61027a..d65e3d5d 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "2.0.0" + "version": "2.1.300" } -} \ No newline at end of file +} diff --git a/progaudi.tarantool.sln b/progaudi.tarantool.sln index 1916e0c7..e33c0bd9 100644 --- a/progaudi.tarantool.sln +++ b/progaudi.tarantool.sln @@ -5,14 +5,14 @@ VisualStudioVersion = 15.0.26730.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "progaudi.tarantool", "src\progaudi.tarantool\progaudi.tarantool.csproj", "{DD007E9F-FB2D-4351-AAB7-F2D367B295C4}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "progaudi.tarantool.tests", "tests\progaudi.tarantool.tests\progaudi.tarantool.tests.csproj", "{4C681801-9A6B-4CE9-8EAA-23F80917F046}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{14BAEDF1-BEFC-4FB2-AAC9-08D397191216}" ProjectSection(SolutionItems) = preProject global.json = global.json EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "progaudi.tarantool.benchmark", "src\progaudi.tarantool.benchmark\progaudi.tarantool.benchmark.csproj", "{CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "insert-performance", "samples\insert-performance\insert-performance.csproj", "{9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "tests", "src\tests\tests.csproj", "{1B3E430D-A1DE-4650-8E4A-1D8B62ECE584}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -36,30 +36,30 @@ Global {DD007E9F-FB2D-4351-AAB7-F2D367B295C4}.Release|x64.Build.0 = Release|Any CPU {DD007E9F-FB2D-4351-AAB7-F2D367B295C4}.Release|x86.ActiveCfg = Release|Any CPU {DD007E9F-FB2D-4351-AAB7-F2D367B295C4}.Release|x86.Build.0 = Release|Any CPU - {4C681801-9A6B-4CE9-8EAA-23F80917F046}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C681801-9A6B-4CE9-8EAA-23F80917F046}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C681801-9A6B-4CE9-8EAA-23F80917F046}.Debug|x64.ActiveCfg = Debug|Any CPU - {4C681801-9A6B-4CE9-8EAA-23F80917F046}.Debug|x64.Build.0 = Debug|Any CPU - {4C681801-9A6B-4CE9-8EAA-23F80917F046}.Debug|x86.ActiveCfg = Debug|Any CPU - {4C681801-9A6B-4CE9-8EAA-23F80917F046}.Debug|x86.Build.0 = Debug|Any CPU - {4C681801-9A6B-4CE9-8EAA-23F80917F046}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C681801-9A6B-4CE9-8EAA-23F80917F046}.Release|Any CPU.Build.0 = Release|Any CPU - {4C681801-9A6B-4CE9-8EAA-23F80917F046}.Release|x64.ActiveCfg = Release|Any CPU - {4C681801-9A6B-4CE9-8EAA-23F80917F046}.Release|x64.Build.0 = Release|Any CPU - {4C681801-9A6B-4CE9-8EAA-23F80917F046}.Release|x86.ActiveCfg = Release|Any CPU - {4C681801-9A6B-4CE9-8EAA-23F80917F046}.Release|x86.Build.0 = Release|Any CPU - {CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}.Debug|x64.ActiveCfg = Debug|Any CPU - {CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}.Debug|x64.Build.0 = Debug|Any CPU - {CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}.Debug|x86.ActiveCfg = Debug|Any CPU - {CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}.Debug|x86.Build.0 = Debug|Any CPU - {CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}.Release|Any CPU.Build.0 = Release|Any CPU - {CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}.Release|x64.ActiveCfg = Release|Any CPU - {CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}.Release|x64.Build.0 = Release|Any CPU - {CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}.Release|x86.ActiveCfg = Release|Any CPU - {CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}.Release|x86.Build.0 = Release|Any CPU + {9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}.Debug|x64.ActiveCfg = Debug|Any CPU + {9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}.Debug|x64.Build.0 = Debug|Any CPU + {9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}.Debug|x86.ActiveCfg = Debug|Any CPU + {9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}.Debug|x86.Build.0 = Debug|Any CPU + {9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}.Release|Any CPU.Build.0 = Release|Any CPU + {9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}.Release|x64.ActiveCfg = Release|Any CPU + {9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}.Release|x64.Build.0 = Release|Any CPU + {9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}.Release|x86.ActiveCfg = Release|Any CPU + {9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}.Release|x86.Build.0 = Release|Any CPU + {1B3E430D-A1DE-4650-8E4A-1D8B62ECE584}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B3E430D-A1DE-4650-8E4A-1D8B62ECE584}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B3E430D-A1DE-4650-8E4A-1D8B62ECE584}.Debug|x64.ActiveCfg = Debug|Any CPU + {1B3E430D-A1DE-4650-8E4A-1D8B62ECE584}.Debug|x64.Build.0 = Debug|Any CPU + {1B3E430D-A1DE-4650-8E4A-1D8B62ECE584}.Debug|x86.ActiveCfg = Debug|Any CPU + {1B3E430D-A1DE-4650-8E4A-1D8B62ECE584}.Debug|x86.Build.0 = Debug|Any CPU + {1B3E430D-A1DE-4650-8E4A-1D8B62ECE584}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B3E430D-A1DE-4650-8E4A-1D8B62ECE584}.Release|Any CPU.Build.0 = Release|Any CPU + {1B3E430D-A1DE-4650-8E4A-1D8B62ECE584}.Release|x64.ActiveCfg = Release|Any CPU + {1B3E430D-A1DE-4650-8E4A-1D8B62ECE584}.Release|x64.Build.0 = Release|Any CPU + {1B3E430D-A1DE-4650-8E4A-1D8B62ECE584}.Release|x86.ActiveCfg = Release|Any CPU + {1B3E430D-A1DE-4650-8E4A-1D8B62ECE584}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/go-insert/Dockerfile b/samples/go-insert/Dockerfile new file mode 100644 index 00000000..a3e91f95 --- /dev/null +++ b/samples/go-insert/Dockerfile @@ -0,0 +1,9 @@ +FROM golang + +WORKDIR /go/src/app +COPY . . + +RUN go get -d -v ./... +RUN go install -v ./... + +CMD ["app"] \ No newline at end of file diff --git a/samples/go-insert/app.go b/samples/go-insert/app.go new file mode 100644 index 00000000..bb7eceb9 --- /dev/null +++ b/samples/go-insert/app.go @@ -0,0 +1,33 @@ +package main + +import ( + "fmt" + "log" + "time" + + "github.com/tarantool/go-tarantool" +) + +func main() { + opts := tarantool.Opts{} + conn, err := tarantool.Connect("tarantool_1_8:3301", opts) + + // conn, err := tarantool.Connect("/path/to/tarantool.socket", opts) + if err != nil { + fmt.Println("Connection refused: %s", err.Error()) + } + start := time.Now() + f := make([]*tarantool.Future, 0) + for i := 0; i < 1000000; i++ { + fut := conn.InsertAsync("pivot", []interface{}{i, []int{i, i}, i}) + f = append(f, fut) + } + for _, element := range f { + _, err := element.Get() + if err != nil { + fmt.Println("Insert failed: %s", err.Error()) + } + } + elapsed := time.Since(start) + log.Printf("Insert took %s", elapsed) +} diff --git a/samples/insert-performance/Program.cs b/samples/insert-performance/Program.cs new file mode 100644 index 00000000..bc2d0b42 --- /dev/null +++ b/samples/insert-performance/Program.cs @@ -0,0 +1,60 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using ProGaudi.Tarantool.Client; +using ProGaudi.Tarantool.Client.Model; + +namespace Tarantool.Test +{ + class Program + { + static void Main() + { + var log = new TextWriterLog(Console.Out); + var options = new ClientOptions(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "localhost:3301" : "tarantool_1_8:3301"); + //var options = new ClientOptions(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "localhost:3301" : "tarantool_1_8:3301", log); + var sw = new Stopwatch(); + try + { + using (var box = new Box(options)) + { + box.Connect().GetAwaiter().GetResult(); + box.Schema.TryGetSpace<(int, (int, int), int)>("pivot", out var space); + var lst = new Task[1000]; + sw.Start(); + for (var i = 0; i < 1_000_000; i++) + { + lst[i % 1000] = space.Insert((i, (i, i), i)); + + if (i % 1000 == 999) + { + Task.WaitAll(lst); + //return; + } + + if (i % 10000 == 9999) + { + Console.Write("*"); + if (i % 100000 == 99999) + { + Console.WriteLine(); + } + } + } + } + } + catch (Exception e) + { + Console.WriteLine(e); + } + finally + { + sw.Stop(); + + Console.WriteLine(); + Console.WriteLine(sw.ElapsedMilliseconds); + } + } + } +} diff --git a/samples/insert-performance/insert-performance.csproj b/samples/insert-performance/insert-performance.csproj new file mode 100644 index 00000000..77f3daa8 --- /dev/null +++ b/samples/insert-performance/insert-performance.csproj @@ -0,0 +1,19 @@ + + + + Exe + netcoreapp2.1 + + + + 7.2 + + + + 7.2 + + + + + + diff --git a/src/progaudi.tarantool.benchmark/IncrementBenchmark.cs b/src/progaudi.tarantool.benchmark/IncrementBenchmark.cs index f92336a7..597a2fb6 100644 --- a/src/progaudi.tarantool.benchmark/IncrementBenchmark.cs +++ b/src/progaudi.tarantool.benchmark/IncrementBenchmark.cs @@ -22,6 +22,6 @@ public IncrementBenchmark() public async Task Redis() => await _redis.StringIncrementAsync("test_for_benchmarking"); [Benchmark] - public async Task> Tarantool() => await _box.Call("test_for_benchmarking"); + public async Task> Tarantool() => await _box.Call("test_for_benchmarking", 0); } } \ No newline at end of file diff --git a/src/progaudi.tarantool.benchmark/progaudi.tarantool.benchmark.csproj b/src/progaudi.tarantool.benchmark/progaudi.tarantool.benchmark.csproj index 442f3c89..19657efc 100644 --- a/src/progaudi.tarantool.benchmark/progaudi.tarantool.benchmark.csproj +++ b/src/progaudi.tarantool.benchmark/progaudi.tarantool.benchmark.csproj @@ -1,10 +1,18 @@ - + Exe - netcoreapp1.1;netcoreapp2.0 + netcoreapp2.0 + latest + + 4 true + progaudi.tarantool + ProGaudi.Tarantool.Client + progaudi.tarantool + Copyright © 2016-2018 + progaudi.tarantool.benchmark ProGaudi.Tarantool.Benchmark diff --git a/src/progaudi.tarantool/AwaitableSocket.cs b/src/progaudi.tarantool/AwaitableSocket.cs new file mode 100644 index 00000000..b10c986b --- /dev/null +++ b/src/progaudi.tarantool/AwaitableSocket.cs @@ -0,0 +1,131 @@ +using System; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +// ReSharper disable once CheckNamespace +namespace Npgsql +{ + public sealed class AwaitableSocket : INotifyCompletion, IDisposable + { + private static readonly Action Sentinel = () => { }; + + private readonly SocketAsyncEventArgs _socketAsyncEventArgs; + private readonly Socket _socket; + + private Action _continuation; + + public AwaitableSocket(SocketAsyncEventArgs socketAsyncEventArgs, Socket socket) + { + _socketAsyncEventArgs = socketAsyncEventArgs; + _socket = socket; + + socketAsyncEventArgs.Completed + += (_, __) => + { + var continuation + = _continuation + ?? Interlocked.CompareExchange(ref _continuation, Sentinel, null); + + continuation?.Invoke(); + }; + } + + public bool IsConnected => _socket.Connected; + public int BytesTransferred => _socketAsyncEventArgs.BytesTransferred; + + public AwaitableSocket ConnectAsync(CancellationToken cancellationToken) + { + Reset(); + + if (!_socket.ConnectAsync(_socketAsyncEventArgs)) + { + IsCompleted = true; + } + + cancellationToken.Register(Cancel); + + void Cancel() + { + if (!_socket.Connected) + { + _socket.Dispose(); + } + } + + return this; + } + + public AwaitableSocket ReceiveAsync() + { + Reset(); + + if (!_socket.ReceiveAsync(_socketAsyncEventArgs)) + { + IsCompleted = true; + } + + return this; + } + + public AwaitableSocket SendAsync() + { + Reset(); + + if (!_socket.SendAsync(_socketAsyncEventArgs)) + { + IsCompleted = true; + } + + return this; + } + + private void Reset() + { + IsCompleted = false; + _continuation = null; + } + + public AwaitableSocket GetAwaiter() + { + return this; + } + + public bool IsCompleted { get; private set; } + + public void OnCompleted(Action continuation) + { + if (_continuation == Sentinel + || Interlocked.CompareExchange( + ref _continuation, continuation, null) == Sentinel) + { + Task.Run(continuation); + } + } + + public void GetResult() + { + if (_socketAsyncEventArgs.SocketError != SocketError.Success) + { + throw new SocketException((int)_socketAsyncEventArgs.SocketError); + } + } + + public void Dispose() + { + if (_socket != null) + { + if (_socket.Connected) + { + _socket.Shutdown(SocketShutdown.Both); + _socket.Close(); + } + + _socket.Dispose(); + } + + _socketAsyncEventArgs?.Dispose(); + } + } +} diff --git a/src/progaudi.tarantool/Box.cs b/src/progaudi.tarantool/Box.cs index 21b892bf..0b329e8f 100644 --- a/src/progaudi.tarantool/Box.cs +++ b/src/progaudi.tarantool/Box.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; +using ProGaudi.MsgPack.Light; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Requests; -using ProGaudi.Tarantool.Client.Model.Responses; using ProGaudi.Tarantool.Client.Utils; namespace ProGaudi.Tarantool.Client @@ -22,7 +22,7 @@ public Box(ClientOptions options) _clientOptions = options; TarantoolConvertersRegistrator.Register(options.MsgPackContext); - _logicalConnection = new LogicalConnectionManager(options); + _logicalConnection = new LogicalConnection(options, new RequestIdCounter()); Metrics = new Metrics(_logicalConnection); Schema = new Schema(_logicalConnection); } @@ -33,6 +33,16 @@ public Box(ClientOptions options) public ISchema Schema { get; } + public ILuaCode GetLuaFunc(string name) + { + throw new NotImplementedException(); + } + + public ILuaCode GetLuaCode(string code) + { + throw new NotImplementedException(); + } + public BoxInfo Info { get => _info; @@ -58,11 +68,12 @@ IEnumerable GetAdditionalTasks() } } - public async Task ReloadBoxInfo() + public Task ReloadBoxInfo() { - var report = await Eval("return box.info").ConfigureAwait(false); - if (report.Data.Length != 1) throw ExceptionHelper.CantParseBoxInfoResponse(); - Info = report.Data[0]; + //var report = await Eval("return box.info").ConfigureAwait(false); + //if (report.Data.Length != 1) throw ExceptionHelper.CantParseBoxInfoResponse(); + //Info = report.Data[0]; + throw new NotImplementedException(); } public static async Task Connect(string replicationSource) @@ -97,71 +108,30 @@ public Task ReloadSchema() return Schema.Reload(); } - public async Task Call_1_6(string functionName) - { - await Call_1_6(functionName, TarantoolTuple.Empty).ConfigureAwait(false); - } - - public async Task Call_1_6(string functionName, TTuple parameters) - { - await Call_1_6(functionName, parameters).ConfigureAwait(false); - } - - public Task> Call_1_6(string functionName) - { - return Call_1_6(functionName, TarantoolTuple.Empty); - } - - public async Task> Call_1_6(string functionName, TTuple parameters) - { - var callRequest = new CallRequest(functionName, parameters, false); - return await _logicalConnection.SendRequest, TResponse>(callRequest).ConfigureAwait(false); - } - - public async Task Call(string functionName) - { - await Call(functionName, TarantoolTuple.Empty).ConfigureAwait(false); - } - - public async Task Call(string functionName, TTuple parameters) - { - await Call(functionName, parameters).ConfigureAwait(false); - } - - public Task> Call(string functionName) - { - return Call(functionName, TarantoolTuple.Empty); - } - - public async Task> Call(string functionName, TTuple parameters) + public Task> Call(string functionName, TTuple parameters) { var callRequest = new CallRequest(functionName, parameters); - return await _logicalConnection.SendRequest, TResponse>(callRequest).ConfigureAwait(false); + return _logicalConnection.SendRequest, TResponse>(callRequest); } - public async Task> Eval(string expression, TTuple parameters) + public Task> Eval(string expression, TTuple parameters) { var evalRequest = new EvalRequest(expression, parameters); - return await _logicalConnection.SendRequest, TResponse>(evalRequest).ConfigureAwait(false); - } - - public Task> Eval(string expression) - { - return Eval(expression, TarantoolTuple.Empty); + return _logicalConnection.SendRequest, TResponse>(evalRequest); } - public Task ExecuteSql(string query, params SqlParameter[] parameters) - { - if (!_sqlReady) throw ExceptionHelper.SqlIsNotAvailable(Info.Version); + //public Task ExecuteSql(string query, params SqlParameter[] parameters) + //{ + // if (!_sqlReady) throw ExceptionHelper.SqlIsNotAvailable(Info.Version); - return _logicalConnection.SendRequest(new ExecuteSqlRequest(query, parameters)); - } + // return _logicalConnection.SendRequest(new ExecuteSqlRequest(query, parameters)); + //} - public Task> ExecuteSql(string query, params SqlParameter[] parameters) - { - if (!_sqlReady) throw ExceptionHelper.SqlIsNotAvailable(Info.Version); + //public Task> ExecuteSql(string query, params SqlParameter[] parameters) + //{ + // if (!_sqlReady) throw ExceptionHelper.SqlIsNotAvailable(Info.Version); - return _logicalConnection.SendRequest(new ExecuteSqlRequest(query, parameters)); - } + // return _logicalConnection.SendRequest(new ExecuteSqlRequest(query, parameters)); + //} } } \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/AuthenticationPacketConverter.cs b/src/progaudi.tarantool/Converters/AuthenticationPacketConverter.cs deleted file mode 100644 index f7823097..00000000 --- a/src/progaudi.tarantool/Converters/AuthenticationPacketConverter.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Requests; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class AuthenticationPacketConverter : IMsgPackConverter - { - private IMsgPackConverter _keyConverter; - private IMsgPackConverter _bytesConverter; - private IMsgPackConverter _stringConverter; - private IMsgPackConverter _nullConverter; - - public void Initialize(MsgPackContext context) - { - _keyConverter = context.GetConverter(); - _bytesConverter = context.GetConverter(); - _stringConverter = context.GetConverter(); - _nullConverter = context.NullConverter; - } - - public void Write(AuthenticationRequest value, IMsgPackWriter writer) - { - if (value == null) - { - _nullConverter.Write(null, writer); - return; - } - - writer.WriteMapHeader(2); - - _keyConverter.Write(Key.Username, writer); - _stringConverter.Write(value.Username, writer); - - _keyConverter.Write(Key.Tuple, writer); - - writer.WriteArrayHeader(2); - _stringConverter.Write("chap-sha1", writer); - _bytesConverter.Write(value.Scramble, writer); - } - - public AuthenticationRequest Read(IMsgPackReader reader) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/CallPacketConverter.cs b/src/progaudi.tarantool/Converters/CallPacketConverter.cs deleted file mode 100644 index 664a3f22..00000000 --- a/src/progaudi.tarantool/Converters/CallPacketConverter.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Requests; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class CallPacketConverter : IMsgPackConverter> - { - private IMsgPackConverter _keyConverter; - private IMsgPackConverter _stringConverter; - private IMsgPackConverter _tupleConverter; - - public void Initialize(MsgPackContext context) - { - _keyConverter = context.GetConverter(); - _stringConverter = context.GetConverter(); - _tupleConverter = context.GetConverter(); - } - - public void Write(CallRequest value, IMsgPackWriter writer) - { - writer.WriteMapHeader(2); - - _keyConverter.Write(Key.FunctionName, writer); - _stringConverter.Write(value.FunctionName, writer); - - _keyConverter.Write(Key.Tuple, writer); - _tupleConverter.Write(value.Tuple, writer); - } - - public CallRequest Read(IMsgPackReader reader) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/DeletePacketConverter.cs b/src/progaudi.tarantool/Converters/DeletePacketConverter.cs deleted file mode 100644 index 70a8715c..00000000 --- a/src/progaudi.tarantool/Converters/DeletePacketConverter.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Requests; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class DeletePacketConverter : IMsgPackConverter> - { - private IMsgPackConverter _keyConverter; - private IMsgPackConverter _uintConverter; - private IMsgPackConverter _selectKeyConverter; - - public void Initialize(MsgPackContext context) - { - _keyConverter = context.GetConverter(); - _uintConverter = context.GetConverter(); - _selectKeyConverter = context.GetConverter(); - } - - public void Write(DeleteRequest value, IMsgPackWriter writer) - { - writer.WriteMapHeader(3); - - _keyConverter.Write(Key.SpaceId, writer); - _uintConverter.Write(value.SpaceId, writer); - - _keyConverter.Write(Key.IndexId, writer); - _uintConverter.Write(value.IndexId, writer); - - _keyConverter.Write(Key.Key, writer); - _selectKeyConverter.Write(value.Key, writer); - } - - public DeleteRequest Read(IMsgPackReader reader) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/EmptyResponseConverter.cs b/src/progaudi.tarantool/Converters/EmptyResponseConverter.cs deleted file mode 100644 index 54b334a3..00000000 --- a/src/progaudi.tarantool/Converters/EmptyResponseConverter.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Responses; -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class EmptyResponseConverter : IMsgPackConverter - { - private IMsgPackConverter _keyConverter; - - public void Initialize(MsgPackContext context) - { - _keyConverter = context.GetConverter(); - } - - public void Write(EmptyResponse value, IMsgPackWriter writer) - { - throw new NotImplementedException(); - } - - public EmptyResponse Read(IMsgPackReader reader) - { - var length = reader.ReadMapLength(); - - if (length > 1) - { - throw ExceptionHelper.InvalidMapLength(length, 0, 1); - } - - if (length ==1) - { - var dataKey = _keyConverter.Read(reader); - if (dataKey != Key.Data) - { - throw ExceptionHelper.UnexpectedKey(dataKey, Key.Data); - } - - var arrayLength = reader.ReadArrayLength(); - if (arrayLength != 0) - { - throw ExceptionHelper.InvalidArrayLength(0, length); - } - } - - return new EmptyResponse(); - - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/ErrorResponsePacketConverter.cs b/src/progaudi.tarantool/Converters/ErrorResponsePacketConverter.cs deleted file mode 100644 index d69f9fb7..00000000 --- a/src/progaudi.tarantool/Converters/ErrorResponsePacketConverter.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Responses; -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class ErrorResponsePacketConverter : IMsgPackConverter - { - private IMsgPackConverter _keyConverter; - private IMsgPackConverter _stringConverter; - - public void Initialize(MsgPackContext context) - { - _keyConverter = context.GetConverter(); - _stringConverter = context.GetConverter(); - } - - public void Write(ErrorResponse value, IMsgPackWriter writer) - { - throw new NotImplementedException(); - } - - public ErrorResponse Read(IMsgPackReader reader) - { - string errorMessage = null; - var length = reader.ReadMapLength(); - - if (length != 1u) - { - throw ExceptionHelper.InvalidMapLength(length, 1u); - } - - var errorKey = _keyConverter.Read(reader); - if (errorKey != Key.Error) - { - throw ExceptionHelper.UnexpectedKey(errorKey, Key.Error); - } - - errorMessage = _stringConverter.Read(reader); - - return new ErrorResponse(errorMessage); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/EvalPacketConverter.cs b/src/progaudi.tarantool/Converters/EvalPacketConverter.cs deleted file mode 100644 index 44bcc5c1..00000000 --- a/src/progaudi.tarantool/Converters/EvalPacketConverter.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Requests; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class EvalPacketConverter : IMsgPackConverter> - { - private IMsgPackConverter _keyConverter; - private IMsgPackConverter _stringConverter; - private IMsgPackConverter _tupleConverter; - - public void Initialize(MsgPackContext context) - { - _keyConverter = context.GetConverter(); - _stringConverter = context.GetConverter(); - _tupleConverter = context.GetConverter(); - } - - public void Write(EvalRequest value, IMsgPackWriter writer) - { - writer.WriteMapHeader(2); - - _keyConverter.Write(Key.Expression, writer); - _stringConverter.Write(value.Expression, writer); - - _keyConverter.Write(Key.Tuple, writer); - _tupleConverter.Write(value.Tuple, writer); - } - - public EvalRequest Read(IMsgPackReader reader) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/ExecuteSqlRequest.cs b/src/progaudi.tarantool/Converters/ExecuteSqlRequest.cs deleted file mode 100644 index 788215bd..00000000 --- a/src/progaudi.tarantool/Converters/ExecuteSqlRequest.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using ProGaudi.MsgPack.Light; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Requests; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class ExecuteSqlRequestConverter : IMsgPackConverter - { - private MsgPackContext _context; - private bool _initialized; - private IMsgPackConverter _nullConverter; - private IMsgPackConverter _stringConverter; - private IMsgPackConverter _keyConverter; - - public void Initialize(MsgPackContext context) - { - _context = context; - } - - public void Write(ExecuteSqlRequest value, IMsgPackWriter writer) - { - if (!_initialized) - { - InitializeIfNeeded(); - } - - writer.WriteMapHeader(3u); - - _keyConverter.Write(Key.SqlQueryText, writer); - _stringConverter.Write(value.Query, writer); - - _keyConverter.Write(Key.SqlParameters, writer); - writer.WriteArrayHeader((uint) value.Parameters.Count); - foreach (var parameter in value.Parameters) - { - parameter.Write(_context, writer, _stringConverter); - } - - _keyConverter.Write(Key.SqlOptions, writer); - _nullConverter.Write(null, writer); - } - - public ExecuteSqlRequest Read(IMsgPackReader reader) - { - throw new NotSupportedException(); - } - - private void InitializeIfNeeded() - { - _initialized = true; - _nullConverter = _context.NullConverter; - _stringConverter = _context.GetConverter(); - _keyConverter = _context.GetConverter(); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/FromStringEnumConverter.cs b/src/progaudi.tarantool/Converters/FromStringEnumConverter.cs deleted file mode 100644 index 0590eb0c..00000000 --- a/src/progaudi.tarantool/Converters/FromStringEnumConverter.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Globalization; -using System.Reflection; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class FromStringEnumConverter : IMsgPackConverter - where T : struct, IConvertible - { - private IMsgPackConverter _stringConverter; - - public void Initialize(MsgPackContext context) - { - _stringConverter = context.GetConverter(); - } - - static FromStringEnumConverter() - { - var enumTypeInfo = typeof(T).GetTypeInfo(); - if (!enumTypeInfo.IsEnum) - { - throw ExceptionHelper.EnumExpected(enumTypeInfo); - } - } - - public void Write(T value, IMsgPackWriter writer) - { - _stringConverter.Write(value.ToString(CultureInfo.InvariantCulture), writer); - } - - public T Read(IMsgPackReader reader) - { - var stringValue = _stringConverter.Read(reader); - - return StringEnum.Parse(typeof (T), stringValue, true); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/IndexConverter.cs b/src/progaudi.tarantool/Converters/IndexConverter.cs deleted file mode 100644 index a9be3ea5..00000000 --- a/src/progaudi.tarantool/Converters/IndexConverter.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class IndexConverter : IMsgPackConverter - { - private IMsgPackConverter _uintConverter; - private IMsgPackConverter _stringConverter; - private IMsgPackConverter _indexTypeConverter; - private IMsgPackConverter _optionsConverter; - private IMsgPackConverter> _indexPartsConverter; - - public void Initialize(MsgPackContext context) - { - _uintConverter = context.GetConverter(); - _stringConverter = context.GetConverter(); - _indexTypeConverter = context.GetConverter(); - _optionsConverter = context.GetConverter(); - _indexPartsConverter = context.GetConverter>(); - } - - public void Write(Index value, IMsgPackWriter writer) - { - throw new NotImplementedException(); - } - - public Index Read(IMsgPackReader reader) - { - var length = reader.ReadArrayLength(); - - if (length != 6u) - { - throw ExceptionHelper.InvalidArrayLength(6u, length); - } - - var spaceId = _uintConverter.Read(reader); - var id= _uintConverter.Read(reader); - var name = _stringConverter.Read(reader); - var type = _indexTypeConverter.Read(reader); - var options = _optionsConverter.Read(reader); - var indexParts = _indexPartsConverter.Read(reader); - - return new Index(id, spaceId, name, options.Unique, type, indexParts.AsReadOnly()); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/IndexCreationOptionsConverter.cs b/src/progaudi.tarantool/Converters/IndexCreationOptionsConverter.cs deleted file mode 100644 index 05807593..00000000 --- a/src/progaudi.tarantool/Converters/IndexCreationOptionsConverter.cs +++ /dev/null @@ -1,45 +0,0 @@ -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class IndexCreationOptionsConverter:IMsgPackConverter - { - private MsgPackContext context; - - public void Initialize(MsgPackContext context) - { - this.context= context; - } - - public void Write(IndexCreationOptions value, IMsgPackWriter writer) - { - throw new System.NotImplementedException(); - } - - public IndexCreationOptions Read(IMsgPackReader reader) - { - var optionsCount = reader.ReadMapLength(); - var stringConverter = context.GetConverter(); - var boolConverter = context.GetConverter(); - - var unique = false; - for (int i = 0; i < optionsCount.Value; i++) - { - var key = stringConverter.Read(reader); - switch (key) - { - case "unique": - unique = boolConverter.Read(reader); - break; - default: - reader.SkipToken(); - break; - } - } - - return new IndexCreationOptions(unique); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/IndexPartConverter.cs b/src/progaudi.tarantool/Converters/IndexPartConverter.cs deleted file mode 100644 index bd561161..00000000 --- a/src/progaudi.tarantool/Converters/IndexPartConverter.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System.Runtime.Serialization; -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class IndexPartConverter : IMsgPackConverter - { - private IMsgPackConverter _uintConverter; - private IMsgPackConverter _indexPartTypeConverter; - private IMsgPackConverter _stringConverter; - - public void Initialize(MsgPackContext context) - { - _uintConverter = context.GetConverter(); - _indexPartTypeConverter = context.GetConverter(); - _stringConverter = context.GetConverter(); - } - - public void Write(IndexPart value, IMsgPackWriter writer) => throw new System.NotImplementedException(); - - public IndexPart Read(IMsgPackReader reader) - { - var type = reader.ReadDataType(); - switch (type) - { - case DataTypes.Map16: - return ReadFromMap(reader, ReadUInt16(reader)); - - case DataTypes.Map32: - return ReadFromMap(reader, ReadUInt32(reader)); - - case DataTypes.Array16: - return ReadFromArray(reader, ReadUInt16(reader)); - - case DataTypes.Array32: - return ReadFromArray(reader, ReadUInt32(reader)); - } - - var length = TryGetLengthFromFixMap(type); - if (length.HasValue) - { - return ReadFromMap(reader, length.Value); - } - - length = TryGetLengthFromFixArray(type); - if (length != null) - { - return ReadFromArray(reader, length.Value); - } - - throw ExceptionUtils.BadTypeException(type, DataTypes.Map16, DataTypes.Map32, DataTypes.FixMap, DataTypes.Array16, DataTypes.Array32, DataTypes.FixArray); - } - - private IndexPart ReadFromArray(IMsgPackReader reader, uint length) - { - if (length != 2u) - { - throw ExceptionHelper.InvalidArrayLength(2u, length); - } - - var fieldNo = _uintConverter.Read(reader); - var indexPartType = _indexPartTypeConverter.Read(reader); - - return new IndexPart(fieldNo, indexPartType); - } - - private IndexPart ReadFromMap(IMsgPackReader reader, uint length) - { - uint? fieldNo = null; - IndexPartType? indexPartType = null; - - for (var i = 0; i < length; i++) - { - switch (_stringConverter.Read(reader)) - { - case "field": - fieldNo = _uintConverter.Read(reader); - break; - case "type": - indexPartType = _indexPartTypeConverter.Read(reader); - break; - default: - reader.SkipToken(); - break; - } - } - - if (fieldNo.HasValue && indexPartType.HasValue) - { - return new IndexPart(fieldNo.Value, indexPartType.Value); - } - - throw new SerializationException("Can't read fieldNo or indexPart from map of index metadata"); - } - - private static uint? TryGetLengthFromFixArray(DataTypes type) - { - var length = type - DataTypes.FixArray; - return type.GetHighBits(4) == DataTypes.FixArray.GetHighBits(4) ? length : (uint?)null; - } - - private static uint? TryGetLengthFromFixMap(DataTypes type) - { - var length = type - DataTypes.FixMap; - return type.GetHighBits(4) == DataTypes.FixMap.GetHighBits(4) ? length : (uint?)null; - } - - internal static ushort ReadUInt16(IMsgPackReader reader) - { - return (ushort)((reader.ReadByte() << 8) + reader.ReadByte()); - } - - internal static uint ReadUInt32(IMsgPackReader reader) - { - var temp = (uint)(reader.ReadByte() << 24); - temp += (uint)reader.ReadByte() << 16; - temp += (uint)reader.ReadByte() << 8; - temp += reader.ReadByte(); - - return temp; - } - } -} diff --git a/src/progaudi.tarantool/Converters/InsertReplacePacketConverter.cs b/src/progaudi.tarantool/Converters/InsertReplacePacketConverter.cs deleted file mode 100644 index fb4e7cb6..00000000 --- a/src/progaudi.tarantool/Converters/InsertReplacePacketConverter.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Requests; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class InsertReplacePacketConverter : IMsgPackConverter> - { - private IMsgPackConverter _keyConverter; - private IMsgPackConverter _uintConverter; - private IMsgPackConverter _tupleConverter; - - public void Initialize(MsgPackContext context) - { - _keyConverter = context.GetConverter(); - _uintConverter = context.GetConverter(); - _tupleConverter = context.GetConverter(); - } - - public void Write(InsertReplaceRequest value, IMsgPackWriter writer) - { - writer.WriteMapHeader(2); - - _keyConverter.Write(Key.SpaceId, writer); - _uintConverter.Write(value.SpaceId, writer); - - _keyConverter.Write(Key.Tuple, writer); - _tupleConverter.Write(value.Tuple, writer); - } - - public InsertReplaceRequest Read(IMsgPackReader reader) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/PacketSizeConverter.cs b/src/progaudi.tarantool/Converters/PacketSizeConverter.cs deleted file mode 100644 index 9aa4d559..00000000 --- a/src/progaudi.tarantool/Converters/PacketSizeConverter.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class PacketSizeConverter : IMsgPackConverter - { - public void Initialize(MsgPackContext context) - { - - } - - public void Write(PacketSize value, IMsgPackWriter writer) - { - writer.Write(DataTypes.UInt32); - - var binary = new UIntBinary(value.Value); - - byte[] bytes; - if (BitConverter.IsLittleEndian) - { - bytes = new[] - { - binary.byte3, - binary.byte2, - binary.byte1, - binary.byte0 - }; - } - else - { - bytes = new[] - { - binary.byte0, - binary.byte1, - binary.byte2, - binary.byte3, - }; - } - - writer.Write(bytes); - } - - public PacketSize Read(IMsgPackReader reader) - { - var type = reader.ReadDataType(); - if (type != DataTypes.UInt32) - { - throw ExceptionHelper.UnexpectedDataType(DataTypes.UInt32, type); - } - - var allbytes = reader.ReadBytes(4); - var uintValue = new UIntBinary(allbytes); - return new PacketSize(uintValue.value); - } - - [StructLayout(LayoutKind.Explicit)] - private struct UIntBinary - { - [FieldOffset(0)] - public readonly uint value; - - [FieldOffset(0)] - public readonly byte byte0; - - [FieldOffset(1)] - public readonly byte byte1; - - [FieldOffset(2)] - public readonly byte byte2; - - [FieldOffset(3)] - public readonly byte byte3; - - public UIntBinary(uint f) - { - this = default(UIntBinary); - value = f; - } - - public UIntBinary(ArraySegment bytes) - { - value = 0; - if (BitConverter.IsLittleEndian) - { - byte0 = bytes.Array[bytes.Offset + 7]; - byte1 = bytes.Array[bytes.Offset + 6]; - byte2 = bytes.Array[bytes.Offset + 5]; - byte3 = bytes.Array[bytes.Offset + 4]; - } - else - { - byte0 = bytes.Array[bytes.Offset + 0]; - byte1 = bytes.Array[bytes.Offset + 1]; - byte2 = bytes.Array[bytes.Offset + 2]; - byte3 = bytes.Array[bytes.Offset + 3]; - } - } - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/PingPacketConverter.cs b/src/progaudi.tarantool/Converters/PingPacketConverter.cs deleted file mode 100644 index 114c40c6..00000000 --- a/src/progaudi.tarantool/Converters/PingPacketConverter.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model.Requests; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class PingPacketConverter : IMsgPackConverter - { - public void Initialize(MsgPackContext context) - { - } - - public void Write(PingRequest value, IMsgPackWriter writer) - { - } - - public PingRequest Read(IMsgPackReader reader) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/RequestHeaderConverter.cs b/src/progaudi.tarantool/Converters/RequestHeaderConverter.cs deleted file mode 100644 index 65373c76..00000000 --- a/src/progaudi.tarantool/Converters/RequestHeaderConverter.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Headers; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class RequestHeaderConverter : IMsgPackConverter - { - private IMsgPackConverter _keyConverter; - private IMsgPackConverter _requestIdConverter; - private IMsgPackConverter _codeConverter; - private IMsgPackConverter _nullConverter; - - public void Initialize(MsgPackContext context) - { - _keyConverter = context.GetConverter(); - _requestIdConverter = context.GetConverter(); - _codeConverter = context.GetConverter(); - _nullConverter = context.NullConverter; - } - - public void Write(RequestHeader value, IMsgPackWriter writer) - { - if (value == null) - { - _nullConverter.Write(null, writer); - return; - } - - writer.WriteMapHeader(2u); - - _keyConverter.Write(Key.Code, writer); - _codeConverter.Write(value.Code, writer); - - _keyConverter.Write(Key.Sync, writer); - _requestIdConverter.Write(value.RequestId, writer); - } - - public RequestHeader Read(IMsgPackReader reader) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/RequestIdConverter.cs b/src/progaudi.tarantool/Converters/RequestIdConverter.cs deleted file mode 100644 index c710c3c3..00000000 --- a/src/progaudi.tarantool/Converters/RequestIdConverter.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class RequestIdConverter : IMsgPackConverter - { - public void Initialize(MsgPackContext context) - { - - } - - public void Write(RequestId value, IMsgPackWriter writer) - { - writer.Write(DataTypes.UInt64); - - var binary = new ULongBinary(value.Value); - - byte[] bytes; - if (BitConverter.IsLittleEndian) - { - bytes = new[] - { - binary.byte7, - binary.byte6, - binary.byte5, - binary.byte4, - binary.byte3, - binary.byte2, - binary.byte1, - binary.byte0 - }; - } - else - { - bytes = new[] - { - binary.byte0, - binary.byte1, - binary.byte2, - binary.byte3, - binary.byte4, - binary.byte5, - binary.byte6, - binary.byte7 - }; - } - - writer.Write(bytes); - } - - public RequestId Read(IMsgPackReader reader) - { - var type = reader.ReadDataType(); - if (type != DataTypes.UInt64) - { - throw ExceptionHelper.UnexpectedDataType(DataTypes.UInt64, type); - } - - var allbytes = reader.ReadBytes(8); - var ulongValue = new ULongBinary(allbytes); - return new RequestId(ulongValue.value); - } - - [StructLayout(LayoutKind.Explicit)] - private struct ULongBinary - { - [FieldOffset(0)] - public readonly ulong value; - - [FieldOffset(0)] - public readonly byte byte0; - - [FieldOffset(1)] - public readonly byte byte1; - - [FieldOffset(2)] - public readonly byte byte2; - - [FieldOffset(3)] - public readonly byte byte3; - - [FieldOffset(4)] - public readonly byte byte4; - - [FieldOffset(5)] - public readonly byte byte5; - - [FieldOffset(6)] - public readonly byte byte6; - - [FieldOffset(7)] - public readonly byte byte7; - - public ULongBinary(ulong f) - { - this = default(ULongBinary); - value = f; - } - - public ULongBinary(ArraySegment bytes) - { - value = 0; - if (BitConverter.IsLittleEndian) - { - byte0 = bytes.Array[bytes.Offset + 7]; - byte1 = bytes.Array[bytes.Offset + 6]; - byte2 = bytes.Array[bytes.Offset + 5]; - byte3 = bytes.Array[bytes.Offset + 4]; - byte4 = bytes.Array[bytes.Offset + 3]; - byte5 = bytes.Array[bytes.Offset + 2]; - byte6 = bytes.Array[bytes.Offset + 1]; - byte7 = bytes.Array[bytes.Offset + 0]; - } - else - { - byte0 = bytes.Array[bytes.Offset + 0]; - byte1 = bytes.Array[bytes.Offset + 1]; - byte2 = bytes.Array[bytes.Offset + 2]; - byte3 = bytes.Array[bytes.Offset + 3]; - byte4 = bytes.Array[bytes.Offset + 4]; - byte5 = bytes.Array[bytes.Offset + 5]; - byte6 = bytes.Array[bytes.Offset + 6]; - byte7 = bytes.Array[bytes.Offset + 7]; - } - } - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/ResponseHeaderConverter.cs b/src/progaudi.tarantool/Converters/ResponseHeaderConverter.cs deleted file mode 100644 index 055e89e3..00000000 --- a/src/progaudi.tarantool/Converters/ResponseHeaderConverter.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Headers; -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class ResponseHeaderConverter : IMsgPackConverter - { - private IMsgPackConverter _keyConverter; - private IMsgPackConverter _ulongConverter; - private IMsgPackConverter _requestIdConverter; - private IMsgPackConverter _codeConverter; - - public void Initialize(MsgPackContext context) - { - _keyConverter = context.GetConverter(); - _ulongConverter = context.GetConverter(); - _codeConverter = context.GetConverter(); - _requestIdConverter = context.GetConverter(); - } - - public void Write(ResponseHeader value, IMsgPackWriter writer) - { - throw new NotImplementedException(); - } - - public ResponseHeader Read(IMsgPackReader reader) - { - var length = reader.ReadMapLength(); - - if (!length.HasValue) - { - throw ExceptionHelper.InvalidMapLength(length, 2u, 3u); - } - - CommandCode? code = null; - RequestId? sync = null; - ulong? schemaId = null; - - for (int i = 0; i < length.Value; i++) - { - var key = _keyConverter.Read(reader); - - switch (key) - { - case Key.Code: - code = _codeConverter.Read(reader); - break; - case Key.Sync: - sync = _requestIdConverter.Read(reader); - break; - case Key.SchemaId: - schemaId = _ulongConverter.Read(reader); - break; - default: - reader.SkipToken(); - break; - } - } - - if (!code.HasValue) - { - throw ExceptionHelper.PropertyUnspecified("Code"); - } - - if (!sync.HasValue) - { - throw ExceptionHelper.PropertyUnspecified("Sync"); - } - - return new ResponseHeader(code.Value, sync.Value, schemaId); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/ResponsePacketConverter.cs b/src/progaudi.tarantool/Converters/ResponsePacketConverter.cs deleted file mode 100644 index e626cec6..00000000 --- a/src/progaudi.tarantool/Converters/ResponsePacketConverter.cs +++ /dev/null @@ -1,175 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Responses; -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class ResponsePacketConverter : IMsgPackConverter - { - private IMsgPackConverter _keyConverter; - private IMsgPackConverter _intConverter; - - public virtual void Initialize(MsgPackContext context) - { - _keyConverter = context.GetConverter(); - _intConverter = context.GetConverter(); - } - - public void Write(DataResponse value, IMsgPackWriter writer) - { - throw new NotSupportedException(); - } - - public DataResponse Read(IMsgPackReader reader) - { - var length = reader.ReadMapLength(); - if (!(1u <= length && length <= 3)) - { - throw ExceptionHelper.InvalidMapLength(length, 1u, 2u); - } - - var sqlInfo = default(SqlInfo); - - for (var i = 0; i < length; i++) - { - var dataKey = _keyConverter.Read(reader); - switch (dataKey) - { - case Key.SqlInfo: - sqlInfo = ReadSqlInfo(reader, _keyConverter, _intConverter); - break; - default: - throw ExceptionHelper.UnexpectedKey(dataKey, Key.Data, Key.Metadata); - } - } - - return new DataResponse(sqlInfo); - } - - internal static SqlInfo ReadSqlInfo(IMsgPackReader reader, IMsgPackConverter keyConverter, IMsgPackConverter intConverter) - { - var length = reader.ReadMapLength(); - if (length == null) - { - return null; - } - - var result = default(SqlInfo); - for (var i = 0; i < length; i++) - { - switch (keyConverter.Read(reader)) - { - case Key.SqlRowCount: - result = new SqlInfo(intConverter.Read(reader)); - break; - default: - reader.SkipToken(); - break; - } - } - - return result; - } - } - - internal class ResponsePacketConverter : IMsgPackConverter> - { - private IMsgPackConverter _keyConverter; - private IMsgPackConverter _dataConverter; - private IMsgPackConverter _stringConverter; - private IMsgPackConverter _intConverter; - - public void Initialize(MsgPackContext context) - { - _keyConverter = context.GetConverter(); - _dataConverter = context.GetConverter(); - _stringConverter = context.GetConverter(); - _intConverter = context.GetConverter(); - } - - public void Write(DataResponse value, IMsgPackWriter writer) - { - throw new NotSupportedException(); - } - - DataResponse IMsgPackConverter>.Read(IMsgPackReader reader) - { - var length = reader.ReadMapLength(); - if (!(1u <= length && length <= 3)) - { - throw ExceptionHelper.InvalidMapLength(length, 1u, 2u); - } - - var data = default(T); - var dataWasSet = false; - var metadata = default(FieldMetadata[]); - var sqlInfo = default(SqlInfo); - - for (var i = 0; i < length; i++) - { - var dataKey = _keyConverter.Read(reader); - switch (dataKey) - { - case Key.Data: - data = _dataConverter.Read(reader); - dataWasSet = true; - break; - case Key.Metadata: - metadata = ReadMetadata(reader); - break; - case Key.SqlInfo: - sqlInfo = ResponsePacketConverter.ReadSqlInfo(reader, _keyConverter, _intConverter); - break; - default: - throw ExceptionHelper.UnexpectedKey(dataKey, Key.Data, Key.Metadata); - } - } - - if (!dataWasSet && sqlInfo == null) - { - throw ExceptionHelper.NoDataInDataResponse(); - } - - return new DataResponse(data, metadata, sqlInfo); - } - - private FieldMetadata[] ReadMetadata(IMsgPackReader reader) - { - var length = reader.ReadArrayLength(); - if (length == null) - { - return null; - } - - var result = new FieldMetadata[length.Value]; - for (var i = 0; i < length; i++) - { - var metadataLength = reader.ReadMapLength(); - if (metadataLength == null) - { - result[i] = null; - continue; - } - - for (var j = 0; j < metadataLength; j++) - { - switch (_keyConverter.Read(reader)) - { - case Key.FieldName: - result[i] = new FieldMetadata(_stringConverter.Read(reader)); - continue; - default: - reader.SkipToken(); - break; - } - } - } - - return result; - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/SelectPacketConverter.cs b/src/progaudi.tarantool/Converters/SelectPacketConverter.cs deleted file mode 100644 index c4e4c080..00000000 --- a/src/progaudi.tarantool/Converters/SelectPacketConverter.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Requests; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class SelectPacketConverter : IMsgPackConverter> - { - private IMsgPackConverter _selectKeyConverter; - private IMsgPackConverter _keyConverter; - private IMsgPackConverter _uintConverter; - private IMsgPackConverter _iteratorConverter; - - public void Initialize(MsgPackContext context) - { - _keyConverter = context.GetConverter(); - _uintConverter = context.GetConverter(); - _iteratorConverter = context.GetConverter(); - _selectKeyConverter = context.GetConverter(); - } - - public void Write(SelectRequest value, IMsgPackWriter writer) - { - writer.WriteMapHeader(6); - - _keyConverter.Write(Key.SpaceId, writer); - _uintConverter.Write(value.SpaceId, writer); - - _keyConverter.Write(Key.IndexId, writer); - _uintConverter.Write(value.IndexId, writer); - - _keyConverter.Write(Key.Limit, writer); - _uintConverter.Write(value.Limit, writer); - - _keyConverter.Write(Key.Offset, writer); - _uintConverter.Write(value.Offset, writer); - - _keyConverter.Write(Key.Iterator, writer); - _iteratorConverter.Write(value.Iterator, writer); - - _keyConverter.Write(Key.Key, writer); - _selectKeyConverter.Write(value.SelectKey, writer); - } - - public SelectRequest Read(IMsgPackReader reader) - { - throw new NotSupportedException(); - } - } -} diff --git a/src/progaudi.tarantool/Converters/SpaceConverter.cs b/src/progaudi.tarantool/Converters/SpaceConverter.cs deleted file mode 100644 index 7b0f6088..00000000 --- a/src/progaudi.tarantool/Converters/SpaceConverter.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class SpaceConverter : IMsgPackConverter - { - private IMsgPackConverter _uintConverter; - private IMsgPackConverter _stringConverter; - private IMsgPackConverter _engineConverter; - private IMsgPackConverter> _fieldConverter; - - public void Initialize(MsgPackContext context) - { - _uintConverter = context.GetConverter(); - _stringConverter = context.GetConverter(); - _engineConverter = context.GetConverter(); - _fieldConverter = context.GetConverter>(); - } - - public void Write(Space value, IMsgPackWriter writer) - { - throw new NotImplementedException(); - } - - public Space Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - const uint expected = 7u; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var id = _uintConverter.Read(reader); - - //TODO Find what skipped number means - reader.SkipToken(); - - var name = _stringConverter.Read(reader); - var engine = _engineConverter.Read(reader); - var fieldCount = _uintConverter.Read(reader); - - //TODO Find what skipped dictionary used for - reader.SkipToken(); - - var fields = _fieldConverter.Read(reader); - - return new Space(id, fieldCount, name, engine, fields.AsReadOnly()); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/SpaceFieldConverter.cs b/src/progaudi.tarantool/Converters/SpaceFieldConverter.cs deleted file mode 100644 index 93e154cf..00000000 --- a/src/progaudi.tarantool/Converters/SpaceFieldConverter.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Converters -{ - public class SpaceFieldConverter:IMsgPackConverter - { - private IMsgPackConverter _stringConverter; - private IMsgPackConverter _typeConverter; - - public void Initialize(MsgPackContext context) - { - _stringConverter = context.GetConverter(); - _typeConverter = context.GetConverter(); - } - - public void Write(SpaceField value, IMsgPackWriter writer) - { - throw new NotImplementedException(); - } - - public SpaceField Read(IMsgPackReader reader) - { - var dictLength = reader.ReadMapLength(); - - string name = null; - var type = (FieldType) (-1); - - for (int i = 0; i < dictLength.Value; i++) - { - var key = _stringConverter.Read(reader); - switch (key) - { - case "name": - name = _stringConverter.Read(reader); - break; - case "type": - type = _typeConverter.Read(reader); - break; - default: - reader.SkipToken(); - break; - } - } - - return new SpaceField(name, type); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/StringSliceOperationConverter.cs b/src/progaudi.tarantool/Converters/StringSliceOperationConverter.cs deleted file mode 100644 index c6b55853..00000000 --- a/src/progaudi.tarantool/Converters/StringSliceOperationConverter.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model.UpdateOperations; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class StringSliceOperationConverter : IMsgPackConverter - { - private IMsgPackConverter _stringConverter; - private IMsgPackConverter _intConverter; - - public void Initialize(MsgPackContext context) - { - _stringConverter = context.GetConverter(); - _intConverter = context.GetConverter(); - } - - public void Write(StringSliceOperation value, IMsgPackWriter writer) - { - - writer.WriteArrayHeader(5); - - _stringConverter.Write(value.OperationType, writer); - _intConverter.Write(value.FieldNumber, writer); - _intConverter.Write(value.Position, writer); - _intConverter.Write(value.Offset, writer); - _stringConverter.Write(value.Argument, writer); - } - - public StringSliceOperation Read(IMsgPackReader reader) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/TarantoolTupleConverters.cs b/src/progaudi.tarantool/Converters/TarantoolTupleConverters.cs deleted file mode 100644 index 7667271e..00000000 --- a/src/progaudi.tarantool/Converters/TarantoolTupleConverters.cs +++ /dev/null @@ -1,511 +0,0 @@ -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class TupleConverter : IMsgPackConverter - { - private IMsgPackConverter _nullConverter; - - public void Initialize(MsgPackContext context) - { - _nullConverter = context.NullConverter; - } - - public void Write(TarantoolTuple value, IMsgPackWriter writer) - { - if (value == null) - { - _nullConverter.Write(null, writer); - return; - } - - writer.WriteArrayHeader(0); - } - - public TarantoolTuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - const uint expected = 0u; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - return TarantoolTuple.Empty; - } - } - - public class TupleConverter : IMsgPackConverter> - { - private IMsgPackConverter _nullConverter; - private IMsgPackConverter _t1Converter; - - public void Initialize(MsgPackContext context) - { - _nullConverter = context.NullConverter; - _t1Converter = context.GetConverter(); - } - - public void Write(TarantoolTuple value, IMsgPackWriter writer) - { - if (value == null) - { - _nullConverter.Write(null, writer); - return; - } - - writer.WriteArrayHeader(1); - _t1Converter.Write(value.Item1, writer); - } - - public TarantoolTuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - - const uint expected = 1u; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - - return TarantoolTuple.Create(item1); - } - } - - public class TupleConverter : IMsgPackConverter> - { - private IMsgPackConverter _nullConverter; - private IMsgPackConverter _t1Converter; - private IMsgPackConverter _t2Converter; - - public void Initialize(MsgPackContext context) - { - _nullConverter = context.NullConverter; - _t1Converter = context.GetConverter(); - _t2Converter = context.GetConverter(); - } - - public void Write(TarantoolTuple value, IMsgPackWriter writer) - { - if (value == null) - { - _nullConverter.Write(null, writer); - return; - } - - writer.WriteArrayHeader(2); - _t1Converter.Write(value.Item1, writer); - _t2Converter.Write(value.Item2, writer); - } - - public TarantoolTuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - const uint expected = 2u; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - var item2 = _t2Converter.Read(reader); - - return TarantoolTuple.Create(item1, item2); - } - } - - public class TupleConverter : IMsgPackConverter> - { - private IMsgPackConverter _nullConverter; - private IMsgPackConverter _t1Converter; - private IMsgPackConverter _t2Converter; - private IMsgPackConverter _t3Converter; - - public void Initialize(MsgPackContext context) - { - _nullConverter = context.NullConverter; - _t1Converter = context.GetConverter(); - _t2Converter = context.GetConverter(); - _t3Converter = context.GetConverter(); - } - - public void Write(TarantoolTuple value, IMsgPackWriter writer) - { - if (value == null) - { - _nullConverter.Write(null, writer); - return; - } - - writer.WriteArrayHeader(3); - _t1Converter.Write(value.Item1, writer); - _t2Converter.Write(value.Item2, writer); - _t3Converter.Write(value.Item3, writer); - } - - public TarantoolTuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - - const uint expected = 3u; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - var item2 = _t2Converter.Read(reader); - var item3 = _t3Converter.Read(reader); - - return TarantoolTuple.Create(item1, item2, item3); - } - } - - public class TupleConverter : IMsgPackConverter> - { - private IMsgPackConverter _nullConverter; - private IMsgPackConverter _t1Converter; - private IMsgPackConverter _t2Converter; - private IMsgPackConverter _t3Converter; - private IMsgPackConverter _t4Converter; - - public void Initialize(MsgPackContext context) - { - _nullConverter = context.NullConverter; - _t1Converter = context.GetConverter(); - _t2Converter = context.GetConverter(); - _t3Converter = context.GetConverter(); - _t4Converter = context.GetConverter(); - } - - public void Write(TarantoolTuple value, IMsgPackWriter writer) - { - if (value == null) - { - _nullConverter.Write(null, writer); - return; - } - - writer.WriteArrayHeader(4); - _t1Converter.Write(value.Item1, writer); - _t2Converter.Write(value.Item2, writer); - _t3Converter.Write(value.Item3, writer); - _t4Converter.Write(value.Item4, writer); - } - - public TarantoolTuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - - const uint expected = 4u; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - var item2 = _t2Converter.Read(reader); - var item3 = _t3Converter.Read(reader); - var item4 = _t4Converter.Read(reader); - - return TarantoolTuple.Create(item1, item2, item3, item4); - } - } - - public class TupleConverter : IMsgPackConverter> - { - private IMsgPackConverter _nullConverter; - private IMsgPackConverter _t1Converter; - private IMsgPackConverter _t2Converter; - private IMsgPackConverter _t3Converter; - private IMsgPackConverter _t4Converter; - private IMsgPackConverter _t5Converter; - - public void Initialize(MsgPackContext context) - { - _nullConverter = context.NullConverter; - _t1Converter = context.GetConverter(); - _t2Converter = context.GetConverter(); - _t3Converter = context.GetConverter(); - _t4Converter = context.GetConverter(); - _t5Converter = context.GetConverter(); - } - - public void Write(TarantoolTuple value, IMsgPackWriter writer) - { - if (value == null) - { - _nullConverter.Write(null, writer); - return; - } - - writer.WriteArrayHeader(5); - _t1Converter.Write(value.Item1, writer); - _t2Converter.Write(value.Item2, writer); - _t3Converter.Write(value.Item3, writer); - _t4Converter.Write(value.Item4, writer); - _t5Converter.Write(value.Item5, writer); - } - - public TarantoolTuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - - const uint expected = 5u; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - var item2 = _t2Converter.Read(reader); - var item3 = _t3Converter.Read(reader); - var item4 = _t4Converter.Read(reader); - var item5 = _t5Converter.Read(reader); - - return TarantoolTuple.Create(item1, item2, item3, item4, item5); - } - } - - public class TupleConverter : IMsgPackConverter> - { - private IMsgPackConverter _nullConverter; - private IMsgPackConverter _t1Converter; - private IMsgPackConverter _t2Converter; - private IMsgPackConverter _t3Converter; - private IMsgPackConverter _t4Converter; - private IMsgPackConverter _t5Converter; - private IMsgPackConverter _t6Converter; - - public void Initialize(MsgPackContext context) - { - _nullConverter = context.NullConverter; - _t1Converter = context.GetConverter(); - _t2Converter = context.GetConverter(); - _t3Converter = context.GetConverter(); - _t4Converter = context.GetConverter(); - _t5Converter = context.GetConverter(); - _t6Converter = context.GetConverter(); - } - - public void Write(TarantoolTuple value, IMsgPackWriter writer) - { - if (value == null) - { - _nullConverter.Write(null, writer); - return; - } - - writer.WriteArrayHeader(6); - _t1Converter.Write(value.Item1, writer); - _t2Converter.Write(value.Item2, writer); - _t3Converter.Write(value.Item3, writer); - _t4Converter.Write(value.Item4, writer); - _t5Converter.Write(value.Item5, writer); - _t6Converter.Write(value.Item6, writer); - } - - public TarantoolTuple Read( - IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - - const uint expected = 6u; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - var item2 = _t2Converter.Read(reader); - var item3 = _t3Converter.Read(reader); - var item4 = _t4Converter.Read(reader); - var item5 = _t5Converter.Read(reader); - var item6 = _t6Converter.Read(reader); - - return TarantoolTuple.Create(item1, item2, item3, item4, item5, item6); - } - } - - public class TupleConverter : IMsgPackConverter> - { - private IMsgPackConverter _nullConverter; - private IMsgPackConverter _t1Converter; - private IMsgPackConverter _t2Converter; - private IMsgPackConverter _t3Converter; - private IMsgPackConverter _t4Converter; - private IMsgPackConverter _t5Converter; - private IMsgPackConverter _t6Converter; - private IMsgPackConverter _t7Converter; - - public void Initialize(MsgPackContext context) - { - _nullConverter = context.NullConverter; - _t1Converter = context.GetConverter(); - _t2Converter = context.GetConverter(); - _t3Converter = context.GetConverter(); - _t4Converter = context.GetConverter(); - _t5Converter = context.GetConverter(); - _t6Converter = context.GetConverter(); - _t7Converter = context.GetConverter(); - } - - public void Write(TarantoolTuple value, IMsgPackWriter writer) - { - if (value == null) - { - _nullConverter.Write(null, writer); - return; - } - - writer.WriteArrayHeader(7); - _t1Converter.Write(value.Item1, writer); - _t2Converter.Write(value.Item2, writer); - _t3Converter.Write(value.Item3, writer); - _t4Converter.Write(value.Item4, writer); - _t5Converter.Write(value.Item5, writer); - _t6Converter.Write(value.Item6, writer); - _t7Converter.Write(value.Item7, writer); - } - - public TarantoolTuple Read( - IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - - const uint expected = 7u; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - var item2 = _t2Converter.Read(reader); - var item3 = _t3Converter.Read(reader); - var item4 = _t4Converter.Read(reader); - var item5 = _t5Converter.Read(reader); - var item6 = _t6Converter.Read(reader); - var item7 = _t7Converter.Read(reader); - - return TarantoolTuple.Create(item1, item2, item3, item4, item5, item6, item7); - } - } - - public class TupleConverter : IMsgPackConverter> - { - private IMsgPackConverter _nullConverter; - private IMsgPackConverter _t1Converter; - private IMsgPackConverter _t2Converter; - private IMsgPackConverter _t3Converter; - private IMsgPackConverter _t4Converter; - private IMsgPackConverter _t5Converter; - private IMsgPackConverter _t6Converter; - private IMsgPackConverter _t7Converter; - private IMsgPackConverter _t8Converter; - - public void Initialize(MsgPackContext context) - { - _nullConverter = context.NullConverter; - _t1Converter = context.GetConverter(); - _t2Converter = context.GetConverter(); - _t3Converter = context.GetConverter(); - _t4Converter = context.GetConverter(); - _t5Converter = context.GetConverter(); - _t6Converter = context.GetConverter(); - _t7Converter = context.GetConverter(); - _t8Converter = context.GetConverter(); - } - - public void Write(TarantoolTuple value, IMsgPackWriter writer) - { - if (value == null) - { - _nullConverter.Write(null, writer); - return; - } - - writer.WriteArrayHeader(8); - _t1Converter.Write(value.Item1, writer); - _t2Converter.Write(value.Item2, writer); - _t3Converter.Write(value.Item3, writer); - _t4Converter.Write(value.Item4, writer); - _t5Converter.Write(value.Item5, writer); - _t6Converter.Write(value.Item6, writer); - _t7Converter.Write(value.Item7, writer); - _t8Converter.Write(value.Item8, writer); - } - - public TarantoolTuple Read( - IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - - const uint expected = 8u; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - var item2 = _t2Converter.Read(reader); - var item3 = _t3Converter.Read(reader); - var item4 = _t4Converter.Read(reader); - var item5 = _t5Converter.Read(reader); - var item6 = _t6Converter.Read(reader); - var item7 = _t7Converter.Read(reader); - var item8 = _t8Converter.Read(reader); - - return new TarantoolTuple(item1, item2, item3, item4, item5, item6, item7, item8); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/UpdateOperationConverter.cs b/src/progaudi.tarantool/Converters/UpdateOperationConverter.cs deleted file mode 100644 index 41defffb..00000000 --- a/src/progaudi.tarantool/Converters/UpdateOperationConverter.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model.UpdateOperations; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class UpdateOperationConverter : IMsgPackConverter>, IMsgPackConverter - { - private IMsgPackConverter _stringConverter; - private IMsgPackConverter _intConverter; - private IMsgPackConverter _argumentConverter; - - public void Initialize(MsgPackContext context) - { - _stringConverter = context.GetConverter(); - _intConverter = context.GetConverter(); - _argumentConverter = context.GetConverter(); - } - - public void Write(UpdateOperation value, IMsgPackWriter writer) - { - writer.WriteArrayHeader(3); - - _stringConverter.Write(value.OperationType, writer); - _intConverter.Write(value.FieldNumber, writer); - _argumentConverter.Write(value.Argument, writer); - } - - public void Write(UpdateOperation value, IMsgPackWriter writer) - { - Write((UpdateOperation) value, writer); - } - - UpdateOperation IMsgPackConverter.Read(IMsgPackReader reader) - { - return Read(reader); - } - - public UpdateOperation Read(IMsgPackReader reader) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/UpdatePacketConverter.cs b/src/progaudi.tarantool/Converters/UpdatePacketConverter.cs deleted file mode 100644 index 630abc08..00000000 --- a/src/progaudi.tarantool/Converters/UpdatePacketConverter.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Requests; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class UpdatePacketConverter : IMsgPackConverter> - { - private IMsgPackConverter _uintConverter; - private IMsgPackConverter _keyConverter; - private IMsgPackConverter _selectKeyConverter; - private MsgPackContext _context; - - public void Initialize(MsgPackContext context) - { - _uintConverter = context.GetConverter(); - _keyConverter = context.GetConverter(); - _selectKeyConverter = context.GetConverter(); - _context = context; - } - - public void Write(UpdateRequest value, IMsgPackWriter writer) - { - writer.WriteMapHeader(4); - - _keyConverter.Write(Key.SpaceId, writer); - _uintConverter.Write(value.SpaceId, writer); - - _keyConverter.Write(Key.IndexId, writer); - _uintConverter.Write(value.IndexId, writer); - - _keyConverter.Write(Key.Key, writer); - _selectKeyConverter.Write(value.Key, writer); - - _keyConverter.Write(Key.Tuple, writer); - writer.WriteArrayHeader((uint) value.UpdateOperations.Length); - - foreach (var updateOperation in value.UpdateOperations) - { - var operationConverter = updateOperation.GetConverter(_context); - operationConverter.Write(updateOperation, writer); - } - } - - public UpdateRequest Read(IMsgPackReader readerr) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/UpsertPacketConverter.cs b/src/progaudi.tarantool/Converters/UpsertPacketConverter.cs deleted file mode 100644 index cdc4dd7d..00000000 --- a/src/progaudi.tarantool/Converters/UpsertPacketConverter.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Requests; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class UpsertPacketConverter : IMsgPackConverter> - { - private IMsgPackConverter _uintConverter; - private IMsgPackConverter _keyConverter; - private IMsgPackConverter _tupleConverter; - private MsgPackContext _context; - - public void Initialize(MsgPackContext context) - { - _uintConverter = context.GetConverter(); - _keyConverter = context.GetConverter(); - _tupleConverter = context.GetConverter(); - _context = context; - } - - public void Write(UpsertRequest value, IMsgPackWriter writer) - { - writer.WriteMapHeader(3); - - _keyConverter.Write(Key.SpaceId, writer); - _uintConverter.Write(value.SpaceId, writer); - - _keyConverter.Write(Key.Tuple, writer); - _tupleConverter.Write(value.Tuple, writer); - - _keyConverter.Write(Key.Ops, writer); - writer.WriteArrayHeader((uint) value.UpdateOperations.Length); - - foreach (var updateOperation in value.UpdateOperations) - { - var operationConverter = updateOperation.GetConverter(_context); - operationConverter.Write(updateOperation, writer); - } - } - - public UpsertRequest Read(IMsgPackReader reader) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/GetOptions.cs b/src/progaudi.tarantool/GetOptions.cs new file mode 100644 index 00000000..8d339f2e --- /dev/null +++ b/src/progaudi.tarantool/GetOptions.cs @@ -0,0 +1,8 @@ +namespace ProGaudi.Tarantool.Client +{ + public enum GetOptions + { + Eval, + Select + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/IBox.cs b/src/progaudi.tarantool/IBox.cs index 2ad3f2db..f6dccc29 100644 --- a/src/progaudi.tarantool/IBox.cs +++ b/src/progaudi.tarantool/IBox.cs @@ -1,7 +1,6 @@ using System; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Responses; namespace ProGaudi.Tarantool.Client { @@ -15,37 +14,12 @@ public interface IBox : IDisposable ISchema Schema { get; } - BoxInfo Info { get; } + ILuaCode GetLuaFunc(string name); - [Obsolete] - ISchema GetSchema(); + ILuaCode GetLuaCode(string code); - Task ReloadSchema(); + BoxInfo Info { get; } Task ReloadBoxInfo(); - - Task Call_1_6(string functionName); - - Task Call_1_6(string functionName, TTuple parameters); - - Task> Call_1_6(string functionName); - - Task> Call_1_6(string functionName, TTuple parameters); - - Task Call(string functionName); - - Task Call(string functionName, TTuple parameters); - - Task> Call(string functionName); - - Task> Call(string functionName, TTuple parameters); - - Task> Eval(string expression, TTuple parameters); - - Task> Eval(string expression); - - Task> ExecuteSql(string query, params SqlParameter[] parameters); - - Task ExecuteSql(string query, params SqlParameter[] parameters); } } \ No newline at end of file diff --git a/src/progaudi.tarantool/IIndex.cs b/src/progaudi.tarantool/IIndex.cs index be9f0c6f..53c7ffa8 100644 --- a/src/progaudi.tarantool/IIndex.cs +++ b/src/progaudi.tarantool/IIndex.cs @@ -1,16 +1,11 @@ using System.Collections.Generic; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Responses; -using ProGaudi.Tarantool.Client.Model.UpdateOperations; namespace ProGaudi.Tarantool.Client { - public interface IIndex + public interface IIndex { - ILogicalConnection LogicalConnection { get; } - uint Id { get; } uint SpaceId { get; } @@ -23,42 +18,166 @@ public interface IIndex IReadOnlyList Parts { get; } - Task> Pairs(TValue value, Iterator iterator); - - Task> Select(TKey key, SelectOptions options = null); - - ///Note: there is no such method in specification http://tarantool.org/doc/book/box/box_index.html. - ///But common sense, and sources https://github.com/tarantool/tarantool/blob/1.7/src/box/lua/index.c says that that method should be - Task> Insert(TTuple tuple); - - ///Note: there is no such method in specification http://tarantool.org/doc/book/box/box_index.html. - ///But common sense, and sources https://github.com/tarantool/tarantool/blob/1.7/src/box/lua/index.c says that that method should be - Task> Replace(TTuple tuple); - - Task Min(); - - Task Min(TKey key); - - Task Max(); - - Task Max(TKey key); - - TTuple Random(int randomValue); - - uint Count(TKey key, Iterator it = Iterator.Eq); - - Task> Update(TKey key, UpdateOperation[] updateOperations); - - Task Upsert(TKey key, UpdateOperation[] updateOperations); - - Task> Delete(TKey key); - - Task Alter(IndexCreationOptions options); - - Task Drop(); - - Task Rename(string indexName); - - Task BSize(); + /// + /// Select tuples using primary index of the space. + /// + Task Select(TKey key, Iterator iterator = Iterator.Eq, uint limit = uint.MaxValue, uint offset = 0); + + /// + /// Inserts a tuple into space. If a space contains sequense, then corresponding element should be nil. + /// + /// Index ought to be unique. + /// Inserted tuple + /// If primary key is duplicated + Task Insert(ref T tuple); + + /// + /// Inserts a tuple into space. If a space contains sequense, then corresponding element should be nil. + /// + /// Index ought to be unique. + /// Inserted tuple + /// If primary key is duplicated + Task Insert(ref TInsertable tuple); + + /// + /// Gets a single element from space by key. + /// + /// + /// When is , then we will send eval request to + /// Tarantool with code return box.space.space-name:get(key), otherwise we will send select request + /// with limit = 2 and validate response on client side. + /// + /// You should measure which one is beneficial for you. + /// + /// Key for element to get. + /// How we should get it: via lua code or via select with limit 2. + /// When there are two or more tuples with given key. + Task Get(TKey key, GetOptions options = GetOptions.Eval); + + /// + /// Replaces the tuple in space. + /// + /// Index ought to be unique. + /// Replaced tuple + Task Replace(T tuple); + + /// + /// Same as . + /// + Task Put(T tuple); + + /// + /// Updates a tuple in space. It is always safe to merge several update operation in one, Tarantool will preserve order. + /// + /// Index ought to be unique. + /// Updated tuple + Task Update(TKey key, UpdateOperation[] updateOperations); + + /// + /// Updates or inserts tuple. + /// + /// + /// Index ought to be unique. + /// + /// Sends an upsert request to Tarantool. This is what happens on Tarantool's side: + /// + /// If there is an existing tuple which matches the key fields of tuple_value, then the request has the same effect as + /// and the are used. If there is no existing tuple which + /// matches the key fields of , then the request has the same effect as and + /// the is used. However, unlike insert or update, upsert will not read a tuple and perform error + /// checks before returning – this is a design feature which enhances throughput but requires more caution on the part of the user. + /// + /// It is illegal to modify a primary-key field. + /// It is illegal to use upsert with a space that has a unique secondary index. + /// + Task Upsert(T tuple, UpdateOperation[] updateOperations); + + /// + /// Updates or inserts tuple. + /// + /// + /// Index ought to be unique. + /// + /// Sends an upsert request to Tarantool. This is what happens on Tarantool's side: + /// + /// If there is an existing tuple which matches the key fields of tuple_value, then the request has the same effect as + /// and the are used. If there is no existing tuple which + /// matches the key fields of , then the request has the same effect as and + /// the is used. However, unlike insert or update, upsert will not read a tuple and perform error + /// checks before returning – this is a design feature which enhances throughput but requires more caution on the part of the user. + /// + /// It is illegal to modify a primary-key field. + /// It is illegal to use upsert with a space that has a unique secondary index. + /// + Task Upsert(TInsertable tuple, UpdateOperation[] updateOperations); + + /// + /// Deletes tuple from space. + /// + /// + /// Index ought to be unique. + /// + /// Vynil storage engine will return null, because of how LSM-trees are working. + /// + /// Deleted tuple. + Task Delete(TKey key); + + /// + /// Find the minimum value in the specified index. + /// + /// + /// Index should be of type. + /// + Task Min(); + + /// + /// Find the first value in the specified index that greater or equal to + /// + /// + /// Index should be of type. + /// Starting with Tarantool version 2.0, will return nothing if key value is not equal to a value in the index. + /// + Task Min(TKey key); + + /// + /// Find the maximum value in the specified index. + /// + /// + /// Index should be of type. + /// + Task Max(); + + /// + /// Find the first value in the specified index that less or equal to + /// + /// + /// Index should be of type. + /// Starting with Tarantool version 2.0, will return nothing if key value is not equal to a value in the index. + /// + Task Max(TKey key); + + /// + /// Find a random value in the specified index. + /// + /// + /// This method is useful when it’s important to get insight into data distribution in an index without having to iterate over the entire data set. + /// Vinyl engine does not support this method. + /// + Task Random(uint seed); + + /// + /// Scans index and returns count of tuples. + /// + Task Count(TKey key, Iterator iterator); + + /// + /// Scans index and returns count of tuples. + /// + Task Count(); + + /// + /// Returns the total number of bytes taken by the index. + /// + Task ByteSize(); } } diff --git a/src/progaudi.tarantool/ILogicalConnection.cs b/src/progaudi.tarantool/ILogicalConnection.cs index b33bcdb3..9a5b73ab 100644 --- a/src/progaudi.tarantool/ILogicalConnection.cs +++ b/src/progaudi.tarantool/ILogicalConnection.cs @@ -1,8 +1,7 @@ using System; +using System.Buffers; using System.Threading.Tasks; - -using ProGaudi.Tarantool.Client.Model.Requests; -using ProGaudi.Tarantool.Client.Model.Responses; +using ProGaudi.Tarantool.Client.Model; namespace ProGaudi.Tarantool.Client { @@ -21,7 +20,7 @@ Task> SendRequest(TRequest reques Task SendRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest; - Task SendRawRequest(TRequest request, TimeSpan? timeout = null) + Task> SendRawRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest; uint PingsFailedByTimeoutCount { get; } diff --git a/src/progaudi.tarantool/ILuaCode.cs b/src/progaudi.tarantool/ILuaCode.cs new file mode 100644 index 00000000..7614c630 --- /dev/null +++ b/src/progaudi.tarantool/ILuaCode.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; + +namespace ProGaudi.Tarantool.Client +{ + public interface ILuaCode + { + Task Invoke(T param); + Task Invoke(T1 param1, T2 param2); + Task Invoke(T1 param1, T2 param2, T3 param3); + Task Invoke(T1 param1, T2 param2, T3 param3, T4 param4); + Task Invoke(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5); + Task Invoke(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6); + Task Invoke(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7); + Task Invoke(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7, T8 param8); + Task Invoke(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7, T8 param8, T9 param9); + + Task Invoke(params object[] parameters); + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/IPhysicalConnection.cs b/src/progaudi.tarantool/IPhysicalConnection.cs index 65c8c9d6..db2c17b8 100644 --- a/src/progaudi.tarantool/IPhysicalConnection.cs +++ b/src/progaudi.tarantool/IPhysicalConnection.cs @@ -1,4 +1,6 @@ using System; +using System.Buffers; +using System.IO; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; @@ -14,6 +16,8 @@ public interface IPhysicalConnection : IDisposable Task ReadAsync(byte[] buffer, int offset, int count); - void Write(byte[] buffer, int offset, int count); + void Write(in ReadOnlySequence buffer); + + Stream Stream { get; } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/IRequestWriter.cs b/src/progaudi.tarantool/IRequestWriter.cs index 90eb8c44..04d34225 100644 --- a/src/progaudi.tarantool/IRequestWriter.cs +++ b/src/progaudi.tarantool/IRequestWriter.cs @@ -1,5 +1,5 @@ using System; -using System.Threading.Tasks; +using System.Buffers; namespace ProGaudi.Tarantool.Client { @@ -9,6 +9,6 @@ internal interface IRequestWriter : IDisposable bool IsConnected { get; } - void Write(ArraySegment header, ArraySegment body); + void Write(in ReadOnlySequence body); } } \ No newline at end of file diff --git a/src/progaudi.tarantool/IResponseReader.cs b/src/progaudi.tarantool/IResponseReader.cs index c8df0334..996b24ed 100644 --- a/src/progaudi.tarantool/IResponseReader.cs +++ b/src/progaudi.tarantool/IResponseReader.cs @@ -10,7 +10,7 @@ public interface IResponseReader : IDisposable { void BeginReading(); - Task GetResponseTask(RequestId requestId); + Task GetResponseTask(RequestId requestId, Func responseCreator); bool IsConnected { get; } } diff --git a/src/progaudi.tarantool/ISchema.cs b/src/progaudi.tarantool/ISchema.cs index b0d1e9cb..0916db26 100644 --- a/src/progaudi.tarantool/ISchema.cs +++ b/src/progaudi.tarantool/ISchema.cs @@ -5,16 +5,33 @@ namespace ProGaudi.Tarantool.Client { public interface ISchema { - [Obsolete("Use indexer")] - Task GetSpace(string name); - [Obsolete("Use indexer")] - Task GetSpace(uint id); + /// + /// Gets typed space by . + /// + /// Type of record, stored in space. + /// Name of space + /// Typed wrapper for space, created on every call. It is advisable to cache it. + /// true, if space info presents on client, false otherwise. + bool TryGetSpace(string name, out ISpace space); - ISpace this[string name] { get; } - ISpace this[uint id] { get; } + /// + /// Gets typed space by . + /// + /// Type of record, stored in space. + /// Id of space + /// Typed wrapper for space, created on every call. It is advisable to cache it. + /// true, if space info presents on client, false otherwise. + bool TryGetSpace(uint id, out ISpace space); + /// + /// Reload scheme. + /// + /// Task Reload(); + /// + /// When schema was reloaded last time. + /// DateTimeOffset LastReloadTime { get; } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/ISpace.cs b/src/progaudi.tarantool/ISpace.cs index f6e04e4f..57cc14c9 100644 --- a/src/progaudi.tarantool/ISpace.cs +++ b/src/progaudi.tarantool/ISpace.cs @@ -1,61 +1,181 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; +using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Responses; -using ProGaudi.Tarantool.Client.Model.UpdateOperations; namespace ProGaudi.Tarantool.Client { - public interface ISpace + public interface ISpace { uint Id { get; } - uint FieldCount { get; } + int OwnerId { get; } string Name { get; } StorageEngine Engine { get; } - IReadOnlyCollection Indices { get; } - - IReadOnlyCollection Fields { get; } - - ILogicalConnection LogicalConnection { get; } - - [Obsolete("Use indexer")] - Task GetIndex(string indexName); - - [Obsolete("Use indexer")] - Task GetIndex(uint indexId); - - IIndex this[string name] { get; } - - IIndex this[uint id] { get; } - - Task> Insert(TTuple tuple); - - Task> Select(TKey selectKey); - - Task Get(TKey key); - - Task> Replace(TTuple tuple); - - Task Put(T tuple); - - Task> Update(TKey key, UpdateOperation[] updateOperations); - - Task Upsert(TTuple tuple, UpdateOperation[] updateOperations); - - Task> Delete(TKey key); - - Task Count(TKey key); - - Task Length(); - - Task> Increment(TKey key); + uint FieldCount { get; } - Task> Decrement(TKey key); + SpaceOptions Options { get; } + + SpaceField[] Fields { get; } + + /// + /// Gets an index by name. Uses local cache. + /// + IIndex this[string name] { get; } + + /// + /// Gets an index by id. Uses local cache. + /// + IIndex this[uint id] { get; } + + /// + /// Gets an index by name, but does not throw exceptions. Uses local cache. + /// + bool TryGetIndex(string name, out IIndex index); + + /// + /// Gets an index by id, but does not throw exceptions. Uses local cache. + /// + bool TryGetIndex(uint id, out IIndex index); + + /// + /// Gets an index by name and change type of tuple, but does not throw exceptions. Uses local cache. + /// + bool TryGetIndex(string name, out IIndex index); + + /// + /// Gets an index by id and change type of tuple, but does not throw exceptions. Uses local cache. + /// + bool TryGetIndex(uint id, out IIndex index); + + /// + /// Inserts a tuple into space. If a space contains sequense, then corresponding element should be nil. + /// + /// Inserted tuple + /// If primary key is duplicated + Task Insert(T tuple); + + /// + /// Inserts a tuple into space. If a space contains sequense, then corresponding element should be nil. + /// + /// Inserted tuple + /// If primary key is duplicated + Task Insert(in TInsertable tuple); + + /// + /// Select tuples using primary index of the space. + /// + Task Select(TKey selectKey, Iterator iterator = Iterator.Eq, uint limit = uint.MaxValue, uint offset = 0u); + + /// + /// Gets a single element from space by key. + /// + /// + /// When is , then we will send eval request to + /// Tarantool with code return box.space.space-name:get(key), otherwise we will send select request + /// with limit = 2 and validate response on client side. + /// + /// You should measure which one is beneficial for you. + /// + /// Key for element to get. + /// How we should get it: via lua code or via select with limit 2. + /// When there are two or more tuples with given key. + Task Get(TKey key, GetOptions options = GetOptions.Eval); + + /// + /// Replaces the tuple in space. + /// + /// Replaced tuple + Task Replace(T tuple); + + /// + /// Same as . + /// + Task Put(T tuple); + + /// + /// Updates a tuple in space. It is always safe to merge several update operation in one, Tarantool will preserve order. + /// + /// Updated tuple + Task Update(TKey key, UpdateOperation[] updateOperations); + + /// + /// Updates or inserts tuple. + /// + /// + /// Sends an upsert request to Tarantool. This is what happens on Tarantool's side: + /// + /// If there is an existing tuple which matches the key fields of tuple_value, then the request has the same effect as + /// and the are used. If there is no existing tuple which + /// matches the key fields of , then the request has the same effect as and + /// the is used. However, unlike insert or update, upsert will not read a tuple and perform error + /// checks before returning – this is a design feature which enhances throughput but requires more caution on the part of the user. + /// + /// It is illegal to modify a primary-key field. + /// It is illegal to use upsert with a space that has a unique secondary index. + /// + Task Upsert(T tuple, UpdateOperation[] updateOperations); + + /// + /// Updates or inserts tuple. + /// + /// + /// Sends an upsert request to Tarantool. This is what happens on Tarantool's side: + /// + /// If there is an existing tuple which matches the key fields of tuple_value, then the request has the same effect as + /// and the are used. If there is no existing tuple which + /// matches the key fields of , then the request has the same effect as and + /// the is used. However, unlike insert or update, upsert will not read a tuple and perform error + /// checks before returning – this is a design feature which enhances throughput but requires more caution on the part of the user. + /// + /// It is illegal to modify a primary-key field. + /// It is illegal to use upsert with a space that has a unique secondary index. + /// + Task Upsert(TInsertable tuple, UpdateOperation[] updateOperations); + + /// + /// Deletes tuple from space. + /// + /// + /// Vynil storage engine will return null, because of how LSM-trees are working. + /// + /// Deleted tuple. + Task Delete(TKey key); + + /// + /// Return count of tuples. + /// + /// + /// If compared with , this methos works slower, because it scans space, according to + /// and + /// + Task Count(TKey key, Iterator iterator); + + /// + /// Return count of tuples. + /// + /// + /// If compared with , this methos works slower, because it scans the entire space. + /// + Task Count(); + + /// + /// Return the number of tuples in the space. + /// + /// + /// If compared with , this method works faster because it does not scan the entire space to count + /// the tuples, but vinyl does not support it. + /// + Task Length(); + + /// + /// Number of bytes in the space. + /// + /// + /// This number, which is stored in Tarantool’s internal memory, represents the total number of bytes in all tuples, not + /// including index keys. For a measure of index size, you'll need to iterate on all indexes + /// + Task ByteSize(); } } diff --git a/src/progaudi.tarantool/Index.cs b/src/progaudi.tarantool/Index.cs index ee8e0337..34180a9d 100644 --- a/src/progaudi.tarantool/Index.cs +++ b/src/progaudi.tarantool/Index.cs @@ -1,171 +1,120 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Requests; -using ProGaudi.Tarantool.Client.Model.Responses; -using ProGaudi.Tarantool.Client.Model.UpdateOperations; -using ProGaudi.Tarantool.Client.Utils; namespace ProGaudi.Tarantool.Client { - public class Index : IIndex + public class Index : IIndex { - public ILogicalConnection LogicalConnection { get; set; } + private readonly IndexMeta _meta; + private readonly Schema _schema; - public Index(uint id, uint spaceId, string name, bool unique, IndexType type, IReadOnlyList parts) + public Index(IndexMeta meta, Schema schema) { - Id = id; - SpaceId = spaceId; - Name = name; - Unique = unique; - Type = type; - Parts = parts; + _meta = meta; + _schema = schema; } - public uint Id { get; } + public uint Id => _meta.Id; - public uint SpaceId { get; } + public uint SpaceId => _meta.SpaceId; - public string Name { get; } + public string Name => _meta.Name; - public bool Unique { get; } + public bool Unique => _meta.Options.Unique; - public IndexType Type { get; } + public IndexType Type => _meta.Type; - public IReadOnlyList Parts { get; } + public IReadOnlyList Parts => _meta.Parts; - public Task> Pairs(TValue value, Iterator iterator) + public Task Select(TKey key, Iterator iterator = Iterator.Eq, uint limit = uint.MaxValue, uint offset = 0) { throw new NotImplementedException(); } - public async Task> Select(TKey key, SelectOptions options = null) + public Task Insert(ref T tuple) { - var selectRequest = new SelectRequest( - SpaceId, - Id, - options?.Limit ?? uint.MaxValue, - options?.Offset ?? 0, - options?.Iterator ?? Iterator.Eq, - key); - - return await LogicalConnection.SendRequest, TTuple>(selectRequest).ConfigureAwait(false); + throw new NotImplementedException(); } - ///Note: there is no such method in specification http://tarantool.org/doc/book/box/box_index.html. - ///But common sense, and sources https://github.com/tarantool/tarantool/blob/1.7/src/box/lua/index.c says that that method sould be - public async Task> Insert(TTuple tuple) + public Task Insert(ref TInsertable tuple) { - var insertRequest = new InsertRequest(SpaceId, tuple); - - return await LogicalConnection.SendRequest, TTuple>(insertRequest).ConfigureAwait(false); + throw new NotImplementedException(); } - ///Note: there is no such method in specification http://tarantool.org/doc/book/box/box_index.html. - ///But common sense, and sources https://github.com/tarantool/tarantool/blob/1.7/src/box/lua/index.c says that that method sould be - public async Task> Replace(TTuple tuple) + public Task Get(TKey key, GetOptions options = GetOptions.Eval) { - var replaceRequest = new ReplaceRequest(SpaceId, tuple); - - return await LogicalConnection.SendRequest, TTuple>(replaceRequest).ConfigureAwait(false); + throw new NotImplementedException(); } - public async Task Min() + public Task Replace(T tuple) { - return await Min(TarantoolTuple.Empty).ConfigureAwait(false); + throw new NotImplementedException(); } - public async Task Min(TKey key) + public Task Put(T tuple) { - if (Type != IndexType.Tree) - { - throw ExceptionHelper.WrongIndexType("TREE", "min"); - } - var iterator = key == null ? Iterator.Eq : Iterator.Ge; - - var selectPacket = new SelectRequest(SpaceId, Id, 1, 0, iterator, key); - - var minResponse = await LogicalConnection.SendRequest, TTuple>(selectPacket).ConfigureAwait(false); - return minResponse.Data.SingleOrDefault(); + throw new NotImplementedException(); } - public async Task Max() + public Task Update(TKey key, UpdateOperation[] updateOperations) { - return await Max(TarantoolTuple.Empty).ConfigureAwait(false); + throw new NotImplementedException(); } - public async Task Max(TKey key) + public Task Upsert(T tuple, UpdateOperation[] updateOperations) { - if (Type != IndexType.Tree) - { - throw ExceptionHelper.WrongIndexType("TREE", "max"); - } - var iterator = key == null ? Iterator.Req : Iterator.Le; - - var selectPacket = new SelectRequest(SpaceId, Id, 1, 0, iterator, key); - - var maxResponse = await LogicalConnection.SendRequest, TTuple>(selectPacket).ConfigureAwait(false); - return maxResponse.Data.SingleOrDefault(); + throw new NotImplementedException(); } - public TTuple Random(int randomValue) + public Task Upsert(TInsertable tuple, UpdateOperation[] updateOperations) { throw new NotImplementedException(); } - public uint Count(TKey key, Iterator it = Iterator.Eq) + public Task Delete(TKey key) { throw new NotImplementedException(); } - public async Task> Update(TKey key, UpdateOperation[] updateOperations) + public Task Min() { - var updateRequest = new UpdateRequest( - SpaceId, - Id, - key, - updateOperations); - - return await LogicalConnection.SendRequest, TTuple>(updateRequest).ConfigureAwait(false); + throw new NotImplementedException(); } - public async Task Upsert(TKey key, UpdateOperation[] updateOperations) + public Task Min(TKey key) { - var updateRequest = new UpsertRequest( - SpaceId, - key, - updateOperations); - - await LogicalConnection.SendRequestWithEmptyResponse(updateRequest).ConfigureAwait(false); + throw new NotImplementedException(); } - public async Task> Delete(TKey key) + public Task Max() { - var deleteRequest = new DeleteRequest(SpaceId, Id, key); + throw new NotImplementedException(); + } - return await LogicalConnection.SendRequest, TTuple>(deleteRequest).ConfigureAwait(false); + public Task Max(TKey key) + { + throw new NotImplementedException(); } - public Task Alter(IndexCreationOptions options) + public Task Random(uint seed) { throw new NotImplementedException(); } - public Task Drop() + public Task Count(TKey key, Iterator iterator) { throw new NotImplementedException(); } - public Task Rename(string indexName) + public Task Count() { throw new NotImplementedException(); } - public Task BSize() + public Task ByteSize() { throw new NotImplementedException(); } diff --git a/src/progaudi.tarantool/IndexMeta.cs b/src/progaudi.tarantool/IndexMeta.cs new file mode 100644 index 00000000..ac58a212 --- /dev/null +++ b/src/progaudi.tarantool/IndexMeta.cs @@ -0,0 +1,70 @@ +using System; +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Model; + +namespace ProGaudi.Tarantool.Client +{ + [MsgPackArray] + public class IndexMeta : IEquatable + { + public IndexMeta(uint spaceId, uint id, string name, IndexType type, IndexCreationOptions options, IndexPart[] parts) + { + SpaceId = spaceId; + Id = id; + Name = name; + Type = type; + Options = options; + Parts = parts; + } + + public IndexMeta() + { + } + + [MsgPackArrayElement(0)] + public uint SpaceId { get; set; } + + [MsgPackArrayElement(1)] + public uint Id { get; set; } + + [MsgPackArrayElement(2)] + public string Name { get; set; } + + [MsgPackArrayElement(3)] + public IndexType Type { get; set; } + + [MsgPackArrayElement(4)] + public IndexCreationOptions Options { get; set; } + + [MsgPackArrayElement(5)] + public IndexPart[] Parts { get; set; } + + public override string ToString() + { + return $"Index: {Name} ({Id}), Unique: {Options.Unique}, Space: {SpaceId}, Parts: {Parts.Length}"; + } + + public bool Equals(IndexMeta other) + { + return SpaceId == other.SpaceId && Id == other.Id && string.Equals(Name, other.Name) && Type == other.Type; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + return obj is IndexMeta meta && Equals(meta); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = (int) SpaceId; + hashCode = (hashCode * 397) ^ (int) Id; + hashCode = (hashCode * 397) ^ (Name != null ? Name.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (int) Type; + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/LogicalConnection.cs b/src/progaudi.tarantool/LogicalConnection.cs index 4ff07e7b..34db864c 100644 --- a/src/progaudi.tarantool/LogicalConnection.cs +++ b/src/progaudi.tarantool/LogicalConnection.cs @@ -1,22 +1,25 @@ using System; +using System.Buffers; +using System.Buffers.Binary; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; - using ProGaudi.MsgPack.Light; - using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Headers; -using ProGaudi.Tarantool.Client.Model.Requests; -using ProGaudi.Tarantool.Client.Model.Responses; using ProGaudi.Tarantool.Client.Utils; namespace ProGaudi.Tarantool.Client { internal class LogicalConnection : ILogicalConnection { - private readonly MsgPackContext _msgPackContext; + private static readonly Func> RawByteCreator = x => + { + var length = (int) (x.Length - x.Position); + var owner = MemoryPool.Shared.Rent(length); + new ReadOnlyMemory(x.GetBuffer(), (int) x.Position, length).CopyTo(owner.Memory); + return owner; + }; private readonly ClientOptions _clientOptions; @@ -36,7 +39,6 @@ public LogicalConnection(ClientOptions options, RequestIdCounter requestIdCounte { _clientOptions = options; _requestIdCounter = requestIdCounter; - _msgPackContext = options.MsgPackContext; _logWriter = options.LogWriter; _physicalConnection = new NetworkStreamPhysicalConnection(); @@ -99,30 +101,28 @@ public bool IsConnected() return _responseReader.IsConnected && _requestWriter.IsConnected && _physicalConnection.IsConnected; } - public async Task SendRequestWithEmptyResponse(TRequest request, TimeSpan? timeout = null) + public Task SendRequestWithEmptyResponse(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { - await SendRequestImpl(request, timeout).ConfigureAwait(false); + return SendRequestImpl(request, _ => null, timeout); } - public async Task> SendRequest(TRequest request, TimeSpan? timeout = null) + public Task> SendRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { - var stream = await SendRequestImpl(request, timeout).ConfigureAwait(false); - return MsgPackSerializer.Deserialize>(stream, _msgPackContext); + return SendRequestImpl(request, buffer => MsgPackSerializer.Deserialize>(buffer, _clientOptions.MsgPackContext), timeout); } - public async Task SendRequest(TRequest request, TimeSpan? timeout = null) + public Task SendRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { - var stream = await SendRequestImpl(request, timeout).ConfigureAwait(false); - return MsgPackSerializer.Deserialize(stream, _msgPackContext); + return SendRequestImpl(request, buffer => MsgPackSerializer.Deserialize(buffer, _clientOptions.MsgPackContext), timeout); } - public async Task SendRawRequest(TRequest request, TimeSpan? timeout = null) + public Task> SendRawRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { - return (await SendRequestImpl(request, timeout).ConfigureAwait(false)).ToArray(); + return SendRequestImpl(request, RawByteCreator, timeout); } private async Task LoginIfNotGuest(GreetingsResponse greetings) @@ -141,7 +141,7 @@ private async Task LoginIfNotGuest(GreetingsResponse greetings) _clientOptions.LogWriter?.WriteLine($"Authentication request send: {authenticateRequest}"); } - private async Task SendRequestImpl(TRequest request, TimeSpan? timeout) + private Task SendRequestImpl(TRequest request, Func response, TimeSpan? timeout) where TRequest : IRequest { if (_disposed) @@ -149,15 +149,15 @@ private async Task SendRequestImpl(TRequest request, Tim throw new ObjectDisposedException(nameof(LogicalConnection)); } - var bodyBuffer = MsgPackSerializer.Serialize(request, _msgPackContext); - var requestId = _requestIdCounter.GetRequestId(); - var responseTask = _responseReader.GetResponseTask(requestId); - - var headerBuffer = CreateAndSerializeHeader(request, requestId, bodyBuffer); - _requestWriter.Write( - headerBuffer, - new ArraySegment(bodyBuffer, 0, bodyBuffer.Length)); + var header = MsgPackSerializer.Serialize(new RequestHeader(request.Code, requestId), _clientOptions.MsgPackContext); + var body = MsgPackSerializer.Serialize(request, _clientOptions.MsgPackContext); + var length = new byte[5]; + length[0] = (byte) DataTypes.UInt32; + BinaryPrimitives.WriteUInt32BigEndian(new Span(length, 1, 4), (uint) (header.Length + body.Length)); + var responseTask = _responseReader.GetResponseTask(requestId, response); + var bodyBuffer = TarantoolSegment.CreateSequence(length, header, body); + _requestWriter.Write(bodyBuffer); try { @@ -167,14 +167,11 @@ private async Task SendRequestImpl(TRequest request, Tim responseTask = responseTask.WithCancellation(cts.Token); } - var responseStream = await responseTask.ConfigureAwait(false); - _logWriter?.WriteLine($"Response with requestId {requestId} is recieved, length: {responseStream.Length}."); - - return responseStream; + return responseTask; } catch (ArgumentException) { - _logWriter?.WriteLine($"Response with requestId {requestId} failed, header:\n{headerBuffer.ToReadableString()} \n body: \n{bodyBuffer.ToReadableString()}"); + _logWriter?.WriteLine($"Request with requestId {requestId} failed, body: \n{bodyBuffer.ToReadableString()}"); throw; } catch (TimeoutException) @@ -183,25 +180,5 @@ private async Task SendRequestImpl(TRequest request, Tim throw; } } - - private ArraySegment CreateAndSerializeHeader( - TRequest request, - RequestId requestId, - byte[] serializedRequest) where TRequest : IRequest - { - var packetSizeBuffer = new byte[Constants.PacketSizeBufferSize + Constants.MaxHeaderLength]; - var stream = new MemoryStream(packetSizeBuffer); - - var requestHeader = new RequestHeader(request.Code, requestId); - stream.Seek(Constants.PacketSizeBufferSize, SeekOrigin.Begin); - MsgPackSerializer.Serialize(requestHeader, stream, _msgPackContext); - - var lengthAndHeaderLengthByteCount = (int)stream.Position; - var headerLength = lengthAndHeaderLengthByteCount - Constants.PacketSizeBufferSize; - var packetLength = new PacketSize((uint) (headerLength + serializedRequest.Length)); - stream.Seek(0, SeekOrigin.Begin); - MsgPackSerializer.Serialize(packetLength, stream, _msgPackContext); - return new ArraySegment(packetSizeBuffer, 0, lengthAndHeaderLengthByteCount); - } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/LogicalConnectionManager.cs b/src/progaudi.tarantool/LogicalConnectionManager.cs index 66f03505..5d8d7d62 100644 --- a/src/progaudi.tarantool/LogicalConnectionManager.cs +++ b/src/progaudi.tarantool/LogicalConnectionManager.cs @@ -1,10 +1,9 @@ using System; +using System.Buffers; using System.Threading; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Requests; -using ProGaudi.Tarantool.Client.Model.Responses; using ProGaudi.Tarantool.Client.Utils; namespace ProGaudi.Tarantool.Client @@ -167,7 +166,7 @@ public async Task SendRequest(TRequest request, TimeSpan return result; } - public async Task SendRawRequest(TRequest request, TimeSpan? timeout = null) + public async Task> SendRawRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { await Connect().ConfigureAwait(false); diff --git a/src/progaudi.tarantool/Model/AuthenticationRequest.cs b/src/progaudi.tarantool/Model/AuthenticationRequest.cs new file mode 100644 index 00000000..155305c0 --- /dev/null +++ b/src/progaudi.tarantool/Model/AuthenticationRequest.cs @@ -0,0 +1,94 @@ +using System; +using System.Linq; +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Utils; + +namespace ProGaudi.Tarantool.Client.Model +{ + public class AuthenticationRequest : IRequest + { + private AuthenticationRequest(string username, byte[] scramble) + { + Username = username; + Scramble = scramble; + } + + public string Username { get; } + + public byte[] Scramble { get; } + + public CommandCodes Code => CommandCodes.Auth; + + public static AuthenticationRequest Create(GreetingsResponse greetings, UriBuilder uri) + { + var scrable = GetScrable(greetings, uri.Password); + var authenticationPacket = new AuthenticationRequest(uri.UserName, scrable); + return authenticationPacket; + } + + private static byte[] GetScrable(GreetingsResponse greetings, string password) + { + var decodedSalt = greetings.Salt; + var first20SaltBytes = new byte[20]; + Array.Copy(decodedSalt, first20SaltBytes, 20); + + var step1 = Sha1Utils.Hash(password); + var step2 = Sha1Utils.Hash(step1); + var step3 = Sha1Utils.Hash(first20SaltBytes, step2); + var scrambleBytes = Sha1Utils.Xor(step1, step3); + + return scrambleBytes; + } + + public override string ToString() + { + return $"Username: {Username}, Scramble: {ToReadableString(Scramble)}"; + } + + private static string ToReadableString(byte[] bytes) + { + return string.Concat(bytes.Select(b => b.ToString("X2 "))); + } + + internal class Formatter : IMsgPackConverter + { + private IMsgPackConverter _keyConverter; + private IMsgPackConverter _bytesConverter; + private IMsgPackConverter _stringConverter; + private IMsgPackConverter _nullConverter; + + public void Initialize(MsgPackContext context) + { + _keyConverter = context.GetConverter(); + _bytesConverter = context.GetConverter(); + _stringConverter = context.GetConverter(); + _nullConverter = context.NullConverter; + } + + public void Write(AuthenticationRequest value, IMsgPackWriter writer) + { + if (value == null) + { + _nullConverter.Write(null, writer); + return; + } + + writer.WriteMapHeader(2); + + _keyConverter.Write(Keys.Username, writer); + _stringConverter.Write(value.Username, writer); + + _keyConverter.Write(Keys.Tuple, writer); + + writer.WriteArrayHeader(2); + _stringConverter.Write("chap-sha1", writer); + _bytesConverter.Write(value.Scramble, writer); + } + + public AuthenticationRequest Read(IMsgPackReader reader) + { + throw new NotImplementedException(); + } + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/BoxInfo.cs b/src/progaudi.tarantool/Model/BoxInfo.cs index d3760aea..b0147141 100644 --- a/src/progaudi.tarantool/Model/BoxInfo.cs +++ b/src/progaudi.tarantool/Model/BoxInfo.cs @@ -1,5 +1,4 @@ using System; -using ProGaudi.MsgPack.Light; namespace ProGaudi.Tarantool.Client.Model { @@ -17,76 +16,76 @@ public class BoxInfo public TarantoolVersion Version { get; private set; } - public class Converter : IMsgPackConverter - { - private IMsgPackConverter _stringConverter; - private MsgPackContext _context; - private bool _initialized; - private IMsgPackConverter _longConverter; - private IMsgPackConverter _boolConverter; + //public class Converter : IMsgPackConverter + //{ + // private IMsgPackConverter _stringConverter; + // private MsgPackContext _context; + // private bool _initialized; + // private IMsgPackConverter _longConverter; + // private IMsgPackConverter _boolConverter; - public void Initialize(MsgPackContext context) - { - _context = context; - } + // public void Initialize(MsgPackContext context) + // { + // _context = context; + // } - public void Write(BoxInfo value, IMsgPackWriter writer) - { - throw new NotSupportedException(); - } + // public void Write(BoxInfo value, IMsgPackWriter writer) + // { + // throw new NotSupportedException(); + // } - public BoxInfo Read(IMsgPackReader reader) - { - if (!_initialized) - { - InitializeIfNeeded(); - } + // public BoxInfo Read(IMsgPackReader reader) + // { + // if (!_initialized) + // { + // InitializeIfNeeded(); + // } - var mapLength = reader.ReadMapLength(); - if (!mapLength.HasValue) - { - return null; - } + // var mapLength = reader.ReadMapLength(); + // if (!mapLength.HasValue) + // { + // return null; + // } - var result = new BoxInfo(); - for (var i = 0; i < mapLength; i++) - { - switch (_stringConverter.Read(reader)) - { - case "id": - result.Id = _longConverter.Read(reader); - break; - case "lsn": - result.Lsn = _longConverter.Read(reader); - break; - case "pid": - result.Pid = _longConverter.Read(reader); - break; - case "ro": - result.ReadOnly = _boolConverter.Read(reader); - break; - case "uuid": - result.Uuid = Guid.Parse(_stringConverter.Read(reader)); - break; - case "version": - result.Version = TarantoolVersion.Parse(_stringConverter.Read(reader)); - break; - default: - reader.SkipToken(); - break; - } - } + // var result = new BoxInfo(); + // for (var i = 0; i < mapLength; i++) + // { + // switch (_stringConverter.Read(reader)) + // { + // case "id": + // result.Id = _longConverter.Read(reader); + // break; + // case "lsn": + // result.Lsn = _longConverter.Read(reader); + // break; + // case "pid": + // result.Pid = _longConverter.Read(reader); + // break; + // case "ro": + // result.ReadOnly = _boolConverter.Read(reader); + // break; + // case "uuid": + // result.Uuid = Guid.Parse(_stringConverter.Read(reader)); + // break; + // case "version": + // result.Version = TarantoolVersion.Parse(_stringConverter.Read(reader)); + // break; + // default: + // reader.SkipToken(); + // break; + // } + // } - return result; - } + // return result; + // } - private void InitializeIfNeeded() - { - _initialized = true; - _stringConverter = _context.GetConverter(); - _longConverter = _context.GetConverter(); - _boolConverter = _context.GetConverter(); - } - } + // private void InitializeIfNeeded() + // { + // _initialized = true; + // _stringConverter = _context.GetConverter(); + // _longConverter = _context.GetConverter(); + // _boolConverter = _context.GetConverter(); + // } + //} } } \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/CallRequest.cs b/src/progaudi.tarantool/Model/CallRequest.cs new file mode 100644 index 00000000..abd1df5f --- /dev/null +++ b/src/progaudi.tarantool/Model/CallRequest.cs @@ -0,0 +1,53 @@ +using System; +using ProGaudi.MsgPack.Light; + +namespace ProGaudi.Tarantool.Client.Model +{ + public class CallRequest : IRequest + { + private readonly bool _use17; + + public CallRequest(string functionName, T tuple, bool use17 = true) + { + _use17 = use17; + FunctionName = functionName; + Tuple = tuple; + } + + public string FunctionName { get; } + + public T Tuple { get; } + + public CommandCodes Code => _use17 ? CommandCodes.Call : CommandCodes.OldCall; + + internal class Formatter : IMsgPackConverter> + { + private IMsgPackConverter _keyConverter; + private IMsgPackConverter _stringConverter; + private IMsgPackConverter _tupleConverter; + + public void Initialize(MsgPackContext context) + { + _keyConverter = context.GetConverter(); + _stringConverter = context.GetConverter(); + _tupleConverter = context.GetConverter(); + } + + public void Write(CallRequest value, IMsgPackWriter writer) + { + writer.WriteMapHeader(2); + + _keyConverter.Write(Keys.FunctionName, writer); + _stringConverter.Write(value.FunctionName, writer); + + _keyConverter.Write(Keys.Tuple, writer); + _tupleConverter.Write(value.Tuple, writer); + } + + public CallRequest Read(IMsgPackReader reader) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/src/progaudi.tarantool/Model/Enums/CommandCode.cs b/src/progaudi.tarantool/Model/CommandCodes.cs similarity index 85% rename from src/progaudi.tarantool/Model/Enums/CommandCode.cs rename to src/progaudi.tarantool/Model/CommandCodes.cs index 6c85dc0c..19dc5d5e 100644 --- a/src/progaudi.tarantool/Model/Enums/CommandCode.cs +++ b/src/progaudi.tarantool/Model/CommandCodes.cs @@ -1,6 +1,6 @@ -namespace ProGaudi.Tarantool.Client.Model.Enums +namespace ProGaudi.Tarantool.Client.Model { - public enum CommandCode : uint + public enum CommandCodes : uint { //User command codes Select = 0x01, diff --git a/src/progaudi.tarantool/Model/ConnectionOptions.cs b/src/progaudi.tarantool/Model/ConnectionOptions.cs index 7fd6b02b..ffd8986d 100644 --- a/src/progaudi.tarantool/Model/ConnectionOptions.cs +++ b/src/progaudi.tarantool/Model/ConnectionOptions.cs @@ -46,6 +46,6 @@ private void Parse(string replicationSource, ILog log) public bool ReadSchemaOnConnect { get; set; } = true; - public bool ReadBoxInfoOnConnect { get; set; } = true; + public bool ReadBoxInfoOnConnect { get; set; } = false; } } \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/DataResponse.cs b/src/progaudi.tarantool/Model/DataResponse.cs new file mode 100644 index 00000000..877fb31c --- /dev/null +++ b/src/progaudi.tarantool/Model/DataResponse.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Utils; + +namespace ProGaudi.Tarantool.Client.Model +{ + public class DataResponse + { + public DataResponse(SqlInfo sqlInfo) + { + SqlInfo = sqlInfo; + } + + public SqlInfo SqlInfo { get; } + + public sealed class Formatter : IMsgPackConverter + { + private IMsgPackConverter _keyConverter; + private IMsgPackConverter _intConverter; + + public void Initialize(MsgPackContext context) + { + _keyConverter = context.GetConverter(); + _intConverter = context.GetConverter(); + } + + public void Write(DataResponse value, IMsgPackWriter writer) + { + throw new NotSupportedException(); + } + + public DataResponse Read(IMsgPackReader reader) + { + var length = reader.ReadMapLength(); + if (!(1u <= length && length <= 3)) + { + throw ExceptionHelper.InvalidMapLength(length, 1u, 2u); + } + + var sqlInfo = default(SqlInfo); + + for (var i = 0; i < length; i++) + { + var dataKey = _keyConverter.Read(reader); + switch (dataKey) + { + case Keys.SqlInfo: + sqlInfo = ReadSqlInfo(reader, _keyConverter, _intConverter); + break; + default: + throw ExceptionHelper.UnexpectedKey(dataKey, Keys.SqlInfo); + } + } + + return new DataResponse(sqlInfo); + } + + internal static SqlInfo ReadSqlInfo(IMsgPackReader reader, IMsgPackConverter keyConverter, IMsgPackConverter intConverter) + { + var length = reader.ReadMapLength(); + if (length == null) + { + return null; + } + + var result = default(SqlInfo); + for (var i = 0; i < length; i++) + { + switch (keyConverter.Read(reader)) + { + case Keys.SqlRowCount: + result = new SqlInfo(intConverter.Read(reader)); + break; + default: + reader.SkipToken(); + break; + } + } + + return result; + } + } + } + + public class DataResponse : DataResponse + { + public DataResponse(T data, FieldMetadata[] metadata, SqlInfo sqlInfo) + : base(sqlInfo) + { + Data = data; + MetaData = metadata; + } + + public T Data { get; } + + public IReadOnlyList MetaData { get; } + + public new class Formatter : IMsgPackConverter> + { + private IMsgPackConverter _keyConverter; + private IMsgPackConverter _dataConverter; + private IMsgPackConverter _stringConverter; + private IMsgPackConverter _intConverter; + + public void Initialize(MsgPackContext context) + { + _keyConverter = context.GetConverter(); + _dataConverter = context.GetConverter(); + _stringConverter = context.GetConverter(); + _intConverter = context.GetConverter(); + } + + public void Write(DataResponse value, IMsgPackWriter writer) + { + throw new NotSupportedException(); + } + + DataResponse IMsgPackConverter>.Read(IMsgPackReader reader) + { + var length = reader.ReadMapLength(); + if (!(1u <= length && length <= 3)) + { + throw ExceptionHelper.InvalidMapLength(length, 1u, 2u); + } + + var data = default(T); + var dataWasSet = false; + var metadata = default(FieldMetadata[]); + var sqlInfo = default(SqlInfo); + + for (var i = 0; i < length; i++) + { + var dataKey = _keyConverter.Read(reader); + switch (dataKey) + { + case Keys.Data: + data = _dataConverter.Read(reader); + dataWasSet = true; + break; + case Keys.Metadata: + metadata = ReadMetadata(reader); + break; + case Keys.SqlInfo: + sqlInfo = DataResponse.Formatter.ReadSqlInfo(reader, _keyConverter, _intConverter); + break; + default: + throw ExceptionHelper.UnexpectedKey(dataKey, Keys.Data, Keys.Metadata, Keys.SqlInfo); + } + } + + if (!dataWasSet && sqlInfo == null) + { + throw ExceptionHelper.NoDataInDataResponse(); + } + + return new DataResponse(data, metadata, sqlInfo); + } + + private FieldMetadata[] ReadMetadata(IMsgPackReader reader) + { + var length = reader.ReadArrayLength(); + if (length == null) + { + return null; + } + + var result = new FieldMetadata[length.Value]; + for (var i = 0; i < length; i++) + { + var metadataLength = reader.ReadMapLength(); + if (metadataLength == null) + { + result[i] = null; + continue; + } + + for (var j = 0; j < metadataLength; j++) + { + switch (_keyConverter.Read(reader)) + { + case Keys.FieldName: + result[i] = new FieldMetadata(_stringConverter.Read(reader)); + continue; + default: + reader.SkipToken(); + break; + } + } + } + + return result; + } + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/DeleteRequest.cs b/src/progaudi.tarantool/Model/DeleteRequest.cs new file mode 100644 index 00000000..76b2598d --- /dev/null +++ b/src/progaudi.tarantool/Model/DeleteRequest.cs @@ -0,0 +1,55 @@ +using System; + +namespace ProGaudi.Tarantool.Client.Model +{ + public class DeleteRequest : IRequest + { + public DeleteRequest(uint spaceId, uint indexId, T key) + { + SpaceId = spaceId; + IndexId = indexId; + Key = key; + } + + public uint SpaceId { get; } + + public uint IndexId { get; } + + public T Key { get; } + + public CommandCodes Code => CommandCodes.Delete; + + //internal class DeletePacketConverter : IMsgPackConverter> + //{ + // private IMsgPackConverter _keyConverter; + // private IMsgPackConverter _uintConverter; + // private IMsgPackConverter _selectKeyConverter; + + // public void Initialize(MsgPackContext context) + // { + // _keyConverter = context.GetConverter(); + // _uintConverter = context.GetConverter(); + // _selectKeyConverter = context.GetConverter(); + // } + + // public void Write(DeleteRequest value, IMsgPackWriter writer) + // { + // writer.WriteMapHeader(3); + + // _keyConverter.Write(Key.SpaceId, writer); + // _uintConverter.Write(value.SpaceId, writer); + + // _keyConverter.Write(Key.IndexId, writer); + // _uintConverter.Write(value.IndexId, writer); + + // _keyConverter.Write(Key.Key, writer); + // _selectKeyConverter.Write(value.Key, writer); + // } + + // public DeleteRequest Read(IMsgPackReader reader) + // { + // throw new NotImplementedException(); + // } + //} + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/EmptyResponse.cs b/src/progaudi.tarantool/Model/EmptyResponse.cs new file mode 100644 index 00000000..544e9f8f --- /dev/null +++ b/src/progaudi.tarantool/Model/EmptyResponse.cs @@ -0,0 +1,7 @@ +namespace ProGaudi.Tarantool.Client.Model +{ + public class EmptyResponse + { + + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/EnumAsStringAttribute.cs b/src/progaudi.tarantool/Model/EnumAsStringAttribute.cs new file mode 100644 index 00000000..c8cda14d --- /dev/null +++ b/src/progaudi.tarantool/Model/EnumAsStringAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace ProGaudi.Tarantool.Client.Model +{ + [AttributeUsage(AttributeTargets.Enum)] + public sealed class EnumAsStringAttribute : Attribute + { + public bool IgnoreCase { get; } + + public EnumAsStringAttribute(bool ignoreCase = false) + { + IgnoreCase = ignoreCase; + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/EnumAsStringFormatter.cs b/src/progaudi.tarantool/Model/EnumAsStringFormatter.cs new file mode 100644 index 00000000..babc73a1 --- /dev/null +++ b/src/progaudi.tarantool/Model/EnumAsStringFormatter.cs @@ -0,0 +1,67 @@ +using System; +using System.Reflection; +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Utils; + +namespace ProGaudi.Tarantool.Client.Model +{ + public sealed class EnumAsStringFormatter : IMsgPackConverter + where T : struct + { + private IMsgPackConverter _stringConverter; + + public void Initialize(MsgPackContext context) + { + _stringConverter = context.GetConverter(); + } + + static EnumAsStringFormatter() + { + var enumTypeInfo = typeof(T).GetTypeInfo(); + if (!enumTypeInfo.IsEnum) + { + throw ExceptionHelper.EnumExpected(enumTypeInfo); + } + } + + public void Write(T value, IMsgPackWriter writer) + { + _stringConverter.Write(value.ToString(), writer); + } + + public T Read(IMsgPackReader reader) + { + var stringValue = _stringConverter.Read(reader); + + return Parse(typeof (T), stringValue, true); + } + + public static T Parse(Type type, string stringValue, bool ignoreCase) + { + T output; + string enumStringValue = null; + + if (!type.GetTypeInfo().IsEnum) + { + throw ExceptionHelper.EnumExpected(type); + } + + if (!Enum.TryParse(stringValue, ignoreCase, out output)) + { + + //Look for our string value associated with fields in this enum + foreach (var fi in type.GetRuntimeFields()) + { + //Check for equality then select actual enum value. + if (string.Compare(enumStringValue, stringValue, ignoreCase) == 0) + { + output = (T)Enum.Parse(type, fi.Name); + break; + } + } + } + + return output; + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/EnumConverter.cs b/src/progaudi.tarantool/Model/EnumConverter.cs similarity index 96% rename from src/progaudi.tarantool/Converters/EnumConverter.cs rename to src/progaudi.tarantool/Model/EnumConverter.cs index b03d4219..2221db1a 100644 --- a/src/progaudi.tarantool/Converters/EnumConverter.cs +++ b/src/progaudi.tarantool/Model/EnumConverter.cs @@ -2,12 +2,10 @@ using System.Collections.Generic; using System.Globalization; using System.Reflection; - using ProGaudi.MsgPack.Light; - using ProGaudi.Tarantool.Client.Utils; -namespace ProGaudi.Tarantool.Client.Converters +namespace ProGaudi.Tarantool.Client.Model { internal class EnumConverter : IMsgPackConverter where T : struct, IConvertible @@ -76,8 +74,7 @@ public void Write(T value, IMsgPackWriter writer) { var enumUnderlyingType = Enum.GetUnderlyingType(typeof(T)); - Action writeMethod; - if (_writeMethodsCache.TryGetValue(enumUnderlyingType, out writeMethod)) + if (_writeMethodsCache.TryGetValue(enumUnderlyingType, out var writeMethod)) { writeMethod(value, writer); } @@ -90,8 +87,7 @@ public void Write(T value, IMsgPackWriter writer) public T Read(IMsgPackReader reader) { var enumUnderlyingType = Enum.GetUnderlyingType(typeof(T)); - Func readMethod; - if (_readMethodsCache.TryGetValue(enumUnderlyingType, out readMethod)) + if (_readMethodsCache.TryGetValue(enumUnderlyingType, out var readMethod)) { return readMethod(reader); } diff --git a/src/progaudi.tarantool/Model/Enums/FieldType.cs b/src/progaudi.tarantool/Model/Enums/FieldType.cs deleted file mode 100644 index 8aceabde..00000000 --- a/src/progaudi.tarantool/Model/Enums/FieldType.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Model.Enums -{ - public enum FieldType - { - Str, - Num, - [StringValue("*")] - Any - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Enums/IndexPartType.cs b/src/progaudi.tarantool/Model/Enums/IndexPartType.cs deleted file mode 100644 index 08fcf896..00000000 --- a/src/progaudi.tarantool/Model/Enums/IndexPartType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ProGaudi.Tarantool.Client.Model.Enums -{ - public enum IndexPartType - { - Str, - Num - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Enums/Key.cs b/src/progaudi.tarantool/Model/Enums/Key.cs deleted file mode 100644 index db777989..00000000 --- a/src/progaudi.tarantool/Model/Enums/Key.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace ProGaudi.Tarantool.Client.Model.Enums -{ - public enum Key : uint - { - // Request keys - Code = 0x00, - Sync = 0x01, - SchemaId = 0x05, - SpaceId = 0x10, - IndexId = 0x11, - Limit = 0x12, - Offset = 0x13, - Iterator = 0x14, - Key = 0x20, - Tuple = 0x21, - FunctionName = 0x22, - Username = 0x23, - Expression = 0x27, - Ops = 0x28, - FieldName = 0x29, - - // Response keys - Data = 0x30, - Error = 0x31, - Metadata = 0x32, - - // Sql keys - SqlQueryText = 0x40, - SqlParameters = 0x41, - SqlOptions = 0x42, - SqlInfo = 0x43, - SqlRowCount = 0x44, - - // Replication keys - ServerId = 0x02, - Lsn = 0x03, - Timestamp = 0x04, - ServerUuid = 0x24, - ClusterUuid = 0x25, - Vclock = 0x26, - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Enums/StorageEngine.cs b/src/progaudi.tarantool/Model/Enums/StorageEngine.cs deleted file mode 100644 index 8b4c922d..00000000 --- a/src/progaudi.tarantool/Model/Enums/StorageEngine.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ProGaudi.Tarantool.Client.Model.Enums -{ - public enum StorageEngine - { - Memtx, - Sophia - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Responses/ErrorResponse.cs b/src/progaudi.tarantool/Model/ErrorResponse.cs similarity index 79% rename from src/progaudi.tarantool/Model/Responses/ErrorResponse.cs rename to src/progaudi.tarantool/Model/ErrorResponse.cs index c1df58af..22cbbf75 100644 --- a/src/progaudi.tarantool/Model/Responses/ErrorResponse.cs +++ b/src/progaudi.tarantool/Model/ErrorResponse.cs @@ -1,4 +1,4 @@ -namespace ProGaudi.Tarantool.Client.Model.Responses +namespace ProGaudi.Tarantool.Client.Model { public class ErrorResponse { diff --git a/src/progaudi.tarantool/Model/Requests/EvalRequest.cs b/src/progaudi.tarantool/Model/EvalRequest.cs similarity index 64% rename from src/progaudi.tarantool/Model/Requests/EvalRequest.cs rename to src/progaudi.tarantool/Model/EvalRequest.cs index 397bc42e..da52e893 100644 --- a/src/progaudi.tarantool/Model/Requests/EvalRequest.cs +++ b/src/progaudi.tarantool/Model/EvalRequest.cs @@ -1,6 +1,4 @@ -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model.Requests +namespace ProGaudi.Tarantool.Client.Model { public class EvalRequest : IRequest { @@ -14,6 +12,6 @@ public EvalRequest(string expression, T tuple) public T Tuple { get; } - public CommandCode Code => CommandCode.Eval; + public CommandCodes Code => CommandCodes.Eval; } } \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/ExecuteSqlRequest.cs b/src/progaudi.tarantool/Model/ExecuteSqlRequest.cs new file mode 100644 index 00000000..bcb1251e --- /dev/null +++ b/src/progaudi.tarantool/Model/ExecuteSqlRequest.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace ProGaudi.Tarantool.Client.Model +{ + //public class ExecuteSqlRequest : IRequest + //{ + // private static readonly SqlParameter[] Empty = new SqlParameter[0]; + + // public ExecuteSqlRequest(string query, IReadOnlyList parameters) + // { + // Query = query; + // Parameters = parameters ?? Empty; + // } + + // public string Query { get; } + + // public IReadOnlyList Parameters { get; } + + // public CommandCode Code => CommandCode.Execute; + //} +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Responses/FieldMetadata.cs b/src/progaudi.tarantool/Model/FieldMetadata.cs similarity index 96% rename from src/progaudi.tarantool/Model/Responses/FieldMetadata.cs rename to src/progaudi.tarantool/Model/FieldMetadata.cs index 519dbdd4..537e9eec 100644 --- a/src/progaudi.tarantool/Model/Responses/FieldMetadata.cs +++ b/src/progaudi.tarantool/Model/FieldMetadata.cs @@ -1,7 +1,7 @@ using System; using JetBrains.Annotations; -namespace ProGaudi.Tarantool.Client.Model.Responses +namespace ProGaudi.Tarantool.Client.Model { public class FieldMetadata : IEquatable { diff --git a/src/progaudi.tarantool/Model/FieldType.cs b/src/progaudi.tarantool/Model/FieldType.cs new file mode 100644 index 00000000..abe24253 --- /dev/null +++ b/src/progaudi.tarantool/Model/FieldType.cs @@ -0,0 +1,14 @@ +namespace ProGaudi.Tarantool.Client.Model +{ + [EnumAsString(true)] + public enum FieldType + { + Str, + Num, + Unsigned, + String, + Scalar, + Map, + Array + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Responses/GreetingsResponse.cs b/src/progaudi.tarantool/Model/GreetingsResponse.cs similarity index 91% rename from src/progaudi.tarantool/Model/Responses/GreetingsResponse.cs rename to src/progaudi.tarantool/Model/GreetingsResponse.cs index 7ce4b77d..129ca70a 100644 --- a/src/progaudi.tarantool/Model/Responses/GreetingsResponse.cs +++ b/src/progaudi.tarantool/Model/GreetingsResponse.cs @@ -1,7 +1,7 @@ using System; using System.Text; -namespace ProGaudi.Tarantool.Client.Model.Responses +namespace ProGaudi.Tarantool.Client.Model { public class GreetingsResponse { diff --git a/src/progaudi.tarantool/Model/HeaderBase.cs b/src/progaudi.tarantool/Model/HeaderBase.cs new file mode 100644 index 00000000..d74234a6 --- /dev/null +++ b/src/progaudi.tarantool/Model/HeaderBase.cs @@ -0,0 +1,15 @@ +namespace ProGaudi.Tarantool.Client.Model +{ + public abstract class HeaderBase + { + protected HeaderBase(CommandCodes code, RequestId requestId) + { + Code = code; + RequestId = requestId; + } + + public CommandCodes Code { get; } + + public RequestId RequestId { get; set; } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Headers/HeaderBase.cs b/src/progaudi.tarantool/Model/Headers/HeaderBase.cs deleted file mode 100644 index fe10d429..00000000 --- a/src/progaudi.tarantool/Model/Headers/HeaderBase.cs +++ /dev/null @@ -1,17 +0,0 @@ -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model.Headers -{ - public abstract class HeaderBase - { - protected HeaderBase(CommandCode code, RequestId requestId) - { - Code = code; - RequestId = requestId; - } - - public CommandCode Code { get; } - - public RequestId RequestId { get; set; } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Headers/RequestHeader.cs b/src/progaudi.tarantool/Model/Headers/RequestHeader.cs deleted file mode 100644 index d5619554..00000000 --- a/src/progaudi.tarantool/Model/Headers/RequestHeader.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model.Headers -{ - public class RequestHeader : HeaderBase - { - public RequestHeader(CommandCode code, RequestId requestId) - : base(code, requestId) - { - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Headers/ResponseHeader.cs b/src/progaudi.tarantool/Model/Headers/ResponseHeader.cs deleted file mode 100644 index b68b7b98..00000000 --- a/src/progaudi.tarantool/Model/Headers/ResponseHeader.cs +++ /dev/null @@ -1,14 +0,0 @@ -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model.Headers -{ - public class ResponseHeader : HeaderBase - { - public ResponseHeader(CommandCode code, RequestId requestId, ulong? schemaId) : base(code, requestId) - { - SchemaId = schemaId; - } - - public ulong? SchemaId { get; } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/IRequest.cs b/src/progaudi.tarantool/Model/IRequest.cs new file mode 100644 index 00000000..2bbb83ff --- /dev/null +++ b/src/progaudi.tarantool/Model/IRequest.cs @@ -0,0 +1,7 @@ +namespace ProGaudi.Tarantool.Client.Model +{ + public interface IRequest + { + CommandCodes Code { get; } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/IndexCreationOptions.cs b/src/progaudi.tarantool/Model/IndexCreationOptions.cs index f0393126..5e934448 100644 --- a/src/progaudi.tarantool/Model/IndexCreationOptions.cs +++ b/src/progaudi.tarantool/Model/IndexCreationOptions.cs @@ -1,5 +1,8 @@ -namespace ProGaudi.Tarantool.Client.Model +using ProGaudi.MsgPack.Light; + +namespace ProGaudi.Tarantool.Client.Model { + [MsgPackMap] public class IndexCreationOptions { public IndexCreationOptions(bool unique) @@ -7,6 +10,11 @@ public IndexCreationOptions(bool unique) Unique = unique; } - public bool Unique { get; } + public IndexCreationOptions() + { + } + + [MsgPackMapElement("unique")] + public bool Unique { get; set; } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/IndexPart.cs b/src/progaudi.tarantool/Model/IndexPart.cs index c527ab88..c37ecafd 100644 --- a/src/progaudi.tarantool/Model/IndexPart.cs +++ b/src/progaudi.tarantool/Model/IndexPart.cs @@ -1,10 +1,12 @@ -using ProGaudi.Tarantool.Client.Model.Enums; +using System.Runtime.Serialization; +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Utils; namespace ProGaudi.Tarantool.Client.Model { public class IndexPart { - public IndexPart(uint fieldNo, IndexPartType type) + public IndexPart(uint fieldNo, FieldType type) { FieldNo = fieldNo; Type = type; @@ -12,6 +14,129 @@ public IndexPart(uint fieldNo, IndexPartType type) public uint FieldNo { get; } - public IndexPartType Type { get; } + public FieldType Type { get; } + + public override string ToString() + { + return $"[{FieldNo}, {Type}]"; + } + + public sealed class Formatter : IMsgPackConverter + { + private IMsgPackConverter _uintConverter; + private IMsgPackConverter _indexPartTypeConverter; + private IMsgPackConverter _stringConverter; + + public void Initialize(MsgPackContext context) + { + _uintConverter = context.GetConverter(); + _indexPartTypeConverter = context.GetConverter(); + _stringConverter = context.GetConverter(); + } + + public void Write(IndexPart value, IMsgPackWriter writer) => throw new System.NotImplementedException(); + + public IndexPart Read(IMsgPackReader reader) + { + var type = reader.ReadDataType(); + switch (type) + { + case DataTypes.Map16: + return ReadFromMap(reader, ReadUInt16(reader)); + + case DataTypes.Map32: + return ReadFromMap(reader, ReadUInt32(reader)); + + case DataTypes.Array16: + return ReadFromArray(reader, ReadUInt16(reader)); + + case DataTypes.Array32: + return ReadFromArray(reader, ReadUInt32(reader)); + } + + var length = TryGetLengthFromFixMap(type); + if (length.HasValue) + { + return ReadFromMap(reader, length.Value); + } + + length = TryGetLengthFromFixArray(type); + if (length != null) + { + return ReadFromArray(reader, length.Value); + } + + throw ExceptionUtils.BadTypeException(type, DataTypes.Map16, DataTypes.Map32, DataTypes.FixMap, DataTypes.Array16, DataTypes.Array32, DataTypes.FixArray); + } + + private IndexPart ReadFromArray(IMsgPackReader reader, uint length) + { + if (length != 2u) + { + throw ExceptionHelper.InvalidArrayLength(2u, length); + } + + var fieldNo = _uintConverter.Read(reader); + var indexPartType = _indexPartTypeConverter.Read(reader); + + return new IndexPart(fieldNo, indexPartType); + } + + private IndexPart ReadFromMap(IMsgPackReader reader, uint length) + { + uint? fieldNo = null; + FieldType? indexPartType = null; + + for (var i = 0; i < length; i++) + { + switch (_stringConverter.Read(reader)) + { + case "field": + fieldNo = _uintConverter.Read(reader); + break; + case "type": + indexPartType = _indexPartTypeConverter.Read(reader); + break; + default: + reader.SkipToken(); + break; + } + } + + if (fieldNo.HasValue && indexPartType.HasValue) + { + return new IndexPart(fieldNo.Value, indexPartType.Value); + } + + throw new SerializationException("Can't read fieldNo or indexPart from map of index metadata"); + } + + private static uint? TryGetLengthFromFixArray(DataTypes type) + { + var length = type - DataTypes.FixArray; + return type.GetHighBits(4) == DataTypes.FixArray.GetHighBits(4) ? length : (uint?)null; + } + + private static uint? TryGetLengthFromFixMap(DataTypes type) + { + var length = type - DataTypes.FixMap; + return type.GetHighBits(4) == DataTypes.FixMap.GetHighBits(4) ? length : (uint?)null; + } + + internal static ushort ReadUInt16(IMsgPackReader reader) + { + return (ushort)((reader.ReadByte() << 8) + reader.ReadByte()); + } + + internal static uint ReadUInt32(IMsgPackReader reader) + { + var temp = (uint)(reader.ReadByte() << 24); + temp += (uint)reader.ReadByte() << 16; + temp += (uint)reader.ReadByte() << 8; + temp += reader.ReadByte(); + + return temp; + } + } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Enums/IndexType.cs b/src/progaudi.tarantool/Model/IndexType.cs similarity index 58% rename from src/progaudi.tarantool/Model/Enums/IndexType.cs rename to src/progaudi.tarantool/Model/IndexType.cs index 7736d45a..e98305b6 100644 --- a/src/progaudi.tarantool/Model/Enums/IndexType.cs +++ b/src/progaudi.tarantool/Model/IndexType.cs @@ -1,5 +1,6 @@ -namespace ProGaudi.Tarantool.Client.Model.Enums +namespace ProGaudi.Tarantool.Client.Model { + [EnumAsString(true)] public enum IndexType { Tree, diff --git a/src/progaudi.tarantool/Model/InsertReplaceRequest.cs b/src/progaudi.tarantool/Model/InsertReplaceRequest.cs new file mode 100644 index 00000000..85057e76 --- /dev/null +++ b/src/progaudi.tarantool/Model/InsertReplaceRequest.cs @@ -0,0 +1,49 @@ +using System; +using ProGaudi.MsgPack.Light; + +namespace ProGaudi.Tarantool.Client.Model +{ + public abstract class InsertReplaceRequest : IRequest + { + protected InsertReplaceRequest(CommandCodes code, uint spaceId, T tuple) + { + Code = code; + SpaceId = spaceId; + Tuple = tuple; + } + + public uint SpaceId { get; } + + public T Tuple { get; } + + public CommandCodes Code { get; } + + public sealed class Formatter : IMsgPackConverter> + { + private IMsgPackConverter _uintConverter; + private IMsgPackConverter _tupleConverter; + + public void Initialize(MsgPackContext context) + { + _uintConverter = context.GetConverter(); + _tupleConverter = context.GetConverter(); + } + + public void Write(InsertReplaceRequest value, IMsgPackWriter writer) + { + writer.WriteMapHeader(2); + + _uintConverter.Write(Keys.SpaceId, writer); + _uintConverter.Write(value.SpaceId, writer); + + _uintConverter.Write(Keys.Tuple, writer); + _tupleConverter.Write(value.Tuple, writer); + } + + public InsertReplaceRequest Read(IMsgPackReader reader) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/src/progaudi.tarantool/Model/InsertRequest.cs b/src/progaudi.tarantool/Model/InsertRequest.cs new file mode 100644 index 00000000..077cfc92 --- /dev/null +++ b/src/progaudi.tarantool/Model/InsertRequest.cs @@ -0,0 +1,10 @@ +namespace ProGaudi.Tarantool.Client.Model +{ + public class InsertRequest : InsertReplaceRequest + { + public InsertRequest(uint spaceId, T tuple) + : base(CommandCodes.Insert, spaceId, tuple) + { + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Enums/Iterator.cs b/src/progaudi.tarantool/Model/Iterator.cs similarity index 84% rename from src/progaudi.tarantool/Model/Enums/Iterator.cs rename to src/progaudi.tarantool/Model/Iterator.cs index ef7dc032..07e097e5 100644 --- a/src/progaudi.tarantool/Model/Enums/Iterator.cs +++ b/src/progaudi.tarantool/Model/Iterator.cs @@ -1,4 +1,4 @@ -namespace ProGaudi.Tarantool.Client.Model.Enums +namespace ProGaudi.Tarantool.Client.Model { public enum Iterator : uint { diff --git a/src/progaudi.tarantool/Model/Keys.cs b/src/progaudi.tarantool/Model/Keys.cs new file mode 100644 index 00000000..c66042da --- /dev/null +++ b/src/progaudi.tarantool/Model/Keys.cs @@ -0,0 +1,42 @@ +namespace ProGaudi.Tarantool.Client.Model +{ + public static class Keys + { + // Request keys + public const uint Code = 0x00; + public const uint Sync = 0x01; + public const uint SchemaId = 0x05; + public const uint SpaceId = 0x10; + public const uint IndexId = 0x11; + public const uint Limit = 0x12; + public const uint Offset = 0x13; + public const uint Iterator = 0x14; + public const uint Key = 0x20; + public const uint Tuple = 0x21; + public const uint FunctionName = 0x22; + public const uint Username = 0x23; + public const uint Expression = 0x27; + public const uint Ops = 0x28; + public const uint FieldName = 0x29; + + // Response keys + public const uint Data = 0x30; + public const uint Error = 0x31; + public const uint Metadata = 0x32; + + // Sql keys + public const uint SqlQueryText = 0x40; + public const uint SqlParameters = 0x41; + public const uint SqlOptions = 0x42; + public const uint SqlInfo = 0x43; + public const uint SqlRowCount = 0x44; + + // Replication keys + public const uint ServerId = 0x02; + public const uint Lsn = 0x03; + public const uint Timestamp = 0x04; + public const uint ServerUuid = 0x24; + public const uint ClusterUuid = 0x25; + public const uint Vclock = 0x26; + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/PingRequest.cs b/src/progaudi.tarantool/Model/PingRequest.cs new file mode 100644 index 00000000..5976ee3e --- /dev/null +++ b/src/progaudi.tarantool/Model/PingRequest.cs @@ -0,0 +1,7 @@ +namespace ProGaudi.Tarantool.Client.Model +{ + public class PingRequest : IRequest + { + public CommandCodes Code => CommandCodes.Ping; + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/ReplaceRequest.cs b/src/progaudi.tarantool/Model/ReplaceRequest.cs new file mode 100644 index 00000000..49ebea51 --- /dev/null +++ b/src/progaudi.tarantool/Model/ReplaceRequest.cs @@ -0,0 +1,10 @@ +namespace ProGaudi.Tarantool.Client.Model +{ + public class ReplaceRequest : InsertReplaceRequest + { + public ReplaceRequest(uint spaceId, T tuple) + : base(CommandCodes.Replace, spaceId, tuple) + { + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/RequestHeader.cs b/src/progaudi.tarantool/Model/RequestHeader.cs new file mode 100644 index 00000000..ce1cc0e0 --- /dev/null +++ b/src/progaudi.tarantool/Model/RequestHeader.cs @@ -0,0 +1,49 @@ +using System; +using ProGaudi.MsgPack.Light; + +namespace ProGaudi.Tarantool.Client.Model +{ + public class RequestHeader : HeaderBase + { + public RequestHeader(CommandCodes code, RequestId requestId) + : base(code, requestId) + { + } + + public sealed class Formatter : IMsgPackConverter + { + private IMsgPackConverter _uintConverter; + private IMsgPackConverter _ulongConverter; + private IMsgPackConverter _nullConverter; + + public void Initialize(MsgPackContext context) + { + _uintConverter = context.GetConverter(); + _ulongConverter = context.GetConverter(); + _nullConverter = context.NullConverter; + } + + public void Write(RequestHeader value, IMsgPackWriter writer) + { + if (value == null) + { + _nullConverter.Write(null, writer); + return; + } + + writer.WriteMapHeader(2u); + + _uintConverter.Write(Keys.Code, writer); + _uintConverter.Write((uint)value.Code, writer); + + _uintConverter.Write(Keys.Sync, writer); + _ulongConverter.Write(value.RequestId.Value, writer); + } + + public RequestHeader Read(IMsgPackReader reader) + { + throw new NotImplementedException(); + } + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/RequestId.cs b/src/progaudi.tarantool/Model/RequestId.cs index 5cac168b..03d098d3 100644 --- a/src/progaudi.tarantool/Model/RequestId.cs +++ b/src/progaudi.tarantool/Model/RequestId.cs @@ -1,6 +1,8 @@ -namespace ProGaudi.Tarantool.Client.Model +using System; + +namespace ProGaudi.Tarantool.Client.Model { - public struct RequestId + public readonly struct RequestId : IEquatable { public RequestId(ulong value) { @@ -23,5 +25,21 @@ public override string ToString() { return Value.ToString(); } + + public bool Equals(RequestId other) + { + return Value == other.Value; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + return obj is RequestId id && Equals(id); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Requests/AuthenticationRequest.cs b/src/progaudi.tarantool/Model/Requests/AuthenticationRequest.cs deleted file mode 100644 index c7643cef..00000000 --- a/src/progaudi.tarantool/Model/Requests/AuthenticationRequest.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Linq; - -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Responses; -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Model.Requests -{ - public class AuthenticationRequest : IRequest - { - private AuthenticationRequest(string username, byte[] scramble) - { - Username = username; - Scramble = scramble; - } - - public string Username { get; } - - public byte[] Scramble { get; } - - public CommandCode Code => CommandCode.Auth; - - public static AuthenticationRequest Create(GreetingsResponse greetings, UriBuilder uri) - { - var scrable = GetScrable(greetings, uri.Password); - var authenticationPacket = new AuthenticationRequest(uri.UserName, scrable); - return authenticationPacket; - } - - private static byte[] GetScrable(GreetingsResponse greetings, string password) - { - var decodedSalt = greetings.Salt; - var first20SaltBytes = new byte[20]; - Array.Copy(decodedSalt, first20SaltBytes, 20); - - var step1 = Sha1Utils.Hash(password); - var step2 = Sha1Utils.Hash(step1); - var step3 = Sha1Utils.Hash(first20SaltBytes, step2); - var scrambleBytes = Sha1Utils.Xor(step1, step3); - - return scrambleBytes; - } - - public override string ToString() - { - return $"Username: {Username}, Scramble: {ToReadableString(Scramble)}"; - } - - private static string ToReadableString(byte[] bytes) - { - return string.Concat(bytes.Select(b => b.ToString("X2 "))); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Requests/CallRequest.cs b/src/progaudi.tarantool/Model/Requests/CallRequest.cs deleted file mode 100644 index ea2b74a9..00000000 --- a/src/progaudi.tarantool/Model/Requests/CallRequest.cs +++ /dev/null @@ -1,22 +0,0 @@ -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model.Requests -{ - public class CallRequest : IRequest - { - private readonly bool _use17; - - public CallRequest(string functionName, T tuple, bool use17 = true) - { - _use17 = use17; - FunctionName = functionName; - Tuple = tuple; - } - - public string FunctionName { get; } - - public T Tuple { get; } - - public CommandCode Code => _use17 ? CommandCode.Call : CommandCode.OldCall; - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Requests/DeleteRequest.cs b/src/progaudi.tarantool/Model/Requests/DeleteRequest.cs deleted file mode 100644 index 05d3c1a1..00000000 --- a/src/progaudi.tarantool/Model/Requests/DeleteRequest.cs +++ /dev/null @@ -1,22 +0,0 @@ -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model.Requests -{ - public class DeleteRequest : IRequest - { - public DeleteRequest(uint spaceId, uint indexId, T key) - { - SpaceId = spaceId; - IndexId = indexId; - Key = key; - } - - public uint SpaceId { get; } - - public uint IndexId { get; } - - public T Key { get; } - - public CommandCode Code => CommandCode.Delete; - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Requests/ExecuteSqlRequest.cs b/src/progaudi.tarantool/Model/Requests/ExecuteSqlRequest.cs deleted file mode 100644 index 4b686f02..00000000 --- a/src/progaudi.tarantool/Model/Requests/ExecuteSqlRequest.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model.Requests -{ - public class ExecuteSqlRequest : IRequest - { - private static readonly SqlParameter[] Empty = new SqlParameter[0]; - - public ExecuteSqlRequest(string query, IReadOnlyList parameters) - { - Query = query; - Parameters = parameters ?? Empty; - } - - public string Query { get; } - - public IReadOnlyList Parameters { get; } - - public CommandCode Code => CommandCode.Execute; - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Requests/IRequest.cs b/src/progaudi.tarantool/Model/Requests/IRequest.cs deleted file mode 100644 index a318ff64..00000000 --- a/src/progaudi.tarantool/Model/Requests/IRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model.Requests -{ - public interface IRequest - { - CommandCode Code { get; } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Requests/InsertReplaceRequest.cs b/src/progaudi.tarantool/Model/Requests/InsertReplaceRequest.cs deleted file mode 100644 index 625f76ab..00000000 --- a/src/progaudi.tarantool/Model/Requests/InsertReplaceRequest.cs +++ /dev/null @@ -1,20 +0,0 @@ -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model.Requests -{ - public abstract class InsertReplaceRequest : IRequest - { - protected InsertReplaceRequest(CommandCode code, uint spaceId, T tuple) - { - Code = code; - SpaceId = spaceId; - Tuple = tuple; - } - - public uint SpaceId { get; } - - public T Tuple { get; } - - public CommandCode Code { get; } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Requests/InsertRequest.cs b/src/progaudi.tarantool/Model/Requests/InsertRequest.cs deleted file mode 100644 index e36b4d16..00000000 --- a/src/progaudi.tarantool/Model/Requests/InsertRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model.Requests -{ - public class InsertRequest : InsertReplaceRequest - { - public InsertRequest(uint spaceId, T tuple) - : base(CommandCode.Insert, spaceId, tuple) - { - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Requests/PingRequest.cs b/src/progaudi.tarantool/Model/Requests/PingRequest.cs deleted file mode 100644 index 4c4d0c3a..00000000 --- a/src/progaudi.tarantool/Model/Requests/PingRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model.Requests -{ - public class PingRequest : IRequest - { - public CommandCode Code => CommandCode.Ping; - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Requests/ReplaceRequest.cs b/src/progaudi.tarantool/Model/Requests/ReplaceRequest.cs deleted file mode 100644 index cbe7009a..00000000 --- a/src/progaudi.tarantool/Model/Requests/ReplaceRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model.Requests -{ - public class ReplaceRequest : InsertReplaceRequest - { - public ReplaceRequest(uint spaceId, T tuple) - : base(CommandCode.Replace, spaceId, tuple) - { - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Requests/SelectRequest.cs b/src/progaudi.tarantool/Model/Requests/SelectRequest.cs deleted file mode 100644 index 39e98ab7..00000000 --- a/src/progaudi.tarantool/Model/Requests/SelectRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model.Requests -{ - public class SelectRequest : IRequest - { - public SelectRequest(uint spaceId, uint indexId, uint limit, uint offset, Iterator iterator, T selectKey) - { - SpaceId = spaceId; - IndexId = indexId; - Limit = limit; - Offset = offset; - Iterator = iterator; - SelectKey = selectKey; - } - - public uint SpaceId { get; } - - public uint IndexId { get; } - - public uint Limit { get; } - - public uint Offset { get; } - - public Iterator Iterator { get; } - - public T SelectKey { get; } - - public CommandCode Code => CommandCode.Select; - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/ResponseHeader.cs b/src/progaudi.tarantool/Model/ResponseHeader.cs new file mode 100644 index 00000000..3a34462f --- /dev/null +++ b/src/progaudi.tarantool/Model/ResponseHeader.cs @@ -0,0 +1,82 @@ +using System; +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Utils; + +namespace ProGaudi.Tarantool.Client.Model +{ + public class ResponseHeader : HeaderBase + { + public ResponseHeader(CommandCodes code, RequestId requestId, ulong? schemaId) : base(code, requestId) + { + SchemaId = schemaId; + } + + public ulong? SchemaId { get; } + + public sealed class Formatter : IMsgPackConverter + { + private IMsgPackConverter _keyConverter; + private IMsgPackConverter _ulongConverter; + private IMsgPackConverter _codeConverter; + + public void Initialize(MsgPackContext context) + { + _keyConverter = context.GetConverter(); + _ulongConverter = context.GetConverter(); + _codeConverter = context.GetConverter(); + } + + public void Write(ResponseHeader value, IMsgPackWriter writer) + { + throw new NotImplementedException(); + } + + public ResponseHeader Read(IMsgPackReader reader) + { + var length = reader.ReadMapLength(); + + if (!length.HasValue) + { + throw ExceptionHelper.InvalidMapLength(length, 2u, 3u); + } + + CommandCodes? code = null; + RequestId? sync = null; + ulong? schemaId = null; + + for (int i = 0; i < length.Value; i++) + { + var key = _keyConverter.Read(reader); + + switch (key) + { + case Keys.Code: + code = _codeConverter.Read(reader); + break; + case Keys.Sync: + sync = new RequestId(_ulongConverter.Read(reader)); + break; + case Keys.SchemaId: + schemaId = _ulongConverter.Read(reader); + break; + default: + reader.SkipToken(); + break; + } + } + + if (!code.HasValue) + { + throw ExceptionHelper.PropertyUnspecified("Code"); + } + + if (!sync.HasValue) + { + throw ExceptionHelper.PropertyUnspecified("Sync"); + } + + return new ResponseHeader(code.Value, sync.Value, schemaId); + } + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Responses/DataResponse.cs b/src/progaudi.tarantool/Model/Responses/DataResponse.cs deleted file mode 100644 index 153f2d27..00000000 --- a/src/progaudi.tarantool/Model/Responses/DataResponse.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; - -namespace ProGaudi.Tarantool.Client.Model.Responses -{ - public class DataResponse - { - public DataResponse(SqlInfo sqlInfo) - { - SqlInfo = sqlInfo; - } - - public SqlInfo SqlInfo { get; } - } - - public class DataResponse : DataResponse - { - public DataResponse(T data, FieldMetadata[] metadata, SqlInfo sqlInfo) - : base(sqlInfo) - { - Data = data; - MetaData = metadata; - } - - public T Data { get; } - - public IReadOnlyList MetaData { get; } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Responses/EmptyResponse.cs b/src/progaudi.tarantool/Model/Responses/EmptyResponse.cs deleted file mode 100644 index fa2cd2a3..00000000 --- a/src/progaudi.tarantool/Model/Responses/EmptyResponse.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ProGaudi.Tarantool.Client.Model.Responses -{ - public class EmptyResponse - { - - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/SelectOptions.cs b/src/progaudi.tarantool/Model/SelectOptions.cs deleted file mode 100644 index d91dc6be..00000000 --- a/src/progaudi.tarantool/Model/SelectOptions.cs +++ /dev/null @@ -1,13 +0,0 @@ -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model -{ - public class SelectOptions - { - public Iterator Iterator { get; set; } = Iterator.Eq; - - public uint Limit { get; set; } = uint.MaxValue; - - public uint Offset { get; set; } = 0; - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/SelectRequest.cs b/src/progaudi.tarantool/Model/SelectRequest.cs new file mode 100644 index 00000000..ba7cd167 --- /dev/null +++ b/src/progaudi.tarantool/Model/SelectRequest.cs @@ -0,0 +1,74 @@ +using System; +using ProGaudi.MsgPack.Light; + +namespace ProGaudi.Tarantool.Client.Model +{ + public class SelectRequest : IRequest + { + public SelectRequest(uint spaceId, uint indexId, uint limit, uint offset, Iterator iterator, T selectKey) + { + SpaceId = spaceId; + IndexId = indexId; + Limit = limit; + Offset = offset; + Iterator = iterator; + SelectKey = selectKey; + } + + public uint SpaceId { get; } + + public uint IndexId { get; } + + public uint Limit { get; } + + public uint Offset { get; } + + public Iterator Iterator { get; } + + public T SelectKey { get; } + + public CommandCodes Code => CommandCodes.Select; + + public sealed class Formatter : IMsgPackConverter> + { + private IMsgPackConverter _selectKeyConverter; + private IMsgPackConverter _uintConverter; + private IMsgPackConverter _iteratorConverter; + + public void Initialize(MsgPackContext context) + { + _uintConverter = context.GetConverter(); + _iteratorConverter = context.GetConverter(); + _selectKeyConverter = context.GetConverter(); + } + + public void Write(SelectRequest value, IMsgPackWriter writer) + { + writer.WriteMapHeader(6); + + _uintConverter.Write(Keys.SpaceId, writer); + _uintConverter.Write(value.SpaceId, writer); + + _uintConverter.Write(Keys.IndexId, writer); + _uintConverter.Write(value.IndexId, writer); + + _uintConverter.Write(Keys.Limit, writer); + _uintConverter.Write(value.Limit, writer); + + _uintConverter.Write(Keys.Offset, writer); + _uintConverter.Write(value.Offset, writer); + + _uintConverter.Write(Keys.Iterator, writer); + _iteratorConverter.Write(value.Iterator, writer); + + _uintConverter.Write(Keys.Key, writer); + _selectKeyConverter.Write(value.SelectKey, writer); + } + + public SelectRequest Read(IMsgPackReader reader) + { + throw new NotSupportedException(); + } + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/SpaceCreationOptions.cs b/src/progaudi.tarantool/Model/SpaceCreationOptions.cs deleted file mode 100644 index 97b9a351..00000000 --- a/src/progaudi.tarantool/Model/SpaceCreationOptions.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; - -using ProGaudi.Tarantool.Client.Model.Enums; - -namespace ProGaudi.Tarantool.Client.Model -{ - public class SpaceCreationOptions - { - public bool Temporary { get; set; } = false; - - public uint? Id { get; set; } = null; - - public uint FieldCount { get; set; } = 0; - - public bool IfNotExists { get; set; } = false; - - public StorageEngine StorageEngine { get; set; } = StorageEngine.Memtx; - - public string User { get; set; } = null; - - public IDictionary Format { get; } = new Dictionary(); - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/SpaceField.cs b/src/progaudi.tarantool/Model/SpaceField.cs index ee007589..e05f567d 100644 --- a/src/progaudi.tarantool/Model/SpaceField.cs +++ b/src/progaudi.tarantool/Model/SpaceField.cs @@ -1,7 +1,8 @@ -using ProGaudi.Tarantool.Client.Model.Enums; +using ProGaudi.MsgPack.Light; namespace ProGaudi.Tarantool.Client.Model { + [MsgPackMap] public class SpaceField { public SpaceField(string name, FieldType type) @@ -10,8 +11,19 @@ public SpaceField(string name, FieldType type) Type = type; } - public string Name { get; } + public SpaceField() + { + } + + [MsgPackMapElement("name")] + public string Name { get; set; } - public FieldType Type { get; } + [MsgPackMapElement("type")] + public FieldType Type { get; set; } + + public override string ToString() + { + return $"[{Name}, {Type}]"; + } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/SpaceOptions.cs b/src/progaudi.tarantool/Model/SpaceOptions.cs new file mode 100644 index 00000000..1b0f208e --- /dev/null +++ b/src/progaudi.tarantool/Model/SpaceOptions.cs @@ -0,0 +1,20 @@ +using ProGaudi.MsgPack.Light; + +namespace ProGaudi.Tarantool.Client.Model +{ + [MsgPackMap] + public class SpaceOptions + { + public SpaceOptions() + { + } + + [MsgPackMapElement("temporary")] + public bool Temporary { get; set; } + + public override string ToString() + { + return $"{nameof(Temporary)}: {Temporary}"; + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Responses/SqlInfo.cs b/src/progaudi.tarantool/Model/SqlInfo.cs similarity index 75% rename from src/progaudi.tarantool/Model/Responses/SqlInfo.cs rename to src/progaudi.tarantool/Model/SqlInfo.cs index fdef12f2..63d79ae2 100644 --- a/src/progaudi.tarantool/Model/Responses/SqlInfo.cs +++ b/src/progaudi.tarantool/Model/SqlInfo.cs @@ -1,4 +1,4 @@ -namespace ProGaudi.Tarantool.Client.Model.Responses +namespace ProGaudi.Tarantool.Client.Model { public class SqlInfo { diff --git a/src/progaudi.tarantool/Model/SqlParameter.cs b/src/progaudi.tarantool/Model/SqlParameter.cs index cd7d5ff6..db83d294 100644 --- a/src/progaudi.tarantool/Model/SqlParameter.cs +++ b/src/progaudi.tarantool/Model/SqlParameter.cs @@ -1,37 +1,36 @@ using System; -using ProGaudi.MsgPack.Light; namespace ProGaudi.Tarantool.Client.Model { - public abstract class SqlParameter - { - internal abstract void Write(MsgPackContext context, IMsgPackWriter writer, IMsgPackConverter stringConverter); - } + //public abstract class SqlParameter + //{ + // internal abstract void Write(MsgPackContext context, IMsgPackWriter writer, IMsgPackConverter stringConverter); + //} - public class SqlParameter : SqlParameter - { - private readonly T _value; - private readonly string _name; + //public class SqlParameter : SqlParameter + //{ + // private readonly T _value; + // private readonly string _name; - public SqlParameter(T value) => _value = value; + // public SqlParameter(T value) => _value = value; - public SqlParameter(T value, string name) - : this(value) - { - if (name[0] != ':' && name[0] != '@' && name[0] != '$') throw new ArgumentException("Name should start either with ':', '$' or '@'.", nameof(name)); + // public SqlParameter(T value, string name) + // : this(value) + // { + // if (name[0] != ':' && name[0] != '@' && name[0] != '$') throw new ArgumentException("Name should start either with ':', '$' or '@'.", nameof(name)); - _name = name; - } + // _name = name; + // } - internal override void Write(MsgPackContext context, IMsgPackWriter writer, IMsgPackConverter stringConverter) - { - if (_name != null) - { - writer.WriteMapHeader(1u); - stringConverter.Write(_name, writer); - } + // internal override void Write(MsgPackContext context, IMsgPackWriter writer, IMsgPackConverter stringConverter) + // { + // if (_name != null) + // { + // writer.WriteMapHeader(1u); + // stringConverter.Write(_name, writer); + // } - context.GetConverter().Write(_value, writer); - } - } + // context.GetConverter().Write(_value, writer); + // } + //} } diff --git a/src/progaudi.tarantool/Model/StorageEngine.cs b/src/progaudi.tarantool/Model/StorageEngine.cs new file mode 100644 index 00000000..5b7eef34 --- /dev/null +++ b/src/progaudi.tarantool/Model/StorageEngine.cs @@ -0,0 +1,10 @@ +namespace ProGaudi.Tarantool.Client.Model +{ + [EnumAsString(true)] + public enum StorageEngine + { + Memtx, + Vinyl, + Sysview + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/UpdateOperations/StringOperations.cs b/src/progaudi.tarantool/Model/StringOperations.cs similarity index 87% rename from src/progaudi.tarantool/Model/UpdateOperations/StringOperations.cs rename to src/progaudi.tarantool/Model/StringOperations.cs index a0780397..e58eb67d 100644 --- a/src/progaudi.tarantool/Model/UpdateOperations/StringOperations.cs +++ b/src/progaudi.tarantool/Model/StringOperations.cs @@ -1,4 +1,4 @@ -namespace ProGaudi.Tarantool.Client.Model.UpdateOperations +namespace ProGaudi.Tarantool.Client.Model { public class StringSliceOperation : UpdateOperation { diff --git a/src/progaudi.tarantool/Model/TarantoolTuple.cs b/src/progaudi.tarantool/Model/TarantoolTuple.cs deleted file mode 100644 index f6f93c67..00000000 --- a/src/progaudi.tarantool/Model/TarantoolTuple.cs +++ /dev/null @@ -1,498 +0,0 @@ -using System.Collections.Generic; - -namespace ProGaudi.Tarantool.Client.Model -{ - public interface ITarantoolTuple - { - } - - public class TarantoolTuple : ITarantoolTuple - { - public TarantoolTuple(T1 item1) - { - - Item1 = item1; - } - - public T1 Item1 { get; } - - protected bool Equals(TarantoolTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((TarantoolTuple) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = EqualityComparer.Default.GetHashCode(Item1); - return hashCode; - } - } - - public override string ToString() - { - return $"{Item1}"; - } - } - - public class TarantoolTuple : ITarantoolTuple - { - public TarantoolTuple(T1 item1, T2 item2) - { - - Item1 = item1; - Item2 = item2; - } - - public T1 Item1 { get; } - public T2 Item2 { get; } - - protected bool Equals(TarantoolTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) && - EqualityComparer.Default.Equals(Item2, other.Item2); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((TarantoolTuple) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = EqualityComparer.Default.GetHashCode(Item1); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item2); - return hashCode; - } - } - - public override string ToString() - { - return $"{Item1}, {Item2}"; - } - } - - public class TarantoolTuple : ITarantoolTuple - { - public TarantoolTuple(T1 item1, T2 item2, T3 item3) - { - - Item1 = item1; - Item2 = item2; - Item3 = item3; - } - - public T1 Item1 { get; } - public T2 Item2 { get; } - public T3 Item3 { get; } - - protected bool Equals(TarantoolTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) && - EqualityComparer.Default.Equals(Item2, other.Item2) && - EqualityComparer.Default.Equals(Item3, other.Item3); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((TarantoolTuple) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = EqualityComparer.Default.GetHashCode(Item1); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item2); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item3); - return hashCode; - } - } - - public override string ToString() - { - return $"{Item1}, {Item2}, {Item3}"; - } - } - - public class TarantoolTuple : ITarantoolTuple - { - public TarantoolTuple(T1 item1, T2 item2, T3 item3, T4 item4) - { - - Item1 = item1; - Item2 = item2; - Item3 = item3; - Item4 = item4; - } - - public T1 Item1 { get; } - public T2 Item2 { get; } - public T3 Item3 { get; } - public T4 Item4 { get; } - - protected bool Equals(TarantoolTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) && - EqualityComparer.Default.Equals(Item2, other.Item2) && - EqualityComparer.Default.Equals(Item3, other.Item3) && - EqualityComparer.Default.Equals(Item4, other.Item4); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((TarantoolTuple) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = EqualityComparer.Default.GetHashCode(Item1); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item2); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item3); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item4); - return hashCode; - } - } - - public override string ToString() - { - return $"{Item1}, {Item2}, {Item3}, {Item4}"; - } - } - - public class TarantoolTuple : ITarantoolTuple - { - public TarantoolTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) - { - - Item1 = item1; - Item2 = item2; - Item3 = item3; - Item4 = item4; - Item5 = item5; - } - - public T1 Item1 { get; } - public T2 Item2 { get; } - public T3 Item3 { get; } - public T4 Item4 { get; } - public T5 Item5 { get; } - - protected bool Equals(TarantoolTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) && - EqualityComparer.Default.Equals(Item2, other.Item2) && - EqualityComparer.Default.Equals(Item3, other.Item3) && - EqualityComparer.Default.Equals(Item4, other.Item4) && - EqualityComparer.Default.Equals(Item5, other.Item5); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((TarantoolTuple) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = EqualityComparer.Default.GetHashCode(Item1); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item2); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item3); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item4); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item5); - return hashCode; - } - } - - public override string ToString() - { - return $"{Item1}, {Item2}, {Item3}, {Item4}, {Item5}"; - } - } - - public class TarantoolTuple : ITarantoolTuple - { - public TarantoolTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) - { - - Item1 = item1; - Item2 = item2; - Item3 = item3; - Item4 = item4; - Item5 = item5; - Item6 = item6; - } - - public T1 Item1 { get; } - public T2 Item2 { get; } - public T3 Item3 { get; } - public T4 Item4 { get; } - public T5 Item5 { get; } - public T6 Item6 { get; } - - protected bool Equals(TarantoolTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) && - EqualityComparer.Default.Equals(Item2, other.Item2) && - EqualityComparer.Default.Equals(Item3, other.Item3) && - EqualityComparer.Default.Equals(Item4, other.Item4) && - EqualityComparer.Default.Equals(Item5, other.Item5) && - EqualityComparer.Default.Equals(Item6, other.Item6); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((TarantoolTuple) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = EqualityComparer.Default.GetHashCode(Item1); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item2); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item3); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item4); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item5); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item6); - return hashCode; - } - } - - public override string ToString() - { - return $"{Item1}, {Item2}, {Item3}, {Item4}, {Item5}, {Item6}"; - } - } - - public class TarantoolTuple : ITarantoolTuple - { - public TarantoolTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) - { - - Item1 = item1; - Item2 = item2; - Item3 = item3; - Item4 = item4; - Item5 = item5; - Item6 = item6; - Item7 = item7; - } - - public T1 Item1 { get; } - public T2 Item2 { get; } - public T3 Item3 { get; } - public T4 Item4 { get; } - public T5 Item5 { get; } - public T6 Item6 { get; } - public T7 Item7 { get; } - - protected bool Equals(TarantoolTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) && - EqualityComparer.Default.Equals(Item2, other.Item2) && - EqualityComparer.Default.Equals(Item3, other.Item3) && - EqualityComparer.Default.Equals(Item4, other.Item4) && - EqualityComparer.Default.Equals(Item5, other.Item5) && - EqualityComparer.Default.Equals(Item6, other.Item6) && - EqualityComparer.Default.Equals(Item7, other.Item7); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((TarantoolTuple) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = EqualityComparer.Default.GetHashCode(Item1); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item2); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item3); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item4); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item5); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item6); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item7); - return hashCode; - } - } - - public override string ToString() - { - return $"{Item1}, {Item2}, {Item3}, {Item4}, {Item5}, {Item6}, {Item7}"; - } - } - - public class TarantoolTuple : ITarantoolTuple - { - public TarantoolTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) - { - - Item1 = item1; - Item2 = item2; - Item3 = item3; - Item4 = item4; - Item5 = item5; - Item6 = item6; - Item7 = item7; - Item8 = item8; - } - - public T1 Item1 { get; } - public T2 Item2 { get; } - public T3 Item3 { get; } - public T4 Item4 { get; } - public T5 Item5 { get; } - public T6 Item6 { get; } - public T7 Item7 { get; } - public T8 Item8 { get; } - - protected bool Equals(TarantoolTuple other) - { - return EqualityComparer.Default.Equals(Item1, other.Item1) && - EqualityComparer.Default.Equals(Item2, other.Item2) && - EqualityComparer.Default.Equals(Item3, other.Item3) && - EqualityComparer.Default.Equals(Item4, other.Item4) && - EqualityComparer.Default.Equals(Item5, other.Item5) && - EqualityComparer.Default.Equals(Item6, other.Item6) && - EqualityComparer.Default.Equals(Item7, other.Item7) && - EqualityComparer.Default.Equals(Item8, other.Item8); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((TarantoolTuple) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = EqualityComparer.Default.GetHashCode(Item1); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item2); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item3); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item4); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item5); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item6); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item7); - hashCode = (hashCode*397) ^ EqualityComparer.Default.GetHashCode(Item8); - return hashCode; - } - } - - public override string ToString() - { - return $"{Item1}, {Item2}, {Item3}, {Item4}, {Item5}, {Item6}, {Item7}, {Item8}"; - } - } - - - public class TarantoolTuple : ITarantoolTuple - { - private TarantoolTuple() - { - } - - public static TarantoolTuple Empty { get; } = new TarantoolTuple(); - - public static TarantoolTuple - Create(T1 item1) - { - return new TarantoolTuple - (item1); - } - - - public static TarantoolTuple - Create(T1 item1, T2 item2) - { - return new TarantoolTuple - (item1, item2); - } - - - public static TarantoolTuple - Create(T1 item1, T2 item2, T3 item3) - { - return new TarantoolTuple - (item1, item2, item3); - } - - - public static TarantoolTuple - Create(T1 item1, T2 item2, T3 item3, T4 item4) - { - return new TarantoolTuple - (item1, item2, item3, item4); - } - - - public static TarantoolTuple - Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) - { - return new TarantoolTuple - (item1, item2, item3, item4, item5); - } - - - public static TarantoolTuple - Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) - { - return new TarantoolTuple - (item1, item2, item3, item4, item5, item6); - } - - - public static TarantoolTuple - Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) - { - return new TarantoolTuple - (item1, item2, item3, item4, item5, item6, item7); - } - - - public static TarantoolTuple - Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, - T8 item8) - { - return new TarantoolTuple - (item1, item2, item3, item4, item5, item6, item7, item8); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/UpdateOperations/UpdateOperation.cs b/src/progaudi.tarantool/Model/UpdateOperation.cs similarity index 96% rename from src/progaudi.tarantool/Model/UpdateOperations/UpdateOperation.cs rename to src/progaudi.tarantool/Model/UpdateOperation.cs index 3fd38a7b..33ac3b00 100644 --- a/src/progaudi.tarantool/Model/UpdateOperations/UpdateOperation.cs +++ b/src/progaudi.tarantool/Model/UpdateOperation.cs @@ -1,6 +1,4 @@ -using ProGaudi.MsgPack.Light; - -namespace ProGaudi.Tarantool.Client.Model.UpdateOperations +namespace ProGaudi.Tarantool.Client.Model { public class UpdateOperation : UpdateOperation { @@ -17,15 +15,15 @@ public UpdateOperation(string operationType, int fieldNumber, T argument) Argument = argument; } - public override IMsgPackConverter GetConverter(MsgPackContext context) - { - return (IMsgPackConverter) context.GetConverter>(); - } + //public override IMsgPackConverter GetConverter(MsgPackContext context) + //{ + // return (IMsgPackConverter) context.GetConverter>(); + //} } public abstract class UpdateOperation { - public abstract IMsgPackConverter GetConverter(MsgPackContext context); + //public abstract IMsgPackConverter GetConverter(MsgPackContext context); #region Integer Operation Factory diff --git a/src/progaudi.tarantool/Model/UpdateOperations/UpdateOperationType.cs b/src/progaudi.tarantool/Model/UpdateOperationType.cs similarity index 91% rename from src/progaudi.tarantool/Model/UpdateOperations/UpdateOperationType.cs rename to src/progaudi.tarantool/Model/UpdateOperationType.cs index a8b6760f..b4f65a15 100644 --- a/src/progaudi.tarantool/Model/UpdateOperations/UpdateOperationType.cs +++ b/src/progaudi.tarantool/Model/UpdateOperationType.cs @@ -1,4 +1,4 @@ -namespace ProGaudi.Tarantool.Client.Model.UpdateOperations +namespace ProGaudi.Tarantool.Client.Model { public static class UpdateOperationType { diff --git a/src/progaudi.tarantool/Model/Requests/UpdateRequest.cs b/src/progaudi.tarantool/Model/UpdateRequest.cs similarity index 69% rename from src/progaudi.tarantool/Model/Requests/UpdateRequest.cs rename to src/progaudi.tarantool/Model/UpdateRequest.cs index 783ee874..c93b8098 100644 --- a/src/progaudi.tarantool/Model/Requests/UpdateRequest.cs +++ b/src/progaudi.tarantool/Model/UpdateRequest.cs @@ -1,7 +1,4 @@ -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.UpdateOperations; - -namespace ProGaudi.Tarantool.Client.Model.Requests +namespace ProGaudi.Tarantool.Client.Model { public class UpdateRequest : IRequest { @@ -21,6 +18,6 @@ public UpdateRequest(uint spaceId, uint indexId, TKey key, UpdateOperation[] upd public UpdateOperation[] UpdateOperations { get; } - public CommandCode Code => CommandCode.Update; + public CommandCodes Code => CommandCodes.Update; } } \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Requests/UpsertRequest.cs b/src/progaudi.tarantool/Model/UpsertRequest.cs similarity index 66% rename from src/progaudi.tarantool/Model/Requests/UpsertRequest.cs rename to src/progaudi.tarantool/Model/UpsertRequest.cs index da7997c9..3d11e5f3 100644 --- a/src/progaudi.tarantool/Model/Requests/UpsertRequest.cs +++ b/src/progaudi.tarantool/Model/UpsertRequest.cs @@ -1,7 +1,4 @@ -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.UpdateOperations; - -namespace ProGaudi.Tarantool.Client.Model.Requests +namespace ProGaudi.Tarantool.Client.Model { public class UpsertRequest : IRequest { @@ -18,6 +15,6 @@ public UpsertRequest(uint spaceId, TTuple tuple, UpdateOperation[] updateOperati public TTuple Tuple { get; } - public CommandCode Code => CommandCode.Upsert; + public CommandCodes Code => CommandCodes.Upsert; } } \ No newline at end of file diff --git a/src/progaudi.tarantool/NameIdLazyWrapper.cs b/src/progaudi.tarantool/NameIdLazyWrapper.cs new file mode 100644 index 00000000..3658cb98 --- /dev/null +++ b/src/progaudi.tarantool/NameIdLazyWrapper.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; + +namespace ProGaudi.Tarantool.Client +{ + public sealed class NameIdLazyWrapper + { + private static T _nil; + private readonly T[] _entities; + private readonly Func _nameGetter; + private readonly Func _idGetter; + private Dictionary _idCache; + private Dictionary _nameCache; + + public NameIdLazyWrapper(T[] entities, Func idGetter, Func nameGetter) + { + _entities = entities; + _nameGetter = nameGetter; + _idGetter = idGetter; + } + + public ref T this[string name] + { + get + { + Init(); + + if (!_nameCache.TryGetValue(name, out var index)) + { + return ref _nil; + } + + return ref _entities[index]; + } + } + + public ref T this[uint id] + { + get + { + Init(); + + if (!_idCache.TryGetValue(id, out var index)) + { + return ref _nil; + } + + return ref _entities[index]; + } + } + + private void Init() + { + if (_nameCache != null) + { + return; + } + + var nameCache = new Dictionary(); + var idCache = new Dictionary(); + for (var i = 0; i < _entities.Length; i++) + { + var entity = _entities[i]; + nameCache[_nameGetter(entity)] = i; + idCache[_idGetter(entity)] = i; + } + + _idCache = idCache; + _nameCache = nameCache; + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs b/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs index 4e0318f2..fd07e50f 100644 --- a/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs +++ b/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs @@ -1,16 +1,14 @@ using System; +using System.Buffers; using System.IO; using System.Linq; using System.Net.Sockets; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; using ProGaudi.Tarantool.Client.Utils; -#if PROGAUDI_NETCORE -using System.Net; -#endif - namespace ProGaudi.Tarantool.Client { internal class NetworkStreamPhysicalConnection : IPhysicalConnection @@ -41,20 +39,24 @@ public async Task Connect(ClientOptions options) _socket = new Socket(SocketType.Stream, ProtocolType.Tcp) { - NoDelay = true + NoDelay = true, + //Blocking = false }; - await ConnectAsync(_socket, singleNode.Uri.Host, singleNode.Uri.Port).ConfigureAwait(false);; + await _socket.ConnectAsync(singleNode.Uri.Host, singleNode.Uri.Port).ConfigureAwait(false); _stream = new NetworkStream(_socket, true); options.LogWriter?.WriteLine("Socket connection established."); } - public void Write(byte[] buffer, int offset, int count) + public void Write(in ReadOnlySequence buffer) { CheckConnectionStatus(); - _stream.Write(buffer, offset, count); + var array = buffer.ToArray(); + _stream.Write(array, 0, array.Length); } + public Stream Stream => _stream; + public async Task Flush() { CheckConnectionStatus(); @@ -67,47 +69,9 @@ public async Task ReadAsync(byte[] buffer, int offset, int count) return await _stream.ReadAsync(buffer, offset, count).ConfigureAwait(false); } -#if PROGAUDI_NETCORE - /// https://github.com/mongodb/mongo-csharp-driver/commit/9c2097f349d5096a04ea81b0c9ceb60c7e1acee4 - private static async Task ConnectAsync(Socket socket, string host, int port) - { - var resolved = await Dns.GetHostAddressesAsync(host).ConfigureAwait(false);; - for (var i = 0; i < resolved.Length; i++) - { - try - { - await socket.ConnectAsync(resolved[i], port).ConfigureAwait(false); - return; - } - catch - { - // if we have tried all of them and still failed, - // then blow up. - if (i == resolved.Length - 1) - { - throw; - } - } - } - - // we should never get here... - throw new InvalidOperationException("Unabled to resolve endpoint."); - } -#else - /// Stolen from corefx github - private static Task ConnectAsync(Socket socket, string host, int port) - { - return Task.Factory.FromAsync( - (targetHost, targetPort, callback, state) => ((Socket)state).BeginConnect(targetHost, targetPort, callback, state), - asyncResult => ((Socket)asyncResult.AsyncState).EndConnect(asyncResult), - host, - port, - socket); - } -#endif - public bool IsConnected => !_disposed && _stream != null; + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void CheckConnectionStatus() { if (_disposed) diff --git a/src/progaudi.tarantool/RequestWriter.cs b/src/progaudi.tarantool/RequestWriter.cs index ace44246..203eecde 100644 --- a/src/progaudi.tarantool/RequestWriter.cs +++ b/src/progaudi.tarantool/RequestWriter.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.Threading; using ProGaudi.Tarantool.Client.Model; @@ -9,7 +10,7 @@ internal class RequestWriter : IRequestWriter { private readonly ClientOptions _clientOptions; private readonly IPhysicalConnection _physicalConnection; - private readonly Queue, ArraySegment>> _buffer; + private readonly Queue> _buffer; private readonly object _lock = new object(); private readonly Thread _thread; private readonly ManualResetEventSlim _exitEvent; @@ -20,11 +21,11 @@ public RequestWriter(ClientOptions clientOptions, IPhysicalConnection physicalCo { _clientOptions = clientOptions; _physicalConnection = physicalConnection; - _buffer = new Queue, ArraySegment>>(); + _buffer = new Queue>(); _thread = new Thread(WriteFunction) { IsBackground = true, - Name = $"{clientOptions.Name} :: Write" + Name = $"Tarantool :: {clientOptions.Name} :: Write" }; _exitEvent = new ManualResetEventSlim(); _newRequestsAvailable = new ManualResetEventSlim(); @@ -34,7 +35,7 @@ public void BeginWriting() { if (_disposed) { - throw new ObjectDisposedException(nameof(ResponseReader)); + throw new ObjectDisposedException(nameof(RequestWriter)); } _clientOptions?.LogWriter?.WriteLine("Starting thread"); @@ -43,18 +44,18 @@ public void BeginWriting() public bool IsConnected => !_disposed; - public void Write(ArraySegment header, ArraySegment body) + public void Write(in ReadOnlySequence body) { if (_disposed) { - throw new ObjectDisposedException(nameof(ResponseReader)); + throw new ObjectDisposedException(nameof(RequestWriter)); } - _clientOptions?.LogWriter?.WriteLine($"Enqueuing request: headers {header.Count} bytes, body {body.Count} bytes."); + _clientOptions?.LogWriter?.WriteLine($"Enqueuing request: body {body.Length} bytes."); bool shouldSignal; lock (_lock) { - _buffer.Enqueue(Tuple.Create(header, body)); + _buffer.Enqueue(body); shouldSignal = _buffer.Count == 1; } @@ -97,32 +98,23 @@ private void WriteFunction() private void WriteRequests(int limit) { - void WriteBuffer(ArraySegment buffer) - { - _physicalConnection.Write(buffer.Array, buffer.Offset, buffer.Count); - } - - Tuple, ArraySegment> GetRequest() + var count = 0; + while (true) { + ReadOnlySequence request; lock (_lock) { if (_buffer.Count > 0) - return _buffer.Dequeue(); + request = _buffer.Dequeue(); + else + break; } - return null; - } - - Tuple, ArraySegment> request; - var count = 0; - while ((request = GetRequest()) != null) - { - _clientOptions?.LogWriter?.WriteLine($"Writing request: headers {request.Item1.Count} bytes, body {request.Item2.Count} bytes."); + _clientOptions.LogWriter?.WriteLine($"Writing request: {request.Length} bytes."); - WriteBuffer(request.Item1); - WriteBuffer(request.Item2); + _physicalConnection.Write(request); - _clientOptions?.LogWriter?.WriteLine($"Wrote request: headers {request.Item1.Count} bytes, body {request.Item2.Count} bytes."); + _clientOptions.LogWriter?.WriteLine($"Wrote request: {request.Length} bytes."); count++; if (limit > 0 && count > limit) diff --git a/src/progaudi.tarantool/ResponseReader.cs b/src/progaudi.tarantool/ResponseReader.cs index 93d15334..e1d0799f 100644 --- a/src/progaudi.tarantool/ResponseReader.cs +++ b/src/progaudi.tarantool/ResponseReader.cs @@ -1,31 +1,36 @@ using System; +using System.Buffers; +using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; - using JetBrains.Annotations; - using ProGaudi.MsgPack.Light; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Headers; -using ProGaudi.Tarantool.Client.Model.Responses; using ProGaudi.Tarantool.Client.Utils; namespace ProGaudi.Tarantool.Client { internal class ResponseReader : IResponseReader { + private static readonly AsyncCallback EndRead = result => + { + ResponseReader reader; + if (result.CompletedSynchronously || (reader = result.AsyncState as ResponseReader) == null) return; + if (reader.EndReading(result)) + reader.BeginReading(); + }; + private readonly IPhysicalConnection _physicalConnection; - private readonly Dictionary> _pendingRequests = - new Dictionary>(); + private readonly Dictionary> _pendingRequests = new Dictionary>(); private readonly ReaderWriterLockSlim _pendingRequestsLock = new ReaderWriterLockSlim(); private readonly ClientOptions _clientOptions; + private readonly byte[] _originalBuffer; private byte[] _buffer; @@ -39,7 +44,7 @@ public ResponseReader(ClientOptions clientOptions, IPhysicalConnection physicalC { _physicalConnection = physicalConnection; _clientOptions = clientOptions; - _buffer = new byte[clientOptions.ConnectionOptions.ReadStreamBufferSize]; + _originalBuffer = _buffer = ArrayPool.Shared.Rent(1024 * 1024); } public bool IsConnected => !_disposed; @@ -51,110 +56,114 @@ public void Dispose() return; } + _disposed = true; _disposed = true; - try - { - _pendingRequestsLock.EnterWriteLock(); + ArrayPool.Shared.Return(_originalBuffer); + var exception = new ObjectDisposedException(nameof(ResponseReader)); + lock (_pendingRequestsLock) + { _clientOptions.LogWriter?.WriteLine("Cancelling all pending requests and setting faulted state..."); - foreach (var response in _pendingRequests.Values) + foreach (var value in _pendingRequests.Values) { - response.SetException(new ObjectDisposedException(nameof(ResponseReader))); + value.Item2(exception); } _pendingRequests.Clear(); } - finally - { - _pendingRequestsLock.ExitWriteLock(); - } } - public Task GetResponseTask(RequestId requestId) + private delegate void ResultSetter(in MemoryStream response); + + private delegate void ExceptionSetter(Exception ex); + + public Task GetResponseTask(RequestId requestId, Func responseCreator) { - try + if (_disposed) { - _pendingRequestsLock.EnterWriteLock(); - - if (_disposed) - { - throw new ObjectDisposedException(nameof(ResponseReader)); - } + throw new ObjectDisposedException(nameof(ResponseReader)); + } + lock (_pendingRequestsLock) + { if (_pendingRequests.ContainsKey(requestId)) { throw ExceptionHelper.RequestWithSuchIdAlreadySent(requestId); } - var tcs = new TaskCompletionSource(); - _pendingRequests.Add(requestId, tcs); + var tcs = new TaskCompletionSource(); + var completionPair = Tuple.Create(ResultSetterImpl, ExceptionSetterImpl); + + _pendingRequests.Add(requestId, completionPair); return tcs.Task; - } - finally - { - _pendingRequestsLock.ExitWriteLock(); + + void ResultSetterImpl(in MemoryStream response) => tcs.SetResult(responseCreator(response)); + + void ExceptionSetterImpl(Exception ex) => tcs.SetException(ex); } } public void BeginReading() { - var freeBufferSpace = EnsureSpaceAndComputeBytesToRead(); - - _clientOptions.LogWriter?.WriteLine($"Begin reading from connection to buffer, bytes count: {freeBufferSpace}"); - - var readingTask = _physicalConnection.ReadAsync(_buffer, _readingOffset, freeBufferSpace); - readingTask.ContinueWith(EndReading); - } + if (_disposed) + { + throw new ObjectDisposedException(nameof(ResponseReader)); + } - private TaskCompletionSource PopResponseCompletionSource(RequestId requestId) - { try { - _pendingRequestsLock.EnterWriteLock(); - - if (_disposed) + bool keepReading; + do { - throw new ObjectDisposedException(nameof(ResponseReader)); - } - - if (_pendingRequests.TryGetValue(requestId, out var request)) - { - _pendingRequests.Remove(requestId); - } - - return request; + keepReading = false; + var space = EnsureSpaceAndComputeBytesToRead(); + var result = _physicalConnection.Stream.BeginRead(_buffer, _readingOffset, space, EndRead, this); + if (result.CompletedSynchronously) + { + keepReading = EndReading(result); + } + } while (keepReading); } - finally + catch (IOException ex) { - _pendingRequestsLock.ExitWriteLock(); + _clientOptions?.LogWriter?.WriteLine("Could not connect: " + ex.Message); } } - private void EndReading(Task readWork) + private Tuple PopResponseCompletionSource(RequestId requestId) { if (_disposed) { - _clientOptions.LogWriter?.WriteLine("Attempt to end reading in disposed state... Exiting."); - return; + throw new ObjectDisposedException(nameof(ResponseReader)); } - if (readWork.Status == TaskStatus.RanToCompletion) - { - var readBytesCount = readWork.Result; - _clientOptions.LogWriter?.WriteLine($"End reading from connection, read bytes count: {readBytesCount}"); + Tuple request; - if (ProcessReadBytes(readBytesCount)) + lock (_pendingRequestsLock) + { + if (_pendingRequests.TryGetValue(requestId, out request)) { - BeginReading(); - return; + _pendingRequests.Remove(requestId); } } - _clientOptions.LogWriter?.WriteLine($"Connection read failed: {readWork.Exception}"); - Dispose(); + return request; + } + + private bool EndReading(IAsyncResult ar) + { + try + { + var bytesRead = _physicalConnection?.Stream.EndRead(ar) ?? 0; + return ProcessReadBytes(bytesRead); + } + catch (Exception) + { + return false; + } } private bool ProcessReadBytes(int readBytesCount) @@ -164,9 +173,11 @@ private bool ProcessReadBytes(int readBytesCount) _clientOptions.LogWriter?.WriteLine("EOF"); return false; } + _readingOffset += readBytesCount; var parsedResponsesCount = TryParseResponses(); _clientOptions.LogWriter?.WriteLine("Processed: " + parsedResponsesCount); + if (!AllBytesProcessed()) { CopyRemainingBytesToBufferBegin(); @@ -201,7 +212,7 @@ private int TryParseResponses() do { var response = TryParseResponse(); - nonEmptyResult = response != null; + nonEmptyResult = response != Empty; if (!nonEmptyResult) { continue; @@ -213,10 +224,10 @@ private int TryParseResponses() return messageCount; } - private void MatchResult(byte[] result) + private void MatchResult(in ArraySegment result) { - var resultStream = new MemoryStream(result); - var header= MsgPackSerializer.Deserialize(resultStream, _clientOptions.MsgPackContext); + var stream = new MemoryStream(result.Array, result.Offset, result.Count, false, true); + var header = MsgPackSerializer.Deserialize(stream, _clientOptions.MsgPackContext); var tcs = PopResponseCompletionSource(header.RequestId); if (tcs == null) @@ -226,19 +237,27 @@ private void MatchResult(byte[] result) return; } - if ((header.Code & CommandCode.ErrorMask) == CommandCode.ErrorMask) + if ((header.Code & CommandCodes.ErrorMask) == CommandCodes.ErrorMask) { - var errorResponse = MsgPackSerializer.Deserialize(resultStream, _clientOptions.MsgPackContext); - tcs.SetException(ExceptionHelper.TarantoolError(header, errorResponse)); + _clientOptions.LogWriter?.WriteLine($"Match for request with id {header.RequestId} is error."); + var errorResponse = MsgPackSerializer.Deserialize(stream, _clientOptions.MsgPackContext); + tcs.Item2(ExceptionHelper.TarantoolError(header, errorResponse)); } else { - _clientOptions.LogWriter?.WriteLine($"Match for request with id {header.RequestId} found."); - tcs.SetResult(resultStream); + _clientOptions.LogWriter?.WriteLine($"Match for request with id {header.RequestId} is ok."); + try + { + tcs.Item1(stream); + } + catch (Exception e) + { + tcs.Item2(e); + } } } - private static void LogUnMatchedResponse(byte[] result, [NotNull]ILog logWriter) + private static void LogUnMatchedResponse(Span result, [NotNull]ILog logWriter) { var builder = new StringBuilder("Warning: can't match request via requestId from response. Response:"); var length = 80/3; @@ -255,49 +274,39 @@ private static void LogUnMatchedResponse(byte[] result, [NotNull]ILog logWriter) logWriter.WriteLine(builder.ToString()); } - private byte[] TryParseResponse() + private static readonly ArraySegment Empty = new ArraySegment(Array.Empty()); + private ArraySegment TryParseResponse() { if (AllBytesProcessed()) { - return null; + return Empty; } - var packetSize = GetPacketSize(); - - if (!packetSize.HasValue) + if (_readingOffset - _parsingOffset < Constants.PacketSizeBufferSize) { _clientOptions.LogWriter?.WriteLine($"Can't read packet length, has less than {Constants.PacketSizeBufferSize} bytes."); - return null; + return Empty; } - if (PacketCompletelyRead(packetSize.Value)) + var length = new ReadOnlySpan(_buffer, _parsingOffset, 5); + if (length[0] != (byte) DataTypes.UInt32) { - _parsingOffset += Constants.PacketSizeBufferSize; - var responseBuffer = new byte[packetSize.Value]; - Array.Copy(_buffer, _parsingOffset, responseBuffer, 0, packetSize.Value); - _parsingOffset += packetSize.Value; - - - return responseBuffer; + throw ExceptionUtils.BadTypeException((DataTypes) length[0], DataTypes.UInt32); } - _clientOptions.LogWriter?.WriteLine($"Packet with length {packetSize} is not completely read."); - - return null; - } + var packetSize = (int)BinaryPrimitives.ReadUInt32BigEndian(length.Slice(1)); - private int? GetPacketSize() - { - if (_readingOffset - _parsingOffset < Constants.PacketSizeBufferSize) + if (PacketCompletelyRead(packetSize)) { - return null; + var offset = _parsingOffset += Constants.PacketSizeBufferSize; + _parsingOffset += packetSize; + + return new ArraySegment(_buffer, offset, packetSize); } - var headerSizeBuffer = new byte[Constants.PacketSizeBufferSize]; - Array.Copy(_buffer, _parsingOffset, headerSizeBuffer, 0, Constants.PacketSizeBufferSize); - var packetSize = (int)MsgPackSerializer.Deserialize(headerSizeBuffer, _clientOptions.MsgPackContext); + _clientOptions.LogWriter?.WriteLine($"Packet with length {packetSize} is not completely read."); - return packetSize; + return Empty; } private bool PacketCompletelyRead(int packetSize) @@ -313,6 +322,8 @@ private int EnsureSpaceAndComputeBytesToRead() return space; } + _clientOptions.LogWriter?.WriteLine($"Resizing buffer from {_buffer.Length} to {_buffer.Length * 2}"); + Array.Resize(ref _buffer, _buffer.Length * 2); space = _buffer.Length - _readingOffset; return space; diff --git a/src/progaudi.tarantool/Schema.cs b/src/progaudi.tarantool/Schema.cs index a377caed..27d4737c 100644 --- a/src/progaudi.tarantool/Schema.cs +++ b/src/progaudi.tarantool/Schema.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; -using System.Threading; +using System.Linq; using System.Threading.Tasks; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Requests; -using ProGaudi.Tarantool.Client.Utils; +using ProGaudi.Tarantool.Client.Model; namespace ProGaudi.Tarantool.Client { @@ -16,51 +14,71 @@ public class Schema : ISchema internal const uint PrimaryIndexId = 0; - private readonly ILogicalConnection _logicalConnection; + private NameIdLazyWrapper _spaces; - private Dictionary _indexByName = new Dictionary(); + private Dictionary _indicesBySpace = new Dictionary(); + private IndexMeta[] _indices; - private Dictionary _indexById = new Dictionary(); + private readonly object _lockObject = new object(); public Schema(ILogicalConnection logicalConnection) { - _logicalConnection = logicalConnection; + Connection = logicalConnection; } - public Task GetSpace(string name) => Task.FromResult(this[name]); + public ILogicalConnection Connection { get; } + + public DateTimeOffset LastReloadTime { get; private set; } - public Task GetSpace(uint id) => Task.FromResult(this[id]); + public bool TryGetSpace(string name, out ISpace space) + { + SpaceMeta meta; + lock (_lockObject) + { + meta = _spaces[name]; + } - public ISpace this[string name] => _indexByName.TryGetValue(name, out var space) ? space : throw ExceptionHelper.InvalidSpaceName(name); + space = new Space(this, meta, _indicesBySpace[meta.Id].Select(x => _indices[x])); + return true; + } - public ISpace this[uint id] => _indexById.TryGetValue(id, out var space) ? space : throw ExceptionHelper.InvalidSpaceId(id); + public bool TryGetSpace(uint id, out ISpace space) + { + SpaceMeta meta; + lock (_lockObject) + { + meta = _spaces[id]; + } - public DateTimeOffset LastReloadTime { get; private set; } + space = new Space(this, meta, _indicesBySpace[meta.Id].Select(x => _indices[x])); + return true; + } public async Task Reload() { - var byName = new Dictionary(); - var byId = new Dictionary(); - - var spaces = await Select(VSpace).ConfigureAwait(false); - foreach (var space in spaces) + var spaces = await Select(VSpace).ConfigureAwait(false); + var indices = await Select(VIndex).ConfigureAwait(false); + var indicesBySpace = indices + .Select((x, i) => new {x, i}) + .GroupBy(x => x.x.SpaceId) + .ToDictionary(x => x.Key, x => x.Select(y => y.i).ToArray()); + + lock (_lockObject) { - byName[space.Name] = space; - byId[space.Id] = space; - space.LogicalConnection = _logicalConnection; - space.SetIndices(await Select(VIndex, Iterator.Eq, space.Id).ConfigureAwait(false)); - } + _spaces = new NameIdLazyWrapper(spaces, x => x.Id, x => x.Name); + + _indices = indices; + _indicesBySpace = indicesBySpace; - Interlocked.Exchange(ref _indexByName, byName); - Interlocked.Exchange(ref _indexById, byId); - LastReloadTime = DateTimeOffset.UtcNow; + LastReloadTime = DateTimeOffset.UtcNow; + } } private async Task Select(uint spaceId, Iterator iterator = Iterator.All, uint id = 0u) { var request = new SelectRequest>(spaceId, PrimaryIndexId, uint.MaxValue, 0, iterator, ValueTuple.Create(id)); - var response = await _logicalConnection + var response = await Connection .SendRequest>, T>(request) .ConfigureAwait(false); return response.Data; diff --git a/src/progaudi.tarantool/SocketPhysicalConnection.cs b/src/progaudi.tarantool/SocketPhysicalConnection.cs new file mode 100644 index 00000000..271ea92a --- /dev/null +++ b/src/progaudi.tarantool/SocketPhysicalConnection.cs @@ -0,0 +1,67 @@ +using System; +using System.Buffers; +using System.IO; +using System.Linq; +using System.Net.Sockets; +using System.Threading.Tasks; +using ProGaudi.Tarantool.Client.Model; + +namespace ProGaudi.Tarantool.Client +{ + public sealed class SocketPhysicalConnection : IPhysicalConnection + { + private Socket _socket; + + private bool _disposed; + + public void Dispose() + { + if (_disposed) + { + return; + } + + _disposed = true; + + _socket?.Dispose(); + } + + public async Task Connect(ClientOptions options) + { + options.LogWriter?.WriteLine("Starting socket connection..."); + var singleNode = options.ConnectionOptions.Nodes.Single(); + + _socket = new Socket(SocketType.Stream, ProtocolType.Tcp) + { + NoDelay = true, + //Blocking = false + }; + await _socket.ConnectAsync(singleNode.Uri.Host, singleNode.Uri.Port).ConfigureAwait(false); + + options.LogWriter?.WriteLine("Socket connection established."); + } + + public Task Flush() + { + throw new NotImplementedException(); + } + + public bool IsConnected { get; } + public Task ReadAsync(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + public void Write(in ReadOnlySequence buffer) + { + throw new NotImplementedException(); + } + + public Stream Stream { get; } + + public void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Space.cs b/src/progaudi.tarantool/Space.cs index 848122a6..e186ece0 100644 --- a/src/progaudi.tarantool/Space.cs +++ b/src/progaudi.tarantool/Space.cs @@ -1,147 +1,148 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; - using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Requests; -using ProGaudi.Tarantool.Client.Model.Responses; -using ProGaudi.Tarantool.Client.Model.UpdateOperations; -using ProGaudi.Tarantool.Client.Utils; namespace ProGaudi.Tarantool.Client { - public class Space : ISpace + public class Space : ISpace { - private Dictionary _indexByName = new Dictionary(); - private Dictionary _indexById = new Dictionary(); + private readonly Schema _schema; + private readonly SpaceMeta _meta; + private readonly Lazy> _indices; - public ILogicalConnection LogicalConnection { get; set; } + public Space(Schema schema, SpaceMeta meta, IEnumerable indexMetas) + { + _schema = schema; + _meta = meta; + _indices = new Lazy>(() => new NameIdLazyWrapper(indexMetas.ToArray(), x => x.Id, x => x.Name)); + } - public Space(uint id, uint fieldCount, string name, StorageEngine engine, IReadOnlyCollection fields) + public override string ToString() { - Id = id; - FieldCount = fieldCount; - Name = name; - Engine = engine; - Fields = fields; + return $"{Name}, id={Id}, Engine={Engine}"; } - public uint Id { get; } + public uint Id => _meta.Id; + + public int OwnerId => _meta.OwnerId; + + public string Name => _meta.Name; + + public StorageEngine Engine => _meta.Engine; - public uint FieldCount { get; } + public uint FieldCount => _meta.FieldCount; - public string Name { get; } + public SpaceOptions Options => _meta.Options; - public StorageEngine Engine { get; } + public SpaceField[] Fields => _meta.Fields; - public IReadOnlyCollection Indices => _indexByName.Values; + public IIndex this[string name] => TryGetIndex(name, out var x) ? x : throw new IndexOutOfRangeException(); - internal void SetIndices(IReadOnlyCollection value) + public IIndex this[uint id] => TryGetIndex(id, out var x) ? x : throw new IndexOutOfRangeException(); + + public bool TryGetIndex(string name, out IIndex index) => TryGetIndex(name, out index); + + public bool TryGetIndex(uint id, out IIndex index) => TryGetIndex(id, out index); + + public bool TryGetIndex(string name, out IIndex index) { - var byName = new Dictionary(); - var byId = new Dictionary(); + var meta = _indices.Value[name]; - if (value != null) + if (meta == null) { - foreach (var index in value) - { - byName[index.Name] = index; - byId[index.Id] = index; - index.LogicalConnection = LogicalConnection; - } + index = default; + return false; } - Interlocked.Exchange(ref _indexByName, byName); - Interlocked.Exchange(ref _indexById, byId); + index = new Index(meta, _schema); + return true; } - public IReadOnlyCollection Fields { get; } - - public Task GetIndex(string name) => Task.FromResult(_indexByName.TryGetValue(name, out var index) ? index : throw ExceptionHelper.InvalidIndexName(name, Name)); + public bool TryGetIndex(uint id, out IIndex index) + { + var meta = _indices.Value[id]; - public Task GetIndex(uint id) => Task.FromResult(_indexById.TryGetValue(id, out var index) ? index : throw ExceptionHelper.InvalidIndexId(id, Name)); + if (meta == null) + { + index = default; + return false; + } - public IIndex this[string name] => _indexByName.TryGetValue(name, out var index) ? index : throw ExceptionHelper.InvalidIndexName(name, Name); + index = new Index(meta, _schema); + return true; + } - public IIndex this[uint id] => _indexById.TryGetValue(id, out var index) ? index : throw ExceptionHelper.InvalidIndexId(id, Name); + public async Task Insert(T tuple) + { + var result = await _schema.Connection.SendRequest, T>(new InsertRequest(Id, tuple)); + return result.Data[0]; + } - public async Task> Insert(TTuple tuple) + public Task Insert(in TInsertable tuple) { - var insertRequest = new InsertRequest(Id, tuple); - return await LogicalConnection.SendRequest, TTuple>(insertRequest).ConfigureAwait(false); + throw new NotImplementedException(); } - public async Task> Select(TKey selectKey) + public Task Select(TKey selectKey, Iterator iterator = Iterator.Eq, uint limit = UInt32.MaxValue, uint offset = 0) { - var selectRequest = new SelectRequest(Id, Schema.PrimaryIndexId, uint.MaxValue, 0, Iterator.Eq, selectKey); - return await LogicalConnection.SendRequest, TTuple>(selectRequest).ConfigureAwait(false); + throw new NotImplementedException(); } - public async Task Get(TKey key) + public Task Get(TKey key, GetOptions options = GetOptions.Eval) { - var selectRequest = new SelectRequest(Id, Schema.PrimaryIndexId, 1, 0, Iterator.Eq, key); - var response = await LogicalConnection.SendRequest, TTuple>(selectRequest).ConfigureAwait(false); - return response.Data.SingleOrDefault(); + throw new NotImplementedException(); } - public async Task> Replace(TTuple tuple) + public Task Replace(T tuple) { - var replaceRequest = new ReplaceRequest(Id, tuple); - return await LogicalConnection.SendRequest, TTuple>(replaceRequest).ConfigureAwait(false); + throw new NotImplementedException(); } - public async Task Put(T tuple) + public Task Put(T tuple) { - var response = await Replace(tuple).ConfigureAwait(false); - return response.Data.First(); + throw new NotImplementedException(); } - public async Task> Update(TKey key, UpdateOperation[] updateOperations) + public Task Update(TKey key, UpdateOperation[] updateOperations) { - var updateRequest = new UpdateRequest(Id, Schema.PrimaryIndexId, key, updateOperations); - return await LogicalConnection.SendRequest, TTuple>(updateRequest).ConfigureAwait(false); + throw new NotImplementedException(); } - public async Task Upsert(TTuple tuple, UpdateOperation[] updateOperations) + public Task Upsert(T tuple, UpdateOperation[] updateOperations) { - var upsertRequest = new UpsertRequest(Id, tuple, updateOperations); - await LogicalConnection.SendRequestWithEmptyResponse(upsertRequest).ConfigureAwait(false); + throw new NotImplementedException(); } - public async Task> Delete(TKey key) + public Task Upsert(TInsertable tuple, UpdateOperation[] updateOperations) { - var deleteRequest = new DeleteRequest(Id, Schema.PrimaryIndexId, key); - return await LogicalConnection.SendRequest, TTuple>(deleteRequest).ConfigureAwait(false); + throw new NotImplementedException(); } - public Task Count(TKey key) + public Task Delete(TKey key) { throw new NotImplementedException(); } - public Task Length() + public Task Count(TKey key, Iterator iterator) { throw new NotImplementedException(); } - public Task> Increment(TKey key) + public Task Count() { - // Currently we can't impelment that method because Upsert returns void. - throw new NotImplementedException(); + throw new NotImplementedException(); } - public Task> Decrement(TKey key) + public Task Length() { - // Currently we can't impelment that method because Upsert returns void. throw new NotImplementedException(); } - public override string ToString() + public Task ByteSize() { - return $"{Name}, id={Id}"; + throw new NotImplementedException(); } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/SpaceMeta.cs b/src/progaudi.tarantool/SpaceMeta.cs new file mode 100644 index 00000000..af0ad801 --- /dev/null +++ b/src/progaudi.tarantool/SpaceMeta.cs @@ -0,0 +1,75 @@ +using System; +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Model; + +namespace ProGaudi.Tarantool.Client +{ + [MsgPackArray] + public class SpaceMeta : IEquatable + { + public SpaceMeta() + { + } + + public SpaceMeta(uint id, int ownerId, string name, StorageEngine engine, uint fieldCount, SpaceOptions options, SpaceField[] fields) + { + Id = id; + OwnerId = ownerId; + Name = name; + Engine = engine; + FieldCount = fieldCount; + Options = options; + Fields = fields; + } + + [MsgPackArrayElement(0)] + public uint Id { get; set; } + + [MsgPackArrayElement(1)] + public int OwnerId { get; set; } + + [MsgPackArrayElement(2)] + public string Name { get; set; } + + [MsgPackArrayElement(3)] + public StorageEngine Engine { get; set; } + + [MsgPackArrayElement(4)] + public uint FieldCount { get; set; } + + [MsgPackArrayElement(5)] + public SpaceOptions Options { get; set; } + + [MsgPackArrayElement(6)] + public SpaceField[] Fields { get; set; } + + public override string ToString() + { + return $"{Name}({Id}, {Engine}, {Options.Temporary})."; + } + + public bool Equals(SpaceMeta other) + { + return Id == other.Id && OwnerId == other.OwnerId && string.Equals(Name, other.Name) && Engine == other.Engine && FieldCount == other.FieldCount; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + return obj is SpaceMeta && Equals((SpaceMeta) obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = (int) Id; + hashCode = (hashCode * 397) ^ OwnerId; + hashCode = (hashCode * 397) ^ (Name != null ? Name.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (int) Engine; + hashCode = (hashCode * 397) ^ (int) FieldCount; + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs b/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs index d2710f97..95d15297 100644 --- a/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs +++ b/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs @@ -1,9 +1,6 @@ using ProGaudi.MsgPack.Light; - using ProGaudi.Tarantool.Client.Converters; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Responses; namespace ProGaudi.Tarantool.Client { @@ -11,55 +8,48 @@ public class TarantoolConvertersRegistrator { public static void Register(MsgPackContext context) { - context.RegisterConverter(new EnumConverter()); - context.RegisterConverter(new EnumConverter()); + context.DiscoverConverters(); + + context.RegisterConverter(new EnumConverter()); context.RegisterConverter(new EnumConverter()); - context.RegisterConverter(new RequestIdConverter()); - context.RegisterConverter(new PacketSizeConverter()); - context.RegisterConverter(new FromStringEnumConverter()); - context.RegisterConverter(new FromStringEnumConverter()); - context.RegisterConverter(new FromStringEnumConverter()); - context.RegisterConverter(new FromStringEnumConverter()); + context.RegisterConverter(new EnumAsStringFormatter()); + context.RegisterConverter(new EnumAsStringFormatter()); + context.RegisterConverter(new EnumAsStringFormatter()); - context.RegisterConverter(new StringSliceOperationConverter()); - context.RegisterGenericConverter(typeof(UpdateOperationConverter<>)); + //context.RegisterConverter(new StringSliceOperationConverter()); + //context.RegisterGenericConverter(typeof(UpdateOperationConverter<>)); - context.RegisterConverter(new ResponseHeaderConverter()); - context.RegisterConverter(new RequestHeaderConverter()); + context.RegisterConverter(new ResponseHeader.Formatter()); + context.RegisterConverter(new RequestHeader.Formatter()); - context.RegisterConverter(new AuthenticationPacketConverter()); - context.RegisterConverter(new EmptyResponseConverter()); - context.RegisterConverter(new ErrorResponsePacketConverter()); + context.RegisterConverter(new AuthenticationRequest.Formatter()); + context.RegisterConverter(new IndexPart.Formatter()); + //context.RegisterConverter(new ErrorResponse); - context.RegisterConverter(new SpaceFieldConverter()); - context.RegisterConverter(new SpaceConverter()); - context.RegisterConverter(new IndexPartConverter()); - context.RegisterConverter(new IndexCreationOptionsConverter()); - context.RegisterConverter(new IndexConverter()); - context.RegisterConverter(new TupleConverter()); - context.RegisterConverter(new BoxInfo.Converter()); + //context.RegisterConverter(new TupleConverter()); + //context.RegisterConverter(new BoxInfo.Converter()); - context.RegisterGenericConverter(typeof(ResponsePacketConverter<>)); - context.RegisterConverter(new ResponsePacketConverter()); - context.RegisterGenericConverter(typeof(UpdatePacketConverter<>)); - context.RegisterGenericConverter(typeof(CallPacketConverter<>)); - context.RegisterGenericConverter(typeof(DeletePacketConverter<>)); - context.RegisterGenericConverter(typeof(EvalPacketConverter<>)); - context.RegisterGenericConverter(typeof(InsertReplacePacketConverter<>)); - context.RegisterGenericConverter(typeof(SelectPacketConverter<>)); - context.RegisterGenericConverter(typeof(UpsertPacketConverter<>)); - context.RegisterConverter(new PingPacketConverter()); - context.RegisterConverter(new ExecuteSqlRequestConverter()); + context.RegisterGenericConverter(typeof(DataResponse<>.Formatter)); + context.RegisterConverter(new DataResponse.Formatter()); + //context.RegisterGenericConverter(typeof(UpdatePacketConverter<>)); + //context.RegisterGenericConverter(typeof(CallPacketConverter<>)); + //context.RegisterGenericConverter(typeof(DeletePacketConverter<>)); + //context.RegisterGenericConverter(typeof(EvalPacketConverter<>)); + context.RegisterGenericConverter(typeof(InsertReplaceRequest<>.Formatter)); + context.RegisterGenericConverter(typeof(SelectRequest<>.Formatter)); + //context.RegisterGenericConverter(typeof(UpsertPacketConverter<>)); + //context.RegisterConverter(new PingPacketConverter()); + //context.RegisterConverter(new ExecuteSqlRequestConverter()); - context.RegisterGenericConverter(typeof(TupleConverter<>)); - context.RegisterGenericConverter(typeof(TupleConverter<,>)); - context.RegisterGenericConverter(typeof(TupleConverter<,,>)); - context.RegisterGenericConverter(typeof(TupleConverter<,,,>)); - context.RegisterGenericConverter(typeof(TupleConverter<,,,,>)); - context.RegisterGenericConverter(typeof(TupleConverter<,,,,,>)); - context.RegisterGenericConverter(typeof(TupleConverter<,,,,,,>)); - context.RegisterGenericConverter(typeof(TupleConverter<,,,,,,,>)); + //context.RegisterGenericConverter(typeof(TupleConverter<>)); + //context.RegisterGenericConverter(typeof(TupleConverter<,>)); + //context.RegisterGenericConverter(typeof(TupleConverter<,,>)); + //context.RegisterGenericConverter(typeof(TupleConverter<,,,>)); + //context.RegisterGenericConverter(typeof(TupleConverter<,,,,>)); + //context.RegisterGenericConverter(typeof(TupleConverter<,,,,,>)); + //context.RegisterGenericConverter(typeof(TupleConverter<,,,,,,>)); + //context.RegisterGenericConverter(typeof(TupleConverter<,,,,,,,>)); context.RegisterGenericConverter(typeof(SystemTupleConverter<>)); context.RegisterGenericConverter(typeof(SystemTupleConverter<,>)); diff --git a/src/progaudi.tarantool/TarantoolSegment.cs b/src/progaudi.tarantool/TarantoolSegment.cs new file mode 100644 index 00000000..3a3ba158 --- /dev/null +++ b/src/progaudi.tarantool/TarantoolSegment.cs @@ -0,0 +1,24 @@ +using System; +using System.Buffers; + +namespace ProGaudi.Tarantool.Client +{ + internal class TarantoolSegment : ReadOnlySequenceSegment + { + private TarantoolSegment(in ReadOnlyMemory memory, in TarantoolSegment next, in long runningIndex) + { + Memory = memory; + Next = next; + RunningIndex = runningIndex; + } + + public static ReadOnlySequence CreateSequence(in ReadOnlyMemory length, in ReadOnlyMemory header, in ReadOnlyMemory body) + { + var b = new TarantoolSegment(body, null, length.Length + header.Length); + var h = new TarantoolSegment(header, b, length.Length); + var l = new TarantoolSegment(length, h, 0); + + return new ReadOnlySequence(l, 0, b, b.Memory.Length); + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/TaskHelpers.cs b/src/progaudi.tarantool/TaskHelpers.cs index b79f6bed..422354a3 100644 --- a/src/progaudi.tarantool/TaskHelpers.cs +++ b/src/progaudi.tarantool/TaskHelpers.cs @@ -1,7 +1,4 @@ -// -// Copyright © eVote -// -namespace ProGaudi.Tarantool.Client +namespace ProGaudi.Tarantool.Client { using System; using System.Threading; @@ -26,4 +23,4 @@ public static async Task WithCancellation( return await task.ConfigureAwait(false); } } -} \ No newline at end of file +} diff --git a/src/progaudi.tarantool/Utils/ByteUtils.cs b/src/progaudi.tarantool/Utils/ByteUtils.cs index 4cc0251c..f68e5c6c 100644 --- a/src/progaudi.tarantool/Utils/ByteUtils.cs +++ b/src/progaudi.tarantool/Utils/ByteUtils.cs @@ -1,5 +1,8 @@ using System; +using System.Buffers; +using System.Buffers.Text; using System.Linq; +using System.Text; namespace ProGaudi.Tarantool.Client.Utils { @@ -14,5 +17,19 @@ public static string ToReadableString(this ArraySegment bytes) { return string.Join(" ", bytes.Select(b => b.ToString("X2"))); } + + public static string ToReadableString(this ReadOnlySequence bytes) + { + var output = new StringBuilder((int) (bytes.Length * 3)); + + foreach (var b in bytes) + { + var s = b.Span; + for (var i = 0; i < s.Length; i++) + output.Append(s[i].ToString("X2")); + } + + return output.ToString(); + } } } diff --git a/src/progaudi.tarantool/Utils/ExceptionHelper.cs b/src/progaudi.tarantool/Utils/ExceptionHelper.cs index c2b4df3e..7514e531 100644 --- a/src/progaudi.tarantool/Utils/ExceptionHelper.cs +++ b/src/progaudi.tarantool/Utils/ExceptionHelper.cs @@ -1,10 +1,5 @@ using System; -using ProGaudi.MsgPack.Light; - using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Headers; -using ProGaudi.Tarantool.Client.Model.Responses; using System.Reflection; using System.Runtime.Serialization; @@ -22,7 +17,7 @@ public static Exception InvalidMapLength(uint? actual, params uint [] expected) return new ArgumentException($"Invalid map length: {string.Join(", ", expected)} is expected, but got {actual}."); } - public static Exception UnexpectedKey(Key actual, params Key[] expected) + public static Exception UnexpectedKey(uint actual, params uint[] expected) { return new ArgumentException($"Unexpected key: {string.Join(", ", expected)} is expected, but got {actual}."); } @@ -32,11 +27,6 @@ public static Exception InvalidArrayLength(uint expected, uint? actual) return new ArgumentException($"Invalid array length: {expected} is expected, but got {actual}."); } - public static Exception UnexpectedDataType(DataTypes expected, DataTypes actual) - { - return new ArgumentException($"Unexpected data type: {expected} is expected, but got {actual}."); - } - public static Exception NotConnected() { return new InvalidOperationException("Can't perform operation. Looks like we are not connected to tarantool. Call 'Connect' method before calling any other operations."); @@ -103,7 +93,7 @@ public static Exception RequestWithSuchIdAlreadySent(RequestId requestId) return new ArgumentException($"Task with id {requestId} already sent."); } - private static string GetDetailedTarantoolMessage(CommandCode code) + private static string GetDetailedTarantoolMessage(CommandCodes code) { switch ((uint)code) { diff --git a/src/progaudi.tarantool/Utils/StringEnum.cs b/src/progaudi.tarantool/Utils/StringEnum.cs deleted file mode 100644 index 692f74fa..00000000 --- a/src/progaudi.tarantool/Utils/StringEnum.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Reflection; - -namespace ProGaudi.Tarantool.Client.Utils -{ - internal class StringValueAttribute : Attribute - { - public StringValueAttribute(string value) - { - Value = value; - } - - public string Value { get; } - } - - internal class StringEnum - { - public static T Parse(Type type, string stringValue, bool ignoreCase) - where T : struct - { - T output; - string enumStringValue = null; - - if (!type.GetTypeInfo().IsEnum) - { - throw ExceptionHelper.EnumExpected(type); - } - - if (!Enum.TryParse(stringValue, ignoreCase, out output)) - { - - //Look for our string value associated with fields in this enum - foreach (var fi in type.GetRuntimeFields()) - { - //Check for our custom attribute - var attrs = fi.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[]; - if (attrs.Length > 0) - enumStringValue = attrs[0].Value; - - //Check for equality then select actual enum value. - if (string.Compare(enumStringValue, stringValue, ignoreCase) == 0) - { - output = (T)Enum.Parse(type, fi.Name); - break; - } - } - } - - return output; - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/progaudi.tarantool.csproj b/src/progaudi.tarantool/progaudi.tarantool.csproj index 82dd3114..2cb610be 100644 --- a/src/progaudi.tarantool/progaudi.tarantool.csproj +++ b/src/progaudi.tarantool/progaudi.tarantool.csproj @@ -1,16 +1,17 @@ - - - net46;netstandard1.4;netstandard2.0 - - - netstandard1.4;netstandard2.0 + + + netstandard2.0 0 $(BuildNumber).1-prerelease - 0.9.$(PatchLevelVersion) + 1.0.$(PatchLevelVersion) - Tarantool low-level client Library + Tarantool client Library + + latest + + 4 true progaudi.tarantool @@ -26,11 +27,11 @@ true true - + - - + + @@ -53,16 +54,6 @@ - - SystemTupleConverters.tt - True - True - - - ValueTupleConverters.tt - True - True - True True @@ -79,8 +70,4 @@ ValueTupleConverters.tt - - - $(DefineConstants);PROGAUDI_NETCORE - \ No newline at end of file diff --git a/src/progaudi.tarantool/progaudi.tarantool.csproj.DotSettings b/src/progaudi.tarantool/progaudi.tarantool.csproj.DotSettings new file mode 100644 index 00000000..96331d1c --- /dev/null +++ b/src/progaudi.tarantool/progaudi.tarantool.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp72 \ No newline at end of file diff --git a/src/tests/Program.cs b/src/tests/Program.cs new file mode 100644 index 00000000..9b884eda --- /dev/null +++ b/src/tests/Program.cs @@ -0,0 +1,49 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Npgsql; + +namespace tests +{ + internal class Program + { + private static void Main() => MainAsync().GetAwaiter().GetResult(); + + private static async Task MainAsync() + { + try + { + var endpoint = new IPEndPoint(IPAddress.Loopback, 10000); + var socket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) + { + Blocking = false, + NoDelay = true + }; + var args = new SocketAsyncEventArgs + { + RemoteEndPoint = endpoint + }; + var c = new AwaitableSocket(args, socket); + await c.ConnectAsync(CancellationToken.None); + args.BufferList = new[] + { + new ArraySegment(Encoding.ASCII.GetBytes("Hello, world!\n")), + new ArraySegment(new byte[20]) + }; + + await c.SendAsync(); + await c.ReceiveAsync(); + + Console.WriteLine(Encoding.ASCII.GetString(args.BufferList[0])); + Console.WriteLine(Encoding.ASCII.GetString(args.BufferList[1])); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + } +} diff --git a/src/tests/tests.csproj b/src/tests/tests.csproj new file mode 100644 index 00000000..3d9c9fe9 --- /dev/null +++ b/src/tests/tests.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp2.1 + latest + + + + + + + + + + + + diff --git a/src/tests/tests.csproj.DotSettings b/src/tests/tests.csproj.DotSettings new file mode 100644 index 00000000..96331d1c --- /dev/null +++ b/src/tests/tests.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp72 \ No newline at end of file diff --git a/tarantool/testdata.lua b/tarantool/testdata.lua index 6cfcf676..161e46a9 100644 --- a/tarantool/testdata.lua +++ b/tarantool/testdata.lua @@ -1,26 +1,24 @@ local log = require("log") -local remote = require('net.box') -local compat = require('compat') +local remote = require("net.box") +local compat = require("compat") local new_parts_format_supported = compat.check_version{1, 7, 6} local is_nullable_supported = compat.check_version{1, 7, 6} local function create_space(space) - log.info{message2="Creating space.", name=space.name} - local format = {} - for name, field in pairs(space.fields) do - format[field.index] = { name=name, type=field.type } - if is_nullable_supported then - format[field.index].is_nullable = false - if field.is_nullable then - format[field.index].is_nullable = true - end - end - end + log.info({space=space.name, status="creating"}) + local format = {} + for name, field in pairs(space.fields) do + format[field.index] = { name=name, type=field.type, is_nullable=(field.is_nullable == true) } + end + + if space.sequence ~= nil then + box.schema.sequence.create(space.sequence, {if_not_exists = true}) + end - local created_space = box.schema.space.create(space.name, { format = format, if_not_exists=true, field_count=#format }) - log.info{message2="Created space.", name=space.name} - return created_space + local created_space = box.schema.space.create(space.name, { format = format, if_not_exists = true, temporary = space.temporary}) + log.info({space=space.name, status="created"}) + return created_space end local function create_index(space, name, unique, type, sequence, ...) @@ -42,93 +40,147 @@ local function create_index(space, name, unique, type, sequence, ...) sequence = sequence } - log.info{message2="Creating index", space=space.name, name=name, options=options} - local index = space:create_index(name, options) - log.info{message2="Created index", space=space.name, name=name} return index end +local function create_index(space, name, unique, type, sequence_name, ...) + log.info({space=space.name, index=name, status="creating"}) + local parts = {} + + for k, v in pairs({...}) do + table.insert(parts, v) + end + + local options = { + unique = unique, + type = type, + parts = parts, + if_not_exists = true, + } + + log.info({space=space.name, index=name, status="options", options=options}) + local index = space:create_index(name, options) + if sequence_name ~= nil then + index:alter({sequence = sequence_name}) + end + + log.info({space=space.name, index=name, status="created"}) + return index +end + local spaces = { primary_only_index = { name = "primary_only_index", fields = { - id = { index = 1, name="id", type = "unsigned" }, - name = { index = 2, name="name", type = "string" }, - price = { index = 3, name="price", type = "scalar", is_nullable=true } + id = { index = 1, type = "unsigned" }, + name = { index = 2, type = "string" }, + price = { index = 3, type = "scalar", is_nullable=true } } }, performance = { name = "performance", fields = { - id = { index = 1, name="id", type = "unsigned" }, - name = { index = 2, name="name", type = "string" } + id = { index = 1, type = "unsigned" }, + name = { index = 2, type = "string" } } }, treeIndexMethods = { name = "space_TreeIndexMethods", + sequence = "space_TreeIndexMethods_id", + fields = { + id = { index = 1, type = "unsigned" }, + name = { index = 2, type = "string" } + } + }, + with_scalar_index = { + name = "with_scalar_index", fields = { - id = { index = 1, name="id", type = "unsigned" }, - name = { index = 2, name="name", type = "string" } + id = { index = 1, type = "scalar" } + } + }, + pivot = { + name = "pivot", + temporary = true, + fields = { + id = { index = 1, type = "unsigned"}, + arr = { index = 2, type = "array" } } } } local function create_spaces_and_indecies() local space = create_space(spaces.primary_only_index) - create_index(space, "primary", true, "HASH", nil, spaces.primary_only_index.fields.id) + create_index(space, "primary", true, "HASH", nil, "id") space = create_space(spaces.performance) - create_index(space, "primary", true, "HASH", nil, spaces.performance.fields.id) + create_index(space, "primary", true, "HASH", nil, "id") + + space = create_space(spaces.with_scalar_index) + create_index(space, "primary", true, "TREE", nil, "id") - space = box.schema.space.create('with_scalar_index', { if_not_exists = true }) - space:create_index('primary', {type='tree', parts={1, 'scalar'}, if_not_exists = true}) + space = create_space(spaces.pivot) + create_index(space, "primary", true, "TREE", nil, "id") + create_index(space, "rtree", false, "RTREE", nil, "arr") + + space = create_space(spaces.treeIndexMethods) + create_index(space, "primary", true, "TREE", spaces.treeIndexMethods.sequence, "id") + + space:insert{nil, "asdf"} + space:insert{nil, "zcxv"} + space:insert{nil, "qwer"} end local function init() + log.info{stage="Spaces",status="begin"} create_spaces_and_indecies() + log.info{stage="Spaces",status="end"} - box.schema.user.create('notSetPassword', { if_not_exists = true }) - box.schema.user.create('emptyPassword', { password = '', if_not_exists = true }) + log.info{stage="Users",status="begin"} + box.schema.user.create("notSetPassword", { if_not_exists = true }) + box.schema.user.create("emptyPassword", { password = "", if_not_exists = true }) - box.schema.user.create('operator', {password = 'operator', if_not_exists = true }) - box.schema.user.grant('operator','read,write,execute','universe', { if_not_exists = true }) - box.schema.user.grant('guest','read,write,execute','universe', { if_not_exists = true }) - box.schema.user.grant('emptyPassword','read,write,execute','universe', { if_not_exists = true }) - box.schema.user.passwd('admin', 'adminPassword') -end + box.schema.user.create("operator", {password = "operator", if_not_exists = true }) + log.info{stage="Users",status="end"} + + log.info{stage="Grants",status="begin"} -local function space_TreeIndexMethods() - local sequence = box.schema.sequence.create('space_TreeIndexMethods_id') - local space = create_space(spaces.treeIndexMethods) - create_index(space, "treeIndex", true, "TREE", sequence.name, spaces.treeIndexMethods.fields.id) + local users = {"operator", "guest", "emptyPassword"} - space:insert{nil, 'asdf'} - space:insert{nil, 'zcxv'} - space:insert{nil, 'qwer'} + for _, user in pairs(users) do + box.schema.user.grant(user, "execute", "universe", nil, { if_not_exists = true }) + + for _, space in pairs(spaces) do + box.schema.user.grant(user, 'read,write', 'space', space.name, {if_not_exists = true}) + if space.sequence ~= nil then + box.schema.user.grant(user, 'read,write', 'sequence', space.sequence, {if_not_exists = true}) + end + end + end + + log.info{stage="Grants",status="end"} end -box.once('init', init) -box.once('space_TreeIndexMethods', space_TreeIndexMethods) +box.once("init", init) -local log = require('log') +local log = require("log") function log_connect () - local m = 'Connection. user=' .. box.session.user() .. ' id=' .. box.session.id() + local m = "Connection. user=" .. box.session.user() .. " id=" .. box.session.id() log.info(m) end function log_disconnect () - local m = 'Disconnection. user=' .. box.session.user() .. ' id=' .. box.session.id() + local m = "Disconnection. user=" .. box.session.user() .. " id=" .. box.session.id() log.info(m) end function log_auth () - local m = 'Authentication attempt' + local m = "Authentication attempt" log.info(m) end function log_auth_ok (user_name) - local m = 'Authenticated user ' .. user_name + local m = "Authenticated user " .. user_name log.info(m) end @@ -138,33 +190,33 @@ box.session.on_auth(log_auth) box.session.on_auth(log_auth_ok) function return_null() - log.info('return_null called') - return require('msgpack').NULL + log.info("return_null called") + return require("msgpack").NULL end function return_tuple_with_null() - log.info('return_tuple_with_null called') - return { require('msgpack').NULL } + log.info("return_tuple_with_null called") + return { require("msgpack").NULL } end function return_tuple() - log.info('return_tuple called') + log.info("return_tuple called") return { 1, 2 } end function return_array() - log.info('return_array called') + log.info("return_array called") return {{ "abc", "def" }} end function return_scalar() - log.info('return_scalar called') + log.info("return_scalar called") return 1 end function return_nothing() - log.info('return_nothing called') + log.info("return_nothing called") end local truncate_space = function(name) @@ -180,16 +232,16 @@ local truncate_space = function(name) end function create_sql_test() - box.sql.execute('create table sql_test(id int primary key, name text)') + box.sql.execute("create table sql_test(id int primary key, name text)") box.sql.execute("insert into sql_test values (1, 'asdf'), (2, 'zxcv'), (3, 'qwer')") end function drop_sql_test() - box.sql.execute('drop table sql_test') + box.sql.execute("drop table sql_test") end function clear_data(spaceNames) - log.info('clearing data...') + log.info("clearing data...") for _, spaceName in ipairs(spaceNames) do truncate_space(spaceName) end diff --git a/tests/progaudi.tarantool.tests/progaudi.tarantool.tests.csproj b/tests/progaudi.tarantool.tests/progaudi.tarantool.tests.csproj index 37c0bc07..58dc3624 100644 --- a/tests/progaudi.tarantool.tests/progaudi.tarantool.tests.csproj +++ b/tests/progaudi.tarantool.tests/progaudi.tarantool.tests.csproj @@ -1,14 +1,21 @@  - progaudi.tarantool Class Library tests + progaudi.tarantool tests Exe - netcoreapp1.0;netcoreapp1.1;netcoreapp2.0 + netcoreapp2.0 + latest + + 4 true + progaudi.tarantool + ProGaudi.Tarantool.Client + progaudi.tarantool + Copyright © 2016-2018 + progaudi.tarantool.tests ProGaudi.Tarantool.Client.Tests progaudi.tarantool - Copyright © 2016-2017 tarantool;csharp;tests https://github.com/progaudi/progaudi.tarantool