From 4621da0408964dd51ddbc06260bf8e9a0786bde1 Mon Sep 17 00:00:00 2001 From: aensidhe Date: Thu, 24 May 2018 18:21:22 +0300 Subject: [PATCH 01/11] Add insert performance benchmark. 21711 ms for 1M inserts --- Scripts/run-insert-benchmark.sh | 16 ++ docker-compose.yml | 52 ++--- progaudi.tarantool.sln | 16 +- samples/insert-performance/Program.cs | 40 ++++ .../insert-performance.csproj | 11 ++ tarantool/testdata.lua | 178 +++++++++++------- 6 files changed, 223 insertions(+), 90 deletions(-) create mode 100644 Scripts/run-insert-benchmark.sh create mode 100644 samples/insert-performance/Program.cs create mode 100644 samples/insert-performance/insert-performance.csproj 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..ef22a4b1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,40 +1,40 @@ 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 - depends_on: - - tarantool_1_7 - - tarantool_1_8 + # admin: + # image: quay.io/basis-company/tarantool-admin + # ports: + # - 8888:80 + # depends_on: + # - tarantool_1_7 + # - tarantool_1_8 diff --git a/progaudi.tarantool.sln b/progaudi.tarantool.sln index 1916e0c7..4ca41d8f 100644 --- a/progaudi.tarantool.sln +++ b/progaudi.tarantool.sln @@ -12,7 +12,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution 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}") = "progaudi.tarantool.benchmark", "src\progaudi.tarantool.benchmark\progaudi.tarantool.benchmark.csproj", "{CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "insert-performance", "samples\insert-performance\insert-performance.csproj", "{9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -60,6 +62,18 @@ Global {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/insert-performance/Program.cs b/samples/insert-performance/Program.cs new file mode 100644 index 00000000..f9ef8b50 --- /dev/null +++ b/samples/insert-performance/Program.cs @@ -0,0 +1,40 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using ProGaudi.Tarantool.Client; +using ProGaudi.Tarantool.Client.Model; + +namespace Tarantool.Test +{ + class Program + { + static void Main(string[] args) + { + using (var box = Box.Connect("localhost:3301").GetAwaiter().GetResult()) + { + var schema = box.GetSchema(); + var space = schema["pivot"]; + var lst = new Task[1000]; + var sw = Stopwatch.StartNew(); + for (var i = 0; i < 1_000_000; i++) + { + lst[i % 1000] = space.Insert((i, (i, i), i)); + + if (i % 1000 == 999) + { + Task.WhenAll(lst); + } + + if (i % 10000 == 9999) + { + Console.Write("*"); + } + } + 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..f275bfe0 --- /dev/null +++ b/samples/insert-performance/insert-performance.csproj @@ -0,0 +1,11 @@ + + + + Exe + netcoreapp2.0 + + + + + + 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 From e67c7b462f6305a38cc5f068bdaa5cbc9fb5bde3 Mon Sep 17 00:00:00 2001 From: aensidhe Date: Thu, 24 May 2018 18:33:37 +0300 Subject: [PATCH 02/11] Drop legacy code --- global.json | 2 +- progaudi.tarantool.sln | 14 - .../IncrementBenchmark.cs | 2 +- .../progaudi.tarantool.benchmark.csproj | 12 +- src/progaudi.tarantool/Box.cs | 53 +- .../Converters/TarantoolTupleConverters.cs | 511 ------------------ src/progaudi.tarantool/IBox.cs | 25 - src/progaudi.tarantool/IIndex.cs | 8 +- src/progaudi.tarantool/Index.cs | 10 - .../Model/TarantoolTuple.cs | 498 ----------------- .../TarantoolConvertersRegistrator.cs | 10 - .../progaudi.tarantool.csproj | 15 +- .../progaudi.tarantool.tests.csproj | 13 +- 13 files changed, 41 insertions(+), 1132 deletions(-) delete mode 100644 src/progaudi.tarantool/Converters/TarantoolTupleConverters.cs delete mode 100644 src/progaudi.tarantool/Model/TarantoolTuple.cs diff --git a/global.json b/global.json index 4d61027a..73054ba6 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "2.0.0" + "version": "2.1.300-rc1-008673" } } \ No newline at end of file diff --git a/progaudi.tarantool.sln b/progaudi.tarantool.sln index 4ca41d8f..33ffca35 100644 --- a/progaudi.tarantool.sln +++ b/progaudi.tarantool.sln @@ -5,8 +5,6 @@ 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 @@ -38,18 +36,6 @@ 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 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/Box.cs b/src/progaudi.tarantool/Box.cs index 21b892bf..68fa7038 100644 --- a/src/progaudi.tarantool/Box.cs +++ b/src/progaudi.tarantool/Box.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; using ProGaudi.Tarantool.Client.Model.Requests; @@ -58,11 +59,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,42 +99,6 @@ 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) { var callRequest = new CallRequest(functionName, parameters); @@ -145,11 +111,6 @@ public async Task> Eval(string expr return await _logicalConnection.SendRequest, TResponse>(evalRequest).ConfigureAwait(false); } - public Task> Eval(string expression) - { - return Eval(expression, TarantoolTuple.Empty); - } - public Task ExecuteSql(string query, params SqlParameter[] parameters) { if (!_sqlReady) throw ExceptionHelper.SqlIsNotAvailable(Info.Version); 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/IBox.cs b/src/progaudi.tarantool/IBox.cs index 2ad3f2db..6d499d91 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 { @@ -23,29 +22,5 @@ public interface IBox : IDisposable Task ReloadSchema(); 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..998caf8e 100644 --- a/src/progaudi.tarantool/IIndex.cs +++ b/src/progaudi.tarantool/IIndex.cs @@ -35,13 +35,13 @@ public interface IIndex ///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(); - Task Min(TKey key); + //Task Min(TKey key); - Task Max(); + //Task Max(); - Task Max(TKey key); + //Task Max(TKey key); TTuple Random(int randomValue); diff --git a/src/progaudi.tarantool/Index.cs b/src/progaudi.tarantool/Index.cs index ee8e0337..7c83e89e 100644 --- a/src/progaudi.tarantool/Index.cs +++ b/src/progaudi.tarantool/Index.cs @@ -74,11 +74,6 @@ public async Task> Replace(TTuple tuple) return await LogicalConnection.SendRequest, TTuple>(replaceRequest).ConfigureAwait(false); } - public async Task Min() - { - return await Min(TarantoolTuple.Empty).ConfigureAwait(false); - } - public async Task Min(TKey key) { if (Type != IndexType.Tree) @@ -93,11 +88,6 @@ public async Task Min(TKey key) return minResponse.Data.SingleOrDefault(); } - public async Task Max() - { - return await Max(TarantoolTuple.Empty).ConfigureAwait(false); - } - public async Task Max(TKey key) { if (Type != IndexType.Tree) 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/TarantoolConvertersRegistrator.cs b/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs index d2710f97..9b67060a 100644 --- a/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs +++ b/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs @@ -37,7 +37,6 @@ public static void Register(MsgPackContext context) context.RegisterConverter(new IndexPartConverter()); context.RegisterConverter(new IndexCreationOptionsConverter()); context.RegisterConverter(new IndexConverter()); - context.RegisterConverter(new TupleConverter()); context.RegisterConverter(new BoxInfo.Converter()); context.RegisterGenericConverter(typeof(ResponsePacketConverter<>)); @@ -52,15 +51,6 @@ public static void Register(MsgPackContext context) 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(SystemTupleConverter<>)); context.RegisterGenericConverter(typeof(SystemTupleConverter<,>)); context.RegisterGenericConverter(typeof(SystemTupleConverter<,,>)); diff --git a/src/progaudi.tarantool/progaudi.tarantool.csproj b/src/progaudi.tarantool/progaudi.tarantool.csproj index 82dd3114..a9c42ddd 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 client Library - Tarantool low-level client Library + latest + + 4 true progaudi.tarantool 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 From d12e7aad227187ca062f18241892ce2f5d80021e Mon Sep 17 00:00:00 2001 From: aensidhe Date: Wed, 6 Jun 2018 21:36:04 +0300 Subject: [PATCH 03/11] Schema interface is done. --- .../Converters/IndexConverter.cs | 53 ----- .../Converters/SpaceConverter.cs | 58 ----- src/progaudi.tarantool/GetOptions.cs | 8 + src/progaudi.tarantool/ISchema.cs | 29 ++- src/progaudi.tarantool/ISpace.cs | 210 ++++++++++++++---- src/progaudi.tarantool/LogicalConnection.cs | 4 +- .../Model/Enums/StorageEngine.cs | 3 +- src/progaudi.tarantool/Model/SelectOptions.cs | 13 -- .../Model/SpaceCreationOptions.cs | 24 -- src/progaudi.tarantool/Model/SpaceOptions.cs | 11 + .../NetworkStreamPhysicalConnection.cs | 33 --- src/progaudi.tarantool/Schema.cs | 78 +++++-- .../TarantoolConvertersRegistrator.cs | 2 - .../progaudi.tarantool.csproj.DotSettings | 2 + 14 files changed, 272 insertions(+), 256 deletions(-) delete mode 100644 src/progaudi.tarantool/Converters/IndexConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/SpaceConverter.cs create mode 100644 src/progaudi.tarantool/GetOptions.cs delete mode 100644 src/progaudi.tarantool/Model/SelectOptions.cs delete mode 100644 src/progaudi.tarantool/Model/SpaceCreationOptions.cs create mode 100644 src/progaudi.tarantool/Model/SpaceOptions.cs create mode 100644 src/progaudi.tarantool/progaudi.tarantool.csproj.DotSettings 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/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/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/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..3e4b86eb 100644 --- a/src/progaudi.tarantool/ISpace.cs +++ b/src/progaudi.tarantool/ISpace.cs @@ -1,61 +1,183 @@ -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(ref 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(ref 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/LogicalConnection.cs b/src/progaudi.tarantool/LogicalConnection.cs index 4ff07e7b..5e41093b 100644 --- a/src/progaudi.tarantool/LogicalConnection.cs +++ b/src/progaudi.tarantool/LogicalConnection.cs @@ -99,10 +99,10 @@ 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, timeout); } public async Task> SendRequest(TRequest request, TimeSpan? timeout = null) diff --git a/src/progaudi.tarantool/Model/Enums/StorageEngine.cs b/src/progaudi.tarantool/Model/Enums/StorageEngine.cs index 8b4c922d..e10bc111 100644 --- a/src/progaudi.tarantool/Model/Enums/StorageEngine.cs +++ b/src/progaudi.tarantool/Model/Enums/StorageEngine.cs @@ -3,6 +3,7 @@ public enum StorageEngine { Memtx, - Sophia + Vinyl, + Sysview } } \ 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/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/SpaceOptions.cs b/src/progaudi.tarantool/Model/SpaceOptions.cs new file mode 100644 index 00000000..8d9b7ca1 --- /dev/null +++ b/src/progaudi.tarantool/Model/SpaceOptions.cs @@ -0,0 +1,11 @@ +using ProGaudi.MsgPack.Light; + +namespace ProGaudi.Tarantool.Client.Model +{ + [MsgPackMap] + public class SpaceOptions + { + [MsgPackMapElement("temporary")] + public bool Temporary { get; set; } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs b/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs index 4e0318f2..be8b04f7 100644 --- a/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs +++ b/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs @@ -7,10 +7,6 @@ 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 @@ -67,34 +63,6 @@ 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( @@ -104,7 +72,6 @@ private static Task ConnectAsync(Socket socket, string host, int port) port, socket); } -#endif public bool IsConnected => !_disposed && _stream != null; diff --git a/src/progaudi.tarantool/Schema.cs b/src/progaudi.tarantool/Schema.cs index a377caed..b0b9bd54 100644 --- a/src/progaudi.tarantool/Schema.cs +++ b/src/progaudi.tarantool/Schema.cs @@ -1,10 +1,9 @@ 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; namespace ProGaudi.Tarantool.Client { @@ -18,42 +17,81 @@ public class Schema : ISchema private readonly ILogicalConnection _logicalConnection; - private Dictionary _indexByName = new Dictionary(); + private Dictionary _spacesByName = new Dictionary(); + private Dictionary _spacesById = new Dictionary(); + private SpaceMeta[] _spaces; - private Dictionary _indexById = new Dictionary(); + private Dictionary _indicesBySpace = new Dictionary(); + private IndexMeta[] _indices; + + private readonly object _lockObject = new object(); public Schema(ILogicalConnection logicalConnection) { _logicalConnection = logicalConnection; } - public Task GetSpace(string name) => Task.FromResult(this[name]); + public DateTimeOffset LastReloadTime { get; private set; } - public Task GetSpace(uint id) => Task.FromResult(this[id]); + public bool TryGetSpace(string name, out ISpace space) + { + if (!_spacesByName.TryGetValue(name, out var index)) + { + space = default; + return false; + } - public ISpace this[string name] => _indexByName.TryGetValue(name, out var space) ? space : throw ExceptionHelper.InvalidSpaceName(name); + return GetSpaceByIndex(index, out space); + } - public ISpace this[uint id] => _indexById.TryGetValue(id, out var space) ? space : throw ExceptionHelper.InvalidSpaceId(id); + public bool TryGetSpace(uint id, out ISpace space) + { + if (!_spacesById.TryGetValue(id, out var index)) + { + space = default; + return false; + } - public DateTimeOffset LastReloadTime { get; private set; } + return GetSpaceByIndex(index, out space); + } public async Task Reload() { - var byName = new Dictionary(); - var byId = new Dictionary(); + 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); + for (var i = 0; i < spaces.Length; i++) { - byName[space.Name] = space; - byId[space.Id] = space; - space.LogicalConnection = _logicalConnection; - space.SetIndices(await Select(VIndex, Iterator.Eq, space.Id).ConfigureAwait(false)); + var space = spaces[i]; + byName[space.Name] = i; + byId[space.Id] = i; } - Interlocked.Exchange(ref _indexByName, byName); - Interlocked.Exchange(ref _indexById, byId); - LastReloadTime = DateTimeOffset.UtcNow; + 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) + { + _spaces = spaces; + _spacesByName = byName; + _spacesById = byId; + + _indices = indices; + _indicesBySpace = indicesBySpace; + + LastReloadTime = DateTimeOffset.UtcNow; + } + } + + private bool GetSpaceByIndex(int index, out ISpace space) + { + var meta = _spaces[index]; + space = new Space(this, meta, _indicesBySpace[meta.Id].Select(x => _indices[x])); + return true; } private async Task Select(uint spaceId, Iterator iterator = Iterator.All, uint id = 0u) diff --git a/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs b/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs index 9b67060a..dfee34e8 100644 --- a/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs +++ b/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs @@ -33,10 +33,8 @@ public static void Register(MsgPackContext context) context.RegisterConverter(new ErrorResponsePacketConverter()); context.RegisterConverter(new SpaceFieldConverter()); - context.RegisterConverter(new SpaceConverter()); context.RegisterConverter(new IndexPartConverter()); context.RegisterConverter(new IndexCreationOptionsConverter()); - context.RegisterConverter(new IndexConverter()); context.RegisterConverter(new BoxInfo.Converter()); context.RegisterGenericConverter(typeof(ResponsePacketConverter<>)); diff --git a/src/progaudi.tarantool/progaudi.tarantool.csproj.DotSettings b/src/progaudi.tarantool/progaudi.tarantool.csproj.DotSettings new file mode 100644 index 00000000..58ad6c88 --- /dev/null +++ b/src/progaudi.tarantool/progaudi.tarantool.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp71 \ No newline at end of file From 6475b6a83ae730cbb7439fe590ce6a96bca9b562 Mon Sep 17 00:00:00 2001 From: aensidhe Date: Wed, 6 Jun 2018 22:18:05 +0300 Subject: [PATCH 04/11] Meta is done. --- src/progaudi.tarantool/Box.cs | 8 +- src/progaudi.tarantool/IBox.cs | 7 +- src/progaudi.tarantool/IIndex.cs | 203 ++++++++++++++++---- src/progaudi.tarantool/ILuaCode.cs | 19 ++ src/progaudi.tarantool/Index.cs | 133 +++++-------- src/progaudi.tarantool/IndexMeta.cs | 30 +++ src/progaudi.tarantool/NameIdLazyWrapper.cs | 72 +++++++ src/progaudi.tarantool/Schema.cs | 44 ++--- src/progaudi.tarantool/Space.cs | 146 +++++++------- src/progaudi.tarantool/SpaceMeta.cs | 32 +++ 10 files changed, 455 insertions(+), 239 deletions(-) create mode 100644 src/progaudi.tarantool/ILuaCode.cs create mode 100644 src/progaudi.tarantool/IndexMeta.cs create mode 100644 src/progaudi.tarantool/NameIdLazyWrapper.cs create mode 100644 src/progaudi.tarantool/SpaceMeta.cs diff --git a/src/progaudi.tarantool/Box.cs b/src/progaudi.tarantool/Box.cs index 68fa7038..42f4db1e 100644 --- a/src/progaudi.tarantool/Box.cs +++ b/src/progaudi.tarantool/Box.cs @@ -99,16 +99,16 @@ public Task ReloadSchema() return Schema.Reload(); } - 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); + return _logicalConnection.SendRequest, TResponse>(evalRequest); } public Task ExecuteSql(string query, params SqlParameter[] parameters) diff --git a/src/progaudi.tarantool/IBox.cs b/src/progaudi.tarantool/IBox.cs index 6d499d91..f6dccc29 100644 --- a/src/progaudi.tarantool/IBox.cs +++ b/src/progaudi.tarantool/IBox.cs @@ -14,12 +14,11 @@ 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(); } diff --git a/src/progaudi.tarantool/IIndex.cs b/src/progaudi.tarantool/IIndex.cs index 998caf8e..249c2410 100644 --- a/src/progaudi.tarantool/IIndex.cs +++ b/src/progaudi.tarantool/IIndex.cs @@ -2,15 +2,12 @@ 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 +20,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/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/Index.cs b/src/progaudi.tarantool/Index.cs index 7c83e89e..850ea57f 100644 --- a/src/progaudi.tarantool/Index.cs +++ b/src/progaudi.tarantool/Index.cs @@ -1,161 +1,122 @@ 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(TKey key) + public Task Replace(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(TKey key) + public Task Put(T tuple) { - 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 Update(TKey key, UpdateOperation[] updateOperations) { throw new NotImplementedException(); } - public uint Count(TKey key, Iterator it = Iterator.Eq) + public Task Upsert(T tuple, UpdateOperation[] updateOperations) { throw new NotImplementedException(); } - public async Task> Update(TKey key, UpdateOperation[] updateOperations) + public Task Upsert(TInsertable tuple, UpdateOperation[] updateOperations) { - var updateRequest = new UpdateRequest( - SpaceId, - Id, - key, - updateOperations); + throw new NotImplementedException(); + } - return await LogicalConnection.SendRequest, TTuple>(updateRequest).ConfigureAwait(false); + public Task Delete(TKey key) + { + throw new NotImplementedException(); } - public async Task Upsert(TKey key, UpdateOperation[] updateOperations) + public Task Min() { - var updateRequest = new UpsertRequest( - SpaceId, - key, - updateOperations); + throw new NotImplementedException(); + } - await LogicalConnection.SendRequestWithEmptyResponse(updateRequest).ConfigureAwait(false); + public Task Min(TKey key) + { + 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..942b527c --- /dev/null +++ b/src/progaudi.tarantool/IndexMeta.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Model; +using ProGaudi.Tarantool.Client.Model.Enums; + +namespace ProGaudi.Tarantool.Client +{ + // TODO: fix serializer to support struct. + [MsgPackArray] + public sealed class IndexMeta + { + [MsgPackArrayElement(1)] + public uint Id { get; set; } + + [MsgPackArrayElement(0)] + public uint SpaceId { get; set; } + + [MsgPackArrayElement(2)] + public string Name { get; set; } + + [MsgPackArrayElement(4)] + public IndexCreationOptions Options { get; set; } + + [MsgPackArrayElement(3)] + public IndexType Type { get; set; } + + [MsgPackArrayElement(5)] + public IReadOnlyList Parts { get; set; } + } +} \ 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/Schema.cs b/src/progaudi.tarantool/Schema.cs index b0b9bd54..00cbe251 100644 --- a/src/progaudi.tarantool/Schema.cs +++ b/src/progaudi.tarantool/Schema.cs @@ -16,10 +16,7 @@ public class Schema : ISchema internal const uint PrimaryIndexId = 0; private readonly ILogicalConnection _logicalConnection; - - private Dictionary _spacesByName = new Dictionary(); - private Dictionary _spacesById = new Dictionary(); - private SpaceMeta[] _spaces; + private NameIdLazyWrapper _spaces; private Dictionary _indicesBySpace = new Dictionary(); private IndexMeta[] _indices; @@ -35,39 +32,31 @@ public Schema(ILogicalConnection logicalConnection) public bool TryGetSpace(string name, out ISpace space) { - if (!_spacesByName.TryGetValue(name, out var index)) + SpaceMeta meta; + lock (_lockObject) { - space = default; - return false; + meta = _spaces[name]; } - return GetSpaceByIndex(index, out space); + space = new Space(this, meta, _indicesBySpace[meta.Id].Select(x => _indices[x])); + return true; } public bool TryGetSpace(uint id, out ISpace space) { - if (!_spacesById.TryGetValue(id, out var index)) + SpaceMeta meta; + lock (_lockObject) { - space = default; - return false; + meta = _spaces[id]; } - return GetSpaceByIndex(index, out space); + 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); - for (var i = 0; i < spaces.Length; i++) - { - var space = spaces[i]; - byName[space.Name] = i; - byId[space.Id] = i; - } - var indices = await Select(VIndex).ConfigureAwait(false); var indicesBySpace = indices .Select((x, i) => new {x, i}) @@ -76,9 +65,7 @@ public async Task Reload() lock (_lockObject) { - _spaces = spaces; - _spacesByName = byName; - _spacesById = byId; + _spaces = new NameIdLazyWrapper(spaces, x => x.Id, x => x.Name); _indices = indices; _indicesBySpace = indicesBySpace; @@ -87,13 +74,6 @@ public async Task Reload() } } - private bool GetSpaceByIndex(int index, out ISpace space) - { - var meta = _spaces[index]; - space = new Space(this, meta, _indicesBySpace[meta.Id].Select(x => _indices[x])); - return true; - } - 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)); diff --git a/src/progaudi.tarantool/Space.cs b/src/progaudi.tarantool/Space.cs index 848122a6..bd312a46 100644 --- a/src/progaudi.tarantool/Space.cs +++ b/src/progaudi.tarantool/Space.cs @@ -1,147 +1,149 @@ 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 => _meta.FieldCount; - public uint FieldCount { get; } + public SpaceOptions Options => _meta.Options; - public string Name { get; } + public SpaceField[] Fields => _meta.Fields; - public StorageEngine Engine { get; } + public IIndex this[string name] => TryGetIndex(name, out var x) ? x : throw new IndexOutOfRangeException(); - public IReadOnlyCollection Indices => _indexByName.Values; + public IIndex this[uint id] => TryGetIndex(id, out var x) ? x : throw new IndexOutOfRangeException(); - internal void SetIndices(IReadOnlyCollection value) + 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 == default) { - 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 == default) + { + 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 Task Insert(ref T tuple) + { + throw new NotImplementedException(); + } - public async Task> Insert(TTuple tuple) + public Task Insert(ref 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..55f6c5bc --- /dev/null +++ b/src/progaudi.tarantool/SpaceMeta.cs @@ -0,0 +1,32 @@ +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Model; +using ProGaudi.Tarantool.Client.Model.Enums; + +namespace ProGaudi.Tarantool.Client +{ + // TODO: fix serializer to support struct. + [MsgPackArray] + public class SpaceMeta + { + [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; } + } +} \ No newline at end of file From 0f69e7bee650048ac03b1e14901d6526fde647a2 Mon Sep 17 00:00:00 2001 From: aensidhe Date: Tue, 12 Jun 2018 19:28:28 +0300 Subject: [PATCH 05/11] New serializer. 31712 ms. --- global.json | 2 +- progaudi.tarantool.sln | 14 + samples/insert-performance/Program.cs | 48 +- .../insert-performance.csproj | 8 + src/progaudi.tarantool/Box.cs | 51 +- .../AuthenticationPacketConverter.cs | 50 -- .../Converters/CallPacketConverter.cs | 38 -- .../Converters/DeletePacketConverter.cs | 43 -- .../Converters/EmptyResponseConverter.cs | 53 -- .../Converters/EnumConverter.cs | 102 ---- .../ErrorResponsePacketConverter.cs | 48 -- .../Converters/EvalPacketConverter.cs | 40 -- .../Converters/ExecuteSqlRequest.cs | 57 --- .../Converters/FromStringEnumConverter.cs | 42 -- .../IndexCreationOptionsConverter.cs | 45 -- .../Converters/IndexPartConverter.cs | 127 ----- .../InsertReplacePacketConverter.cs | 40 -- .../Converters/PacketSizeConverter.cs | 106 ---- .../Converters/PingPacketConverter.cs | 24 - .../Converters/RequestHeaderConverter.cs | 48 -- .../Converters/RequestIdConverter.cs | 134 ----- .../Converters/ResponseHeaderConverter.cs | 79 --- .../Converters/ResponsePacketConverter.cs | 175 ------- .../Converters/SelectPacketConverter.cs | 52 -- .../Converters/SpaceFieldConverter.cs | 53 -- .../StringSliceOperationConverter.cs | 37 -- .../Converters/SystemTupleConverters.cs | 471 ------------------ .../Converters/UpdateOperationConverter.cs | 46 -- .../Converters/UpdatePacketConverter.cs | 54 -- .../Converters/UpsertPacketConverter.cs | 51 -- .../Converters/ValueTupleConverters.cs | 401 --------------- src/progaudi.tarantool/IIndex.cs | 2 - src/progaudi.tarantool/ILogicalConnection.cs | 4 +- src/progaudi.tarantool/IRequestWriter.cs | 3 +- src/progaudi.tarantool/IResponseReader.cs | 2 +- src/progaudi.tarantool/ISpace.cs | 6 +- src/progaudi.tarantool/Index.cs | 2 - src/progaudi.tarantool/IndexMeta.cs | 32 +- src/progaudi.tarantool/LogicalConnection.cs | 59 +-- .../LogicalConnectionManager.cs | 2 - .../{Requests => }/AuthenticationRequest.cs | 36 +- src/progaudi.tarantool/Model/BoxInfo.cs | 129 +++-- src/progaudi.tarantool/Model/CallRequest.cs | 46 ++ src/progaudi.tarantool/Model/ClientOptions.cs | 17 +- .../{Enums/CommandCode.cs => CommandCodes.cs} | 4 +- .../Model/ConnectionOptions.cs | 2 +- src/progaudi.tarantool/Model/DataResponse.cs | 133 +++++ src/progaudi.tarantool/Model/DeleteRequest.cs | 55 ++ src/progaudi.tarantool/Model/EmptyResponse.cs | 7 + .../Model/EnumAsStringAttribute.cs | 15 + .../Model/EnumAsStringFormatter.cs | 71 +++ src/progaudi.tarantool/Model/EnumResolver.cs | 43 ++ .../Model/Enums/FieldType.cs | 12 - .../Model/Enums/IndexPartType.cs | 8 - src/progaudi.tarantool/Model/Enums/Key.cs | 42 -- .../Model/{Responses => }/ErrorResponse.cs | 2 +- .../Model/{Requests => }/EvalRequest.cs | 6 +- .../Model/ExecuteSqlRequest.cs | 21 + src/progaudi.tarantool/Model/FieldMetadata.cs | 92 ++++ src/progaudi.tarantool/Model/FieldType.cs | 14 + .../{Responses => }/GreetingsResponse.cs | 2 +- src/progaudi.tarantool/Model/HeaderBase.cs | 15 + .../Model/Headers/HeaderBase.cs | 17 - .../Model/Headers/RequestHeader.cs | 12 - .../Model/Headers/ResponseHeader.cs | 14 - src/progaudi.tarantool/Model/IRequest.cs | 7 + .../Model/IndexCreationOptions.cs | 6 +- src/progaudi.tarantool/Model/IndexPart.cs | 98 +++- .../Model/{Enums => }/IndexType.cs | 3 +- .../Model/InsertReplaceRequest.cs | 43 ++ src/progaudi.tarantool/Model/InsertRequest.cs | 10 + .../Model/{Enums => }/Iterator.cs | 2 +- src/progaudi.tarantool/Model/Keys.cs | 42 ++ .../Model/PackerResolver.cs | 70 +++ src/progaudi.tarantool/Model/PingRequest.cs | 7 + .../Model/ReplaceRequest.cs | 10 + src/progaudi.tarantool/Model/RequestHeader.cs | 36 ++ .../Model/Requests/CallRequest.cs | 22 - .../Model/Requests/DeleteRequest.cs | 22 - .../Model/Requests/ExecuteSqlRequest.cs | 22 - .../Model/Requests/IRequest.cs | 9 - .../Model/Requests/InsertReplaceRequest.cs | 20 - .../Model/Requests/InsertRequest.cs | 12 - .../Model/Requests/PingRequest.cs | 9 - .../Model/Requests/ReplaceRequest.cs | 12 - .../Model/Requests/SelectRequest.cs | 31 -- .../Model/ResponseHeader.cs | 76 +++ .../Model/Responses/DataResponse.cs | 28 -- .../Model/Responses/EmptyResponse.cs | 7 - .../Model/Responses/FieldMetadata.cs | 47 -- .../Model/Responses/SqlInfo.cs | 12 - src/progaudi.tarantool/Model/SelectRequest.cs | 64 +++ src/progaudi.tarantool/Model/SpaceField.cs | 10 +- src/progaudi.tarantool/Model/SpaceOptions.cs | 11 +- src/progaudi.tarantool/Model/SqlInfo.cs | 58 +++ src/progaudi.tarantool/Model/SqlParameter.cs | 51 +- .../Model/{Enums => }/StorageEngine.cs | 3 +- .../StringOperations.cs | 2 +- .../{UpdateOperations => }/UpdateOperation.cs | 14 +- .../UpdateOperationType.cs | 2 +- .../Model/{Requests => }/UpdateRequest.cs | 7 +- .../Model/{Requests => }/UpsertRequest.cs | 7 +- src/progaudi.tarantool/RequestLength.cs | 33 ++ src/progaudi.tarantool/RequestWriter.cs | 32 +- src/progaudi.tarantool/ResponseReader.cs | 33 +- src/progaudi.tarantool/Schema.cs | 10 +- src/progaudi.tarantool/Space.cs | 21 +- src/progaudi.tarantool/SpaceMeta.cs | 27 +- .../TarantoolConvertersRegistrator.cs | 69 --- src/progaudi.tarantool/TaskHelpers.cs | 7 +- .../Utils/ExceptionHelper.cs | 14 +- src/progaudi.tarantool/Utils/StringEnum.cs | 52 -- .../progaudi.tarantool.csproj | 13 +- .../progaudi.tarantool.csproj.DotSettings | 2 +- src/tests/Program.cs | 77 +++ src/tests/tests.csproj | 21 + src/tests/tests.csproj.DotSettings | 2 + 117 files changed, 1530 insertions(+), 3241 deletions(-) delete mode 100644 src/progaudi.tarantool/Converters/AuthenticationPacketConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/CallPacketConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/DeletePacketConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/EmptyResponseConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/EnumConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/ErrorResponsePacketConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/EvalPacketConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/ExecuteSqlRequest.cs delete mode 100644 src/progaudi.tarantool/Converters/FromStringEnumConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/IndexCreationOptionsConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/IndexPartConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/InsertReplacePacketConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/PacketSizeConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/PingPacketConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/RequestHeaderConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/RequestIdConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/ResponseHeaderConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/ResponsePacketConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/SelectPacketConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/SpaceFieldConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/StringSliceOperationConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/SystemTupleConverters.cs delete mode 100644 src/progaudi.tarantool/Converters/UpdateOperationConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/UpdatePacketConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/UpsertPacketConverter.cs delete mode 100644 src/progaudi.tarantool/Converters/ValueTupleConverters.cs rename src/progaudi.tarantool/Model/{Requests => }/AuthenticationRequest.cs (52%) create mode 100644 src/progaudi.tarantool/Model/CallRequest.cs rename src/progaudi.tarantool/Model/{Enums/CommandCode.cs => CommandCodes.cs} (85%) create mode 100644 src/progaudi.tarantool/Model/DataResponse.cs create mode 100644 src/progaudi.tarantool/Model/DeleteRequest.cs create mode 100644 src/progaudi.tarantool/Model/EmptyResponse.cs create mode 100644 src/progaudi.tarantool/Model/EnumAsStringAttribute.cs create mode 100644 src/progaudi.tarantool/Model/EnumAsStringFormatter.cs create mode 100644 src/progaudi.tarantool/Model/EnumResolver.cs delete mode 100644 src/progaudi.tarantool/Model/Enums/FieldType.cs delete mode 100644 src/progaudi.tarantool/Model/Enums/IndexPartType.cs delete mode 100644 src/progaudi.tarantool/Model/Enums/Key.cs rename src/progaudi.tarantool/Model/{Responses => }/ErrorResponse.cs (79%) rename src/progaudi.tarantool/Model/{Requests => }/EvalRequest.cs (64%) create mode 100644 src/progaudi.tarantool/Model/ExecuteSqlRequest.cs create mode 100644 src/progaudi.tarantool/Model/FieldMetadata.cs create mode 100644 src/progaudi.tarantool/Model/FieldType.cs rename src/progaudi.tarantool/Model/{Responses => }/GreetingsResponse.cs (91%) create mode 100644 src/progaudi.tarantool/Model/HeaderBase.cs delete mode 100644 src/progaudi.tarantool/Model/Headers/HeaderBase.cs delete mode 100644 src/progaudi.tarantool/Model/Headers/RequestHeader.cs delete mode 100644 src/progaudi.tarantool/Model/Headers/ResponseHeader.cs create mode 100644 src/progaudi.tarantool/Model/IRequest.cs rename src/progaudi.tarantool/Model/{Enums => }/IndexType.cs (58%) create mode 100644 src/progaudi.tarantool/Model/InsertReplaceRequest.cs create mode 100644 src/progaudi.tarantool/Model/InsertRequest.cs rename src/progaudi.tarantool/Model/{Enums => }/Iterator.cs (84%) create mode 100644 src/progaudi.tarantool/Model/Keys.cs create mode 100644 src/progaudi.tarantool/Model/PackerResolver.cs create mode 100644 src/progaudi.tarantool/Model/PingRequest.cs create mode 100644 src/progaudi.tarantool/Model/ReplaceRequest.cs create mode 100644 src/progaudi.tarantool/Model/RequestHeader.cs delete mode 100644 src/progaudi.tarantool/Model/Requests/CallRequest.cs delete mode 100644 src/progaudi.tarantool/Model/Requests/DeleteRequest.cs delete mode 100644 src/progaudi.tarantool/Model/Requests/ExecuteSqlRequest.cs delete mode 100644 src/progaudi.tarantool/Model/Requests/IRequest.cs delete mode 100644 src/progaudi.tarantool/Model/Requests/InsertReplaceRequest.cs delete mode 100644 src/progaudi.tarantool/Model/Requests/InsertRequest.cs delete mode 100644 src/progaudi.tarantool/Model/Requests/PingRequest.cs delete mode 100644 src/progaudi.tarantool/Model/Requests/ReplaceRequest.cs delete mode 100644 src/progaudi.tarantool/Model/Requests/SelectRequest.cs create mode 100644 src/progaudi.tarantool/Model/ResponseHeader.cs delete mode 100644 src/progaudi.tarantool/Model/Responses/DataResponse.cs delete mode 100644 src/progaudi.tarantool/Model/Responses/EmptyResponse.cs delete mode 100644 src/progaudi.tarantool/Model/Responses/FieldMetadata.cs delete mode 100644 src/progaudi.tarantool/Model/Responses/SqlInfo.cs create mode 100644 src/progaudi.tarantool/Model/SelectRequest.cs create mode 100644 src/progaudi.tarantool/Model/SqlInfo.cs rename src/progaudi.tarantool/Model/{Enums => }/StorageEngine.cs (56%) rename src/progaudi.tarantool/Model/{UpdateOperations => }/StringOperations.cs (87%) rename src/progaudi.tarantool/Model/{UpdateOperations => }/UpdateOperation.cs (96%) rename src/progaudi.tarantool/Model/{UpdateOperations => }/UpdateOperationType.cs (91%) rename src/progaudi.tarantool/Model/{Requests => }/UpdateRequest.cs (69%) rename src/progaudi.tarantool/Model/{Requests => }/UpsertRequest.cs (66%) create mode 100644 src/progaudi.tarantool/RequestLength.cs delete mode 100644 src/progaudi.tarantool/TarantoolConvertersRegistrator.cs delete mode 100644 src/progaudi.tarantool/Utils/StringEnum.cs create mode 100644 src/tests/Program.cs create mode 100644 src/tests/tests.csproj create mode 100644 src/tests/tests.csproj.DotSettings diff --git a/global.json b/global.json index 73054ba6..386035de 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "2.1.300-rc1-008673" + "version": "2.1.300" } } \ No newline at end of file diff --git a/progaudi.tarantool.sln b/progaudi.tarantool.sln index 33ffca35..bb674d63 100644 --- a/progaudi.tarantool.sln +++ b/progaudi.tarantool.sln @@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "progaudi.tarantool.benchmar EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "insert-performance", "samples\insert-performance\insert-performance.csproj", "{9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tests", "src\tests\tests.csproj", "{4B1516E4-71FF-479B-A821-203FF2C97582}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -60,6 +62,18 @@ Global {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 + {4B1516E4-71FF-479B-A821-203FF2C97582}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B1516E4-71FF-479B-A821-203FF2C97582}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B1516E4-71FF-479B-A821-203FF2C97582}.Debug|x64.ActiveCfg = Debug|Any CPU + {4B1516E4-71FF-479B-A821-203FF2C97582}.Debug|x64.Build.0 = Debug|Any CPU + {4B1516E4-71FF-479B-A821-203FF2C97582}.Debug|x86.ActiveCfg = Debug|Any CPU + {4B1516E4-71FF-479B-A821-203FF2C97582}.Debug|x86.Build.0 = Debug|Any CPU + {4B1516E4-71FF-479B-A821-203FF2C97582}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B1516E4-71FF-479B-A821-203FF2C97582}.Release|Any CPU.Build.0 = Release|Any CPU + {4B1516E4-71FF-479B-A821-203FF2C97582}.Release|x64.ActiveCfg = Release|Any CPU + {4B1516E4-71FF-479B-A821-203FF2C97582}.Release|x64.Build.0 = Release|Any CPU + {4B1516E4-71FF-479B-A821-203FF2C97582}.Release|x86.ActiveCfg = Release|Any CPU + {4B1516E4-71FF-479B-A821-203FF2C97582}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/insert-performance/Program.cs b/samples/insert-performance/Program.cs index f9ef8b50..9775411a 100644 --- a/samples/insert-performance/Program.cs +++ b/samples/insert-performance/Program.cs @@ -8,32 +8,42 @@ namespace Tarantool.Test { class Program { - static void Main(string[] args) + static void Main() { - using (var box = Box.Connect("localhost:3301").GetAwaiter().GetResult()) + var log = new TextWriterLog(Console.Out); + var options = new ClientOptions("localhost:3301"); + try { - var schema = box.GetSchema(); - var space = schema["pivot"]; - var lst = new Task[1000]; - var sw = Stopwatch.StartNew(); - for (var i = 0; i < 1_000_000; i++) + using (var box = new Box(options)) { - lst[i % 1000] = space.Insert((i, (i, i), i)); - - if (i % 1000 == 999) + box.Connect().GetAwaiter().GetResult(); + var schema = box.GetSchema(); + schema.TryGetSpace<(int, (int, int), int)>("pivot", out var space); + var lst = new Task[1000]; + var sw = Stopwatch.StartNew(); + for (var i = 0; i < 1_000_000; i++) { - Task.WhenAll(lst); - } + lst[i % 1000] = space.Insert((i, (i, i), i)); + + if (i % 1000 == 999) + { + Task.WaitAll(lst); + } - if (i % 10000 == 9999) - { - Console.Write("*"); + if (i % 10000 == 9999) + { + Console.Write("*"); + } } - } - sw.Stop(); + sw.Stop(); - Console.WriteLine(); - Console.WriteLine(sw.ElapsedMilliseconds); + Console.WriteLine(); + Console.WriteLine(sw.ElapsedMilliseconds); + } + } + catch (Exception e) + { + Console.WriteLine(e); } } } diff --git a/samples/insert-performance/insert-performance.csproj b/samples/insert-performance/insert-performance.csproj index f275bfe0..1cc692ee 100644 --- a/samples/insert-performance/insert-performance.csproj +++ b/samples/insert-performance/insert-performance.csproj @@ -5,6 +5,14 @@ netcoreapp2.0 + + 7.2 + + + + 7.2 + + diff --git a/src/progaudi.tarantool/Box.cs b/src/progaudi.tarantool/Box.cs index 42f4db1e..eb989ce5 100644 --- a/src/progaudi.tarantool/Box.cs +++ b/src/progaudi.tarantool/Box.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using MessagePack; +using MessagePack.Resolvers; 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 @@ -21,9 +21,8 @@ public class Box : IBox 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); } @@ -34,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; @@ -111,18 +120,34 @@ public Task> Eval(string expression 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)); + static Box() + { + CompositeResolver.RegisterAndSetAsDefault( + BuiltinResolver.Instance, + AttributeFormatterResolver.Instance, + PackerResolver.Instance, + EnumResolver.Instance, + + DynamicEnumResolver.Instance, + DynamicGenericResolver.Instance, + DynamicObjectResolver.Instance, + + PrimitiveObjectResolver.Instance + ); } } } \ 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/EnumConverter.cs b/src/progaudi.tarantool/Converters/EnumConverter.cs deleted file mode 100644 index b03d4219..00000000 --- a/src/progaudi.tarantool/Converters/EnumConverter.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Reflection; - -using ProGaudi.MsgPack.Light; - -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters -{ - internal class EnumConverter : IMsgPackConverter - where T : struct, IConvertible - { - private IMsgPackConverter _sbyteConverter; - private IMsgPackConverter _byteConverter; - private IMsgPackConverter _shortConverter; - private IMsgPackConverter _ushortConverter; - private IMsgPackConverter _intConverter; - private IMsgPackConverter _uintConverter; - private IMsgPackConverter _longConverter; - private IMsgPackConverter _ulongConverter; - - private readonly Dictionary> _writeMethodsCache = new Dictionary>(); - private readonly Dictionary> _readMethodsCache = new Dictionary>(); - - public void Initialize(MsgPackContext context) - { - _sbyteConverter = context.GetConverter(); - _byteConverter = context.GetConverter(); - _shortConverter = context.GetConverter(); - _ushortConverter = context.GetConverter(); - _intConverter = context.GetConverter(); - _uintConverter = context.GetConverter(); - _longConverter = context.GetConverter(); - _ulongConverter = context.GetConverter(); - - InitializeWriteMethodsChache(); - InitializeReadMethodsCache(); - } - - private void InitializeReadMethodsCache() - { - _readMethodsCache.Add(typeof(sbyte), reader => (T)Enum.ToObject(typeof(T), _sbyteConverter.Read(reader))); - _readMethodsCache.Add(typeof(byte), reader => (T)Enum.ToObject(typeof(T), _byteConverter.Read(reader))); - _readMethodsCache.Add(typeof(short), reader => (T)Enum.ToObject(typeof(T), _shortConverter.Read(reader))); - _readMethodsCache.Add(typeof(ushort), reader => (T)Enum.ToObject(typeof(T), _ushortConverter.Read(reader))); - _readMethodsCache.Add(typeof(int), reader => (T)Enum.ToObject(typeof(T), _intConverter.Read(reader))); - _readMethodsCache.Add(typeof(uint), reader => (T)Enum.ToObject(typeof(T), _uintConverter.Read(reader))); - _readMethodsCache.Add(typeof(long), reader => (T)Enum.ToObject(typeof(T), _longConverter.Read(reader))); - _readMethodsCache.Add(typeof(ulong), reader => (T)Enum.ToObject(typeof(T), _ulongConverter.Read(reader))); - } - - private void InitializeWriteMethodsChache() - { - _writeMethodsCache.Add(typeof(sbyte), (value, writer) => _sbyteConverter.Write(value.ToSByte(CultureInfo.InvariantCulture), writer)); - _writeMethodsCache.Add(typeof(byte), (value, writer) => _byteConverter.Write(value.ToByte(CultureInfo.InvariantCulture), writer)); - _writeMethodsCache.Add(typeof(short), (value, writer) => _shortConverter.Write(value.ToInt16(CultureInfo.InvariantCulture), writer)); - _writeMethodsCache.Add(typeof(ushort), (value, writer) => _ushortConverter.Write(value.ToUInt16(CultureInfo.InvariantCulture), writer)); - _writeMethodsCache.Add(typeof(int), (value, writer) => _intConverter.Write(value.ToInt32(CultureInfo.InvariantCulture), writer)); - _writeMethodsCache.Add(typeof(uint), (value, writer) => _uintConverter.Write(value.ToUInt32(CultureInfo.InvariantCulture), writer)); - _writeMethodsCache.Add(typeof(long), (value, writer) => _longConverter.Write(value.ToInt64(CultureInfo.InvariantCulture), writer)); - _writeMethodsCache.Add(typeof(ulong), (value, writer) => _ulongConverter.Write(value.ToUInt64(CultureInfo.InvariantCulture), writer)); - } - - static EnumConverter() - { - var enumTypeInfo = typeof(T).GetTypeInfo(); - if (!enumTypeInfo.IsEnum) - { - throw ExceptionHelper.EnumExpected(enumTypeInfo); - } - } - - public void Write(T value, IMsgPackWriter writer) - { - var enumUnderlyingType = Enum.GetUnderlyingType(typeof(T)); - - Action writeMethod; - if (_writeMethodsCache.TryGetValue(enumUnderlyingType, out writeMethod)) - { - writeMethod(value, writer); - } - else - { - throw ExceptionHelper.UnexpectedEnumUnderlyingType(enumUnderlyingType); - } - } - - public T Read(IMsgPackReader reader) - { - var enumUnderlyingType = Enum.GetUnderlyingType(typeof(T)); - Func readMethod; - if (_readMethodsCache.TryGetValue(enumUnderlyingType, out readMethod)) - { - return readMethod(reader); - } - - throw ExceptionHelper.UnexpectedEnumUnderlyingType(enumUnderlyingType); - } - } -} \ 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/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/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/SystemTupleConverters.cs b/src/progaudi.tarantool/Converters/SystemTupleConverters.cs deleted file mode 100644 index dd115f92..00000000 --- a/src/progaudi.tarantool/Converters/SystemTupleConverters.cs +++ /dev/null @@ -1,471 +0,0 @@ - - -using System; -using ProGaudi.MsgPack.Light; -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters{ - - public class SystemTupleConverter :IMsgPackConverter> - { - private IMsgPackConverter _nullConverter; - - private IMsgPackConverter _t1Converter; - - public void Initialize(MsgPackContext context) - { - _nullConverter = context.NullConverter; - - _t1Converter = context.GetConverter(); - } - - public void Write(Tuple value, IMsgPackWriter writer) - { - if (value == null) - { - _nullConverter.Write(null, writer); - return; - } - - writer.WriteArrayHeader(1); - - _t1Converter.Write(value.Item1, writer); - } - - public Tuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - const uint expected = 1; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - - return Tuple.Create( - item1 - ); - } - } - - public class SystemTupleConverter :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(Tuple 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 Tuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - const uint expected = 2; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - var item2 = _t2Converter.Read(reader); - - return Tuple.Create( - item1, - item2 - ); - } - } - - public class SystemTupleConverter :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(Tuple 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 Tuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - const uint expected = 3; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - var item2 = _t2Converter.Read(reader); - var item3 = _t3Converter.Read(reader); - - return Tuple.Create( - item1, - item2, - item3 - ); - } - } - - public class SystemTupleConverter :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(Tuple 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 Tuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - const uint expected = 4; - 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 Tuple.Create( - item1, - item2, - item3, - item4 - ); - } - } - - public class SystemTupleConverter :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(Tuple 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 Tuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - const uint expected = 5; - 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 Tuple.Create( - item1, - item2, - item3, - item4, - item5 - ); - } - } - - public class SystemTupleConverter :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(Tuple 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 Tuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - const uint expected = 6; - 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 Tuple.Create( - item1, - item2, - item3, - item4, - item5, - item6 - ); - } - } - - public class SystemTupleConverter :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(Tuple 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 Tuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return null; - } - - const uint expected = 7; - 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 Tuple.Create( - item1, - item2, - item3, - item4, - item5, - item6, - item7 - ); - } - } - -} \ 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/Converters/ValueTupleConverters.cs b/src/progaudi.tarantool/Converters/ValueTupleConverters.cs deleted file mode 100644 index 29c96e75..00000000 --- a/src/progaudi.tarantool/Converters/ValueTupleConverters.cs +++ /dev/null @@ -1,401 +0,0 @@ - - -using System; -using ProGaudi.MsgPack.Light; -using ProGaudi.Tarantool.Client.Utils; - -namespace ProGaudi.Tarantool.Client.Converters{ - - public class ValueTupleConverter :IMsgPackConverter> - { - private IMsgPackConverter _t1Converter; - - public void Initialize(MsgPackContext context) - { - _t1Converter = context.GetConverter(); - } - - public void Write(ValueTuple value, IMsgPackWriter writer) - { - writer.WriteArrayHeader(1); - - _t1Converter.Write(value.Item1, writer); - } - - public ValueTuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return default(ValueTuple); - } - - const uint expected = 1; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - - return ValueTuple.Create( - item1 - ); - } - } - - public class ValueTupleConverter :IMsgPackConverter> - { - private IMsgPackConverter _t1Converter; - - private IMsgPackConverter _t2Converter; - - public void Initialize(MsgPackContext context) - { - _t1Converter = context.GetConverter(); - _t2Converter = context.GetConverter(); - } - - public void Write(ValueTuple value, IMsgPackWriter writer) - { - writer.WriteArrayHeader(2); - - _t1Converter.Write(value.Item1, writer); - _t2Converter.Write(value.Item2, writer); - } - - public ValueTuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return default(ValueTuple); - } - - const uint expected = 2; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - var item2 = _t2Converter.Read(reader); - - return ValueTuple.Create( - item1, - item2 - ); - } - } - - public class ValueTupleConverter :IMsgPackConverter> - { - private IMsgPackConverter _t1Converter; - - private IMsgPackConverter _t2Converter; - - private IMsgPackConverter _t3Converter; - - public void Initialize(MsgPackContext context) - { - _t1Converter = context.GetConverter(); - _t2Converter = context.GetConverter(); - _t3Converter = context.GetConverter(); - } - - public void Write(ValueTuple value, IMsgPackWriter writer) - { - writer.WriteArrayHeader(3); - - _t1Converter.Write(value.Item1, writer); - _t2Converter.Write(value.Item2, writer); - _t3Converter.Write(value.Item3, writer); - } - - public ValueTuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return default(ValueTuple); - } - - const uint expected = 3; - if (actual != expected) - { - throw ExceptionHelper.InvalidArrayLength(expected, actual); - } - - var item1 = _t1Converter.Read(reader); - var item2 = _t2Converter.Read(reader); - var item3 = _t3Converter.Read(reader); - - return ValueTuple.Create( - item1, - item2, - item3 - ); - } - } - - public class ValueTupleConverter :IMsgPackConverter> - { - private IMsgPackConverter _t1Converter; - - private IMsgPackConverter _t2Converter; - - private IMsgPackConverter _t3Converter; - - private IMsgPackConverter _t4Converter; - - public void Initialize(MsgPackContext context) - { - _t1Converter = context.GetConverter(); - _t2Converter = context.GetConverter(); - _t3Converter = context.GetConverter(); - _t4Converter = context.GetConverter(); - } - - public void Write(ValueTuple value, IMsgPackWriter writer) - { - writer.WriteArrayHeader(4); - - _t1Converter.Write(value.Item1, writer); - _t2Converter.Write(value.Item2, writer); - _t3Converter.Write(value.Item3, writer); - _t4Converter.Write(value.Item4, writer); - } - - public ValueTuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return default(ValueTuple); - } - - const uint expected = 4; - 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 ValueTuple.Create( - item1, - item2, - item3, - item4 - ); - } - } - - public class ValueTupleConverter :IMsgPackConverter> - { - private IMsgPackConverter _t1Converter; - - private IMsgPackConverter _t2Converter; - - private IMsgPackConverter _t3Converter; - - private IMsgPackConverter _t4Converter; - - private IMsgPackConverter _t5Converter; - - public void Initialize(MsgPackContext context) - { - _t1Converter = context.GetConverter(); - _t2Converter = context.GetConverter(); - _t3Converter = context.GetConverter(); - _t4Converter = context.GetConverter(); - _t5Converter = context.GetConverter(); - } - - public void Write(ValueTuple value, IMsgPackWriter writer) - { - 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 ValueTuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return default(ValueTuple); - } - - const uint expected = 5; - 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 ValueTuple.Create( - item1, - item2, - item3, - item4, - item5 - ); - } - } - - public class ValueTupleConverter :IMsgPackConverter> - { - private IMsgPackConverter _t1Converter; - - private IMsgPackConverter _t2Converter; - - private IMsgPackConverter _t3Converter; - - private IMsgPackConverter _t4Converter; - - private IMsgPackConverter _t5Converter; - - private IMsgPackConverter _t6Converter; - - public void Initialize(MsgPackContext context) - { - _t1Converter = context.GetConverter(); - _t2Converter = context.GetConverter(); - _t3Converter = context.GetConverter(); - _t4Converter = context.GetConverter(); - _t5Converter = context.GetConverter(); - _t6Converter = context.GetConverter(); - } - - public void Write(ValueTuple value, IMsgPackWriter writer) - { - 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 ValueTuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return default(ValueTuple); - } - - const uint expected = 6; - 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 ValueTuple.Create( - item1, - item2, - item3, - item4, - item5, - item6 - ); - } - } - - public class ValueTupleConverter :IMsgPackConverter> - { - 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) - { - _t1Converter = context.GetConverter(); - _t2Converter = context.GetConverter(); - _t3Converter = context.GetConverter(); - _t4Converter = context.GetConverter(); - _t5Converter = context.GetConverter(); - _t6Converter = context.GetConverter(); - _t7Converter = context.GetConverter(); - } - - public void Write(ValueTuple value, IMsgPackWriter writer) - { - 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 ValueTuple Read(IMsgPackReader reader) - { - var actual = reader.ReadArrayLength(); - if (actual == null) - { - return default(ValueTuple); - } - - const uint expected = 7; - 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 ValueTuple.Create( - item1, - item2, - item3, - item4, - item5, - item6, - item7 - ); - } - } - -} \ No newline at end of file diff --git a/src/progaudi.tarantool/IIndex.cs b/src/progaudi.tarantool/IIndex.cs index 249c2410..53c7ffa8 100644 --- a/src/progaudi.tarantool/IIndex.cs +++ b/src/progaudi.tarantool/IIndex.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.UpdateOperations; namespace ProGaudi.Tarantool.Client { diff --git a/src/progaudi.tarantool/ILogicalConnection.cs b/src/progaudi.tarantool/ILogicalConnection.cs index b33bcdb3..76439d7d 100644 --- a/src/progaudi.tarantool/ILogicalConnection.cs +++ b/src/progaudi.tarantool/ILogicalConnection.cs @@ -1,8 +1,6 @@ using System; 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 { diff --git a/src/progaudi.tarantool/IRequestWriter.cs b/src/progaudi.tarantool/IRequestWriter.cs index 90eb8c44..5f8659c1 100644 --- a/src/progaudi.tarantool/IRequestWriter.cs +++ b/src/progaudi.tarantool/IRequestWriter.cs @@ -1,5 +1,4 @@ using System; -using System.Threading.Tasks; namespace ProGaudi.Tarantool.Client { @@ -9,6 +8,6 @@ internal interface IRequestWriter : IDisposable bool IsConnected { get; } - void Write(ArraySegment header, ArraySegment body); + void Write(byte[] header, byte[] body); } } \ No newline at end of file diff --git a/src/progaudi.tarantool/IResponseReader.cs b/src/progaudi.tarantool/IResponseReader.cs index c8df0334..1726afd5 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<(byte[] result, int bodyStart)> GetResponseTask(RequestId requestId); bool IsConnected { get; } } diff --git a/src/progaudi.tarantool/ISpace.cs b/src/progaudi.tarantool/ISpace.cs index 3e4b86eb..57cc14c9 100644 --- a/src/progaudi.tarantool/ISpace.cs +++ b/src/progaudi.tarantool/ISpace.cs @@ -1,7 +1,5 @@ using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.UpdateOperations; namespace ProGaudi.Tarantool.Client { @@ -56,14 +54,14 @@ public interface ISpace /// /// Inserted tuple /// If primary key is duplicated - Task Insert(ref T tuple); + 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(ref TInsertable tuple); + Task Insert(in TInsertable tuple); /// /// Select tuples using primary index of the space. diff --git a/src/progaudi.tarantool/Index.cs b/src/progaudi.tarantool/Index.cs index 850ea57f..34180a9d 100644 --- a/src/progaudi.tarantool/Index.cs +++ b/src/progaudi.tarantool/Index.cs @@ -3,8 +3,6 @@ using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.UpdateOperations; namespace ProGaudi.Tarantool.Client { diff --git a/src/progaudi.tarantool/IndexMeta.cs b/src/progaudi.tarantool/IndexMeta.cs index 942b527c..cebb952f 100644 --- a/src/progaudi.tarantool/IndexMeta.cs +++ b/src/progaudi.tarantool/IndexMeta.cs @@ -1,30 +1,34 @@ using System.Collections.Generic; -using ProGaudi.MsgPack.Light; +using MessagePack; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; namespace ProGaudi.Tarantool.Client { // TODO: fix serializer to support struct. - [MsgPackArray] - public sealed class IndexMeta + [MessagePackObject] + public struct IndexMeta { - [MsgPackArrayElement(1)] - public uint Id { get; set; } - - [MsgPackArrayElement(0)] + [Key(0)] public uint SpaceId { get; set; } - [MsgPackArrayElement(2)] + [Key(1)] + public uint Id { get; set; } + + [Key(2)] public string Name { get; set; } - [MsgPackArrayElement(4)] + [Key(3)] + public IndexType Type { get; set; } + + [Key(4)] public IndexCreationOptions Options { get; set; } - [MsgPackArrayElement(3)] - public IndexType Type { get; set; } + [Key(5)] + public IndexPart[] Parts { get; set; } - [MsgPackArrayElement(5)] - public IReadOnlyList Parts { get; set; } + public override string ToString() + { + return $"Index: {Name} ({Id}), Unique: {Options.Unique}, Space: {SpaceId}, Parts: {Parts.Length}"; + } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/LogicalConnection.cs b/src/progaudi.tarantool/LogicalConnection.cs index 5e41093b..11f3a894 100644 --- a/src/progaudi.tarantool/LogicalConnection.cs +++ b/src/progaudi.tarantool/LogicalConnection.cs @@ -1,23 +1,15 @@ using System; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; - -using ProGaudi.MsgPack.Light; - +using MessagePack; 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 readonly ClientOptions _clientOptions; private readonly RequestIdCounter _requestIdCounter; @@ -36,7 +28,6 @@ public LogicalConnection(ClientOptions options, RequestIdCounter requestIdCounte { _clientOptions = options; _requestIdCounter = requestIdCounter; - _msgPackContext = options.MsgPackContext; _logWriter = options.LogWriter; _physicalConnection = new NetworkStreamPhysicalConnection(); @@ -108,21 +99,23 @@ public Task SendRequestWithEmptyResponse(TRequest request, TimeSpan? t public async Task> SendRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { - var stream = await SendRequestImpl(request, timeout).ConfigureAwait(false); - return MsgPackSerializer.Deserialize>(stream, _msgPackContext); + var (result, bodyStart) = await SendRequestImpl(request, timeout).ConfigureAwait(false); + var formatter = MessagePackSerializer.DefaultResolver.GetFormatter>(); + return formatter.Deserialize(result, bodyStart, MessagePackSerializer.DefaultResolver, out _); } public async Task SendRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { - var stream = await SendRequestImpl(request, timeout).ConfigureAwait(false); - return MsgPackSerializer.Deserialize(stream, _msgPackContext); + var (result, bodyStart) = await SendRequestImpl(request, timeout).ConfigureAwait(false); + var formatter = MessagePackSerializer.DefaultResolver.GetFormatter(); + return formatter.Deserialize(result, bodyStart, MessagePackSerializer.DefaultResolver, out _); } public async Task SendRawRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { - return (await SendRequestImpl(request, timeout).ConfigureAwait(false)).ToArray(); + return (await SendRequestImpl(request, timeout).ConfigureAwait(false)).result; } private async Task LoginIfNotGuest(GreetingsResponse greetings) @@ -141,7 +134,7 @@ private async Task LoginIfNotGuest(GreetingsResponse greetings) _clientOptions.LogWriter?.WriteLine($"Authentication request send: {authenticateRequest}"); } - private async Task SendRequestImpl(TRequest request, TimeSpan? timeout) + private async Task<(byte[] result, int bodyStart)> SendRequestImpl(TRequest request, TimeSpan? timeout) where TRequest : IRequest { if (_disposed) @@ -149,15 +142,13 @@ private async Task SendRequestImpl(TRequest request, Tim throw new ObjectDisposedException(nameof(LogicalConnection)); } - var bodyBuffer = MsgPackSerializer.Serialize(request, _msgPackContext); + var bodyBuffer = MessagePackSerializer.Serialize(request); 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 headerBuffer = MessagePackSerializer.Serialize(new RequestHeader(request.Code, requestId)); + _requestWriter.Write(headerBuffer, bodyBuffer); try { @@ -167,10 +158,10 @@ 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}."); + var (result, bodyStart) = await responseTask.ConfigureAwait(false); + _logWriter?.WriteLine($"Response with requestId {requestId} is recieved, length: {result.Length}."); - return responseStream; + return (result, bodyStart); } catch (ArgumentException) { @@ -183,25 +174,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..c727eddb 100644 --- a/src/progaudi.tarantool/LogicalConnectionManager.cs +++ b/src/progaudi.tarantool/LogicalConnectionManager.cs @@ -3,8 +3,6 @@ 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 diff --git a/src/progaudi.tarantool/Model/Requests/AuthenticationRequest.cs b/src/progaudi.tarantool/Model/AuthenticationRequest.cs similarity index 52% rename from src/progaudi.tarantool/Model/Requests/AuthenticationRequest.cs rename to src/progaudi.tarantool/Model/AuthenticationRequest.cs index c7643cef..d48b3d5d 100644 --- a/src/progaudi.tarantool/Model/Requests/AuthenticationRequest.cs +++ b/src/progaudi.tarantool/Model/AuthenticationRequest.cs @@ -1,12 +1,13 @@ using System; using System.Linq; - -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Responses; +using MessagePack; +using MessagePack.Formatters; using ProGaudi.Tarantool.Client.Utils; +using static MessagePack.MessagePackBinary; -namespace ProGaudi.Tarantool.Client.Model.Requests +namespace ProGaudi.Tarantool.Client.Model { + [MessagePackFormatter(typeof(Formatter))] public class AuthenticationRequest : IRequest { private AuthenticationRequest(string username, byte[] scramble) @@ -19,7 +20,7 @@ private AuthenticationRequest(string username, byte[] scramble) public byte[] Scramble { get; } - public CommandCode Code => CommandCode.Auth; + public CommandCodes Code => CommandCodes.Auth; public static AuthenticationRequest Create(GreetingsResponse greetings, UriBuilder uri) { @@ -51,5 +52,30 @@ private static string ToReadableString(byte[] bytes) { return string.Concat(bytes.Select(b => b.ToString("X2 "))); } + + internal class Formatter : IMessagePackFormatter + { + public int Serialize(ref byte[] bytes, int offset, AuthenticationRequest value, IFormatterResolver formatterResolver) + { + if (value == null) return WriteNil(ref bytes, offset); + + var startOffset = offset; + + offset += WriteFixedMapHeaderUnsafe(ref bytes, offset, 2); + offset += WriteUInt32(ref bytes, offset, Keys.Username); + offset += WriteString(ref bytes, offset, value.Username); + offset += WriteUInt32(ref bytes, offset, Keys.Tuple); + offset += WriteArrayHeader(ref bytes, offset, 2); + offset += WriteString(ref bytes, offset, "chap-sha1"); + offset += WriteBytes(ref bytes, offset, value.Scramble); + + return offset - startOffset; + } + + public AuthenticationRequest Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + 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..34317475 --- /dev/null +++ b/src/progaudi.tarantool/Model/CallRequest.cs @@ -0,0 +1,46 @@ +using System; +using MessagePack; +using MessagePack.Formatters; +using static MessagePack.MessagePackBinary; + +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 : IMessagePackFormatter> + { + public int Serialize(ref byte[] bytes, int offset, CallRequest value, IFormatterResolver formatterResolver) + { + var startOffset = offset; + + offset += WriteFixedMapHeaderUnsafe(ref bytes, offset, 2); + offset += WriteUInt32(ref bytes, offset, Keys.FunctionName); + offset += WriteString(ref bytes, offset, value.FunctionName); + offset += WriteUInt32(ref bytes, offset, Keys.Tuple); + offset += formatterResolver.GetFormatter().Serialize(ref bytes, offset, value.Tuple, formatterResolver); + + return offset - startOffset; + } + + public CallRequest Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/src/progaudi.tarantool/Model/ClientOptions.cs b/src/progaudi.tarantool/Model/ClientOptions.cs index 200569c0..6ff7ff64 100644 --- a/src/progaudi.tarantool/Model/ClientOptions.cs +++ b/src/progaudi.tarantool/Model/ClientOptions.cs @@ -1,23 +1,20 @@ -using ProGaudi.MsgPack.Light; - -namespace ProGaudi.Tarantool.Client.Model +namespace ProGaudi.Tarantool.Client.Model { public class ClientOptions { - public ClientOptions(ILog log = null, MsgPackContext context = null) - : this(new ConnectionOptions(), log, context) + public ClientOptions(ILog log = null) + : this(new ConnectionOptions(), log) { } - public ClientOptions(string replicationSource, ILog log = null, MsgPackContext context = null) - : this(new ConnectionOptions(replicationSource, log), log, context) + public ClientOptions(string replicationSource, ILog log = null) + : this(new ConnectionOptions(replicationSource, log), log) { } - private ClientOptions(ConnectionOptions options, ILog log, MsgPackContext context) + private ClientOptions(ConnectionOptions options, ILog log) { ConnectionOptions = options; - MsgPackContext = context ?? new MsgPackContext(); if (log != null) { LogWriter = new LogWriterWrapper(this, log); @@ -26,8 +23,6 @@ private ClientOptions(ConnectionOptions options, ILog log, MsgPackContext contex public ILog LogWriter { get; } - public MsgPackContext MsgPackContext { get; } - public ConnectionOptions ConnectionOptions { get; } public string Name { get; set; } 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..fdffa1d4 --- /dev/null +++ b/src/progaudi.tarantool/Model/DataResponse.cs @@ -0,0 +1,133 @@ +using System.Collections.Generic; +using MessagePack; +using MessagePack.Formatters; +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 : IMessagePackFormatter + { + public int Serialize(ref byte[] bytes, int offset, DataResponse value, IFormatterResolver formatterResolver) + { + throw new System.NotImplementedException(); + } + + public DataResponse Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + if (MessagePackBinary.IsNil(bytes, offset)) + { + readSize = 1; + return null; + } + + var startOffset = offset; + var length = MessagePackBinary.ReadMapHeaderRaw(bytes, offset, out readSize); + offset += readSize; + + var result = default(DataResponse); + for (var i = 0; i < length; i++) + { + var key = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); + offset += readSize; + switch (key) + { + case Keys.SqlInfo: + result = new DataResponse(formatterResolver.GetFormatter().Deserialize(bytes, offset, formatterResolver, out readSize)); + offset += readSize; + break; + default: + offset += MessagePackBinary.ReadNext(bytes, offset); + break; + } + } + + readSize = offset - startOffset; + + 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 : IMessagePackFormatter> + { + public int Serialize(ref byte[] bytes, int offset, DataResponse value, IFormatterResolver formatterResolver) + { + throw new System.NotImplementedException(); + } + + public DataResponse Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + if (MessagePackBinary.IsNil(bytes, offset)) + { + readSize = 1; + return null; + } + + var startOffset = offset; + var length = MessagePackBinary.ReadMapHeaderRaw(bytes, offset, out readSize); + offset += readSize; + + var data = default(T); + var dataWasSet = false; + var metadata = default(FieldMetadata[]); + var sqlInfo = default(SqlInfo); + + for (var i = 0; i < length; i++) + { + var key = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); + offset += readSize; + switch (key) + { + case Keys.Data: + data = formatterResolver.GetFormatter().Deserialize(bytes, offset, formatterResolver, out readSize); + dataWasSet = true; + offset += readSize; + break; + case Keys.Metadata: + metadata = formatterResolver.GetFormatter().Deserialize(bytes, offset, formatterResolver, out readSize); + offset += readSize; + break; + case Keys.SqlInfo: + sqlInfo = formatterResolver.GetFormatter().Deserialize(bytes, offset, formatterResolver, out readSize); + offset += readSize; + break; + default: + offset += MessagePackBinary.ReadNext(bytes, offset); + break; + } + } + + if (!dataWasSet && sqlInfo == null) + { + throw ExceptionHelper.NoDataInDataResponse(); + } + + readSize = offset - startOffset; + + return new DataResponse(data, metadata, sqlInfo); + } + } + } +} \ 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..9010d25f --- /dev/null +++ b/src/progaudi.tarantool/Model/EnumAsStringFormatter.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using MessagePack; +using MessagePack.Formatters; + +namespace ProGaudi.Tarantool.Client.Model +{ + public sealed class EnumAsStringFormatter : IMessagePackFormatter + { + private readonly bool _ignoreCase; + private readonly Dictionary _nameValueMapping; + private readonly Dictionary _valueNameMapping; + + public EnumAsStringFormatter(bool ignoreCase) + { + _ignoreCase = ignoreCase; + var names = Enum.GetNames(typeof(T)); + var values = Enum.GetValues(typeof(T)); + + _nameValueMapping = new Dictionary(names.Length, ignoreCase ? (IEqualityComparer) IgnoreCaseComparer.Instance : EqualityComparer.Default); + _valueNameMapping = new Dictionary(names.Length); + + for (var i = 0; i < names.Length; i++) + { + _nameValueMapping[names[i]] = (T)values.GetValue(i); + _valueNameMapping[(T)values.GetValue(i)] = names[i]; + } + } + + public int Serialize(ref byte[] bytes, int offset, T value, IFormatterResolver formatterResolver) + { + if (!_valueNameMapping.TryGetValue(value, out var name)) + { + name = value.ToString(); // fallback for flags etc, But Enum.ToString is too slow. + } + + return MessagePackBinary.WriteString(ref bytes, offset, name); + } + + public T Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + var name = MessagePackBinary.ReadString(bytes, offset, out readSize); + + if (!_nameValueMapping.TryGetValue(name, out var value)) + { + value = (T)Enum.Parse(typeof(T), name, _ignoreCase); // Enum.Parse is too slow + } + + return value; + } + + private class IgnoreCaseComparer : IEqualityComparer + { + public static readonly IgnoreCaseComparer Instance = new IgnoreCaseComparer(); + + private IgnoreCaseComparer() + { + } + + public bool Equals(string x, string y) + { + return string.Compare(x, y, StringComparison.InvariantCultureIgnoreCase) == 0; + } + + public int GetHashCode(string obj) + { + return obj.ToLowerInvariant().GetHashCode(); + } + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/EnumResolver.cs b/src/progaudi.tarantool/Model/EnumResolver.cs new file mode 100644 index 00000000..1e18c827 --- /dev/null +++ b/src/progaudi.tarantool/Model/EnumResolver.cs @@ -0,0 +1,43 @@ +using System; +using System.Reflection; +using MessagePack; +using MessagePack.Formatters; + +namespace ProGaudi.Tarantool.Client.Model +{ + public sealed class EnumResolver : IFormatterResolver + { + public static readonly EnumResolver Instance = new EnumResolver(); + + private EnumResolver() + { + } + + public IMessagePackFormatter GetFormatter() + { + return Cache.Formatter; + } + + private static class Cache + { + public static readonly IMessagePackFormatter Formatter; + + static Cache() + { + var type = typeof(T); + if (!type.IsEnum) + { + return; + } + + var enumAsString = type.GetCustomAttribute(); + if (enumAsString == null) + { + return; + } + + Formatter = (IMessagePackFormatter) Activator.CreateInstance(typeof(EnumAsStringFormatter<>).MakeGenericType(type), new object[] { enumAsString.IgnoreCase }); + } + } + } +} \ No newline at end of file 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/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/FieldMetadata.cs b/src/progaudi.tarantool/Model/FieldMetadata.cs new file mode 100644 index 00000000..32c76aec --- /dev/null +++ b/src/progaudi.tarantool/Model/FieldMetadata.cs @@ -0,0 +1,92 @@ +using System; +using JetBrains.Annotations; +using MessagePack; +using MessagePack.Formatters; + +namespace ProGaudi.Tarantool.Client.Model +{ + [MessagePackFormatter(typeof(Formatter))] + public class FieldMetadata : IEquatable + { + public string Name { get; } + + public FieldMetadata([NotNull] string name) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + } + + public override string ToString() => Name; + + public bool Equals(FieldMetadata other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return string.Equals(Name, other.Name); + } + + 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((FieldMetadata) obj); + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public static bool operator ==(FieldMetadata left, FieldMetadata right) + { + return Equals(left, right); + } + + public static bool operator !=(FieldMetadata left, FieldMetadata right) + { + return !Equals(left, right); + } + + public sealed class Formatter : IMessagePackFormatter + { + public int Serialize(ref byte[] bytes, int offset, FieldMetadata value, IFormatterResolver formatterResolver) + { + throw new NotImplementedException(); + } + + public FieldMetadata Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + if (MessagePackBinary.IsNil(bytes, offset)) + { + readSize = 1; + return null; + } + + var startOffset = offset; + var length = MessagePackBinary.ReadMapHeaderRaw(bytes, offset, out readSize); + offset += readSize; + + var result = default(FieldMetadata); + for (var i = 0; i < length; i++) + { + var key = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); + offset += readSize; + switch (key) + { + case Keys.FieldName: + result = new FieldMetadata(MessagePackBinary.ReadString(bytes, offset, out readSize)); + offset += readSize; + break; + default: + offset += MessagePackBinary.ReadNext(bytes, offset); + break; + } + } + + readSize = offset - startOffset; + + return result; + } + } + } +} \ No newline at end of file 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..27a1ecfe 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 MessagePack; + +namespace ProGaudi.Tarantool.Client.Model { + [MessagePackObject] public class IndexCreationOptions { public IndexCreationOptions(bool unique) @@ -7,6 +10,7 @@ public IndexCreationOptions(bool unique) Unique = unique; } + [Key("unique")] public bool Unique { get; } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/IndexPart.cs b/src/progaudi.tarantool/Model/IndexPart.cs index c527ab88..df74735c 100644 --- a/src/progaudi.tarantool/Model/IndexPart.cs +++ b/src/progaudi.tarantool/Model/IndexPart.cs @@ -1,10 +1,15 @@ -using ProGaudi.Tarantool.Client.Model.Enums; +using System; +using System.Runtime.Serialization; +using MessagePack; +using MessagePack.Formatters; +using ProGaudi.Tarantool.Client.Utils; namespace ProGaudi.Tarantool.Client.Model { + [MessagePackFormatter(typeof(Formatter))] public class IndexPart { - public IndexPart(uint fieldNo, IndexPartType type) + public IndexPart(uint fieldNo, FieldType type) { FieldNo = fieldNo; Type = type; @@ -12,6 +17,93 @@ 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 : IMessagePackFormatter + { + public int Serialize(ref byte[] bytes, int offset, IndexPart value, IFormatterResolver formatterResolver) + { + throw new NotImplementedException(); + } + + public IndexPart Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + var type = MessagePackBinary.GetMessagePackType(bytes, offset); + switch (type) + { + case MessagePackType.Nil: + readSize = 1; + return null; + case MessagePackType.Array: + return ReadFromArray(bytes, offset, formatterResolver, out readSize); + case MessagePackType.Map: + return ReadFromMap(bytes, offset, formatterResolver, out readSize); + default: + throw new ArgumentOutOfRangeException(); + } + } + + private static IndexPart ReadFromMap(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + var startOffset = offset; + var length = MessagePackBinary.ReadArrayHeaderRaw(bytes, offset, out readSize); + offset += readSize; + + uint? fieldNo = null; + FieldType? indexPartType = null; + + for (var i = 0; i < length; i++) + { + var key = MessagePackBinary.ReadString(bytes, offset, out readSize); + offset += readSize; + switch (key) + { + case "field": + fieldNo = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); + offset += readSize; + break; + case "type": + indexPartType = formatterResolver.GetFormatter().Deserialize(bytes, offset, formatterResolver, out readSize); + offset += readSize; + break; + default: + offset += MessagePackBinary.ReadNext(bytes, offset); + break; + } + } + + readSize = offset - startOffset; + + return fieldNo.HasValue && indexPartType.HasValue + ? new IndexPart(fieldNo.Value, indexPartType.Value) + : throw new SerializationException("Can't read fieldNo or indexPart from map of index metadata"); + } + + private static IndexPart ReadFromArray(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + var startOffset = offset; + var length = MessagePackBinary.ReadArrayHeaderRaw(bytes, offset, out readSize); + offset += readSize; + + if (length != 2u) + { + throw ExceptionHelper.InvalidArrayLength(2u, length); + } + + var fieldNo = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); + offset += readSize; + var indexPartType = formatterResolver.GetFormatter().Deserialize(bytes, offset, formatterResolver, out readSize); + offset += readSize; + + readSize = offset - startOffset; + + return new IndexPart(fieldNo, indexPartType); + } + } } } \ 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..37c9b4c0 --- /dev/null +++ b/src/progaudi.tarantool/Model/InsertReplaceRequest.cs @@ -0,0 +1,43 @@ +using MessagePack; +using MessagePack.Formatters; +using static MessagePack.MessagePackBinary; + +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 : IMessagePackFormatter> + { + public int Serialize(ref byte[] bytes, int offset, InsertReplaceRequest value, IFormatterResolver formatterResolver) + { + var startOffset = offset; + + offset += WriteFixedMapHeaderUnsafe(ref bytes, offset, 2); + offset += WriteUInt32(ref bytes, offset, Keys.SpaceId); + offset += WriteUInt32(ref bytes, offset, value.SpaceId); + offset += WriteUInt32(ref bytes, offset, Keys.Tuple); + offset += formatterResolver.GetFormatter().Serialize(ref bytes, offset, value.Tuple, formatterResolver); + + return offset - startOffset; + } + + public InsertReplaceRequest Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + throw new System.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/PackerResolver.cs b/src/progaudi.tarantool/Model/PackerResolver.cs new file mode 100644 index 00000000..110de932 --- /dev/null +++ b/src/progaudi.tarantool/Model/PackerResolver.cs @@ -0,0 +1,70 @@ +using System; +using MessagePack; +using MessagePack.Formatters; + +namespace ProGaudi.Tarantool.Client.Model +{ + public sealed class PackerResolver : IFormatterResolver + { + public static readonly PackerResolver Instance = new PackerResolver(); + + private PackerResolver() + { + } + + public IMessagePackFormatter GetFormatter() + { + return Cache.Formatter; + } + + private static class Cache + { + public static readonly IMessagePackFormatter Formatter; + + static Cache() + { + var type = typeof(T); + + if (type == typeof(DataResponse)) + { + Formatter = new DataResponse.Formatter() as IMessagePackFormatter; + return; + } + + if (!type.IsGenericType) return; + + var genericTypeDefinition = type.GetGenericTypeDefinition(); + if (genericTypeDefinition == typeof(CallRequest<>)) + { + var result = typeof(CallRequest<>.Formatter).MakeGenericType(type.GenericTypeArguments); + Formatter = (IMessagePackFormatter) Activator.CreateInstance(result); + return; + } + + if ( + genericTypeDefinition == typeof(InsertReplaceRequest<>) || + genericTypeDefinition == typeof(ReplaceRequest<>) || + genericTypeDefinition == typeof(InsertRequest<>) + ) + { + var result = typeof(InsertReplaceRequest<>.Formatter).MakeGenericType(type.GenericTypeArguments); + Formatter = (IMessagePackFormatter) Activator.CreateInstance(result); + return; + } + + if (genericTypeDefinition == typeof(SelectRequest<>)) + { + var result = typeof(SelectRequest<>.Formatter).MakeGenericType(type.GenericTypeArguments); + Formatter = (IMessagePackFormatter) Activator.CreateInstance(result); + return; + } + + if (genericTypeDefinition == typeof(DataResponse<>)) + { + var result = typeof(DataResponse<>.Formatter).MakeGenericType(type.GenericTypeArguments); + Formatter = (IMessagePackFormatter) Activator.CreateInstance(result); + } + } + } + } +} \ 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..5e7080f7 --- /dev/null +++ b/src/progaudi.tarantool/Model/RequestHeader.cs @@ -0,0 +1,36 @@ +using MessagePack; +using MessagePack.Formatters; +using static MessagePack.MessagePackBinary; + +namespace ProGaudi.Tarantool.Client.Model +{ + [MessagePackFormatter(typeof(Formatter))] + public class RequestHeader : HeaderBase + { + public RequestHeader(CommandCodes code, RequestId requestId) + : base(code, requestId) + { + } + + public sealed class Formatter : IMessagePackFormatter + { + public int Serialize(ref byte[] bytes, int offset, RequestHeader value, IFormatterResolver formatterResolver) + { + var startOffset = offset; + + offset += WriteFixedMapHeaderUnsafe(ref bytes, offset, 2); + offset += WriteUInt32(ref bytes, offset, Keys.Code); + offset += WriteUInt32(ref bytes, offset, (uint)value.Code); + offset += WriteUInt32(ref bytes, offset, Keys.Sync); + offset += WriteUInt64(ref bytes, offset, value.RequestId.Value); + + return offset - startOffset; + } + + public RequestHeader Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + throw new System.NotImplementedException(); + } + } + } +} \ 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..806a70c5 --- /dev/null +++ b/src/progaudi.tarantool/Model/ResponseHeader.cs @@ -0,0 +1,76 @@ +using MessagePack; +using MessagePack.Formatters; +using ProGaudi.Tarantool.Client.Utils; + +namespace ProGaudi.Tarantool.Client.Model +{ + [MessagePackFormatter(typeof(Formatter))] + 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 : IMessagePackFormatter + { + public int Serialize(ref byte[] bytes, int offset, ResponseHeader value, IFormatterResolver formatterResolver) + { + throw new System.NotImplementedException(); + } + + public ResponseHeader Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + var startOffset = offset; + var length = MessagePackBinary.ReadMapHeaderRaw(bytes, offset, out readSize); + offset += readSize; + + CommandCodes? code = null; + RequestId? sync = null; + ulong? schemaId = null; + + for (var i = 0; i < length; i++) + { + var key = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); + offset += readSize; + + switch (key) + { + case Keys.Code: + code = (CommandCodes) MessagePackBinary.ReadUInt32(bytes, offset, out readSize); + offset += readSize; + break; + case Keys.Sync: + sync = new RequestId(MessagePackBinary.ReadUInt64(bytes, offset, out readSize)); + offset += readSize; + break; + case Keys.SchemaId: + schemaId = MessagePackBinary.ReadUInt64(bytes, offset, out readSize); + offset += readSize; + break; + default: + readSize = MessagePackBinary.ReadNextBlock(bytes, offset); + offset += readSize; + break; + } + } + + readSize = offset - startOffset; + + 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/Responses/FieldMetadata.cs b/src/progaudi.tarantool/Model/Responses/FieldMetadata.cs deleted file mode 100644 index 519dbdd4..00000000 --- a/src/progaudi.tarantool/Model/Responses/FieldMetadata.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using JetBrains.Annotations; - -namespace ProGaudi.Tarantool.Client.Model.Responses -{ - public class FieldMetadata : IEquatable - { - public string Name { get; } - - public FieldMetadata([NotNull] string name) - { - Name = name ?? throw new ArgumentNullException(nameof(name)); - } - - public override string ToString() => Name; - - public bool Equals(FieldMetadata other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return string.Equals(Name, other.Name); - } - - 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((FieldMetadata) obj); - } - - public override int GetHashCode() - { - return Name.GetHashCode(); - } - - public static bool operator ==(FieldMetadata left, FieldMetadata right) - { - return Equals(left, right); - } - - public static bool operator !=(FieldMetadata left, FieldMetadata right) - { - return !Equals(left, right); - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/Responses/SqlInfo.cs b/src/progaudi.tarantool/Model/Responses/SqlInfo.cs deleted file mode 100644 index fdef12f2..00000000 --- a/src/progaudi.tarantool/Model/Responses/SqlInfo.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace ProGaudi.Tarantool.Client.Model.Responses -{ - public class SqlInfo - { - public SqlInfo(int rowCount) - { - RowCount = rowCount; - } - - public int RowCount { get; } - } -} \ 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..339570ce --- /dev/null +++ b/src/progaudi.tarantool/Model/SelectRequest.cs @@ -0,0 +1,64 @@ +using MessagePack; +using MessagePack.Formatters; +using static MessagePack.MessagePackBinary; + +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 : IMessagePackFormatter> + { + public int Serialize(ref byte[] bytes, int offset, SelectRequest value, IFormatterResolver formatterResolver) + { + var startOffset = offset; + + offset += WriteFixedMapHeaderUnsafe(ref bytes, offset, 6); + offset += WriteUInt32(ref bytes, offset, Keys.SpaceId); + offset += WriteUInt32(ref bytes, offset, value.SpaceId); + offset += WriteUInt32(ref bytes, offset, Keys.IndexId); + offset += WriteUInt32(ref bytes, offset, value.IndexId); + offset += WriteUInt32(ref bytes, offset, Keys.Limit); + offset += WriteUInt32(ref bytes, offset, value.Limit); + offset += WriteUInt32(ref bytes, offset, Keys.Offset); + offset += WriteUInt32(ref bytes, offset, value.Offset); + offset += WriteUInt32(ref bytes, offset, Keys.Iterator); + offset += WriteUInt32(ref bytes, offset, (uint)value.Iterator); + offset += WriteUInt32(ref bytes, offset, Keys.Key); + offset += formatterResolver + .GetFormatter() + .Serialize(ref bytes, offset, value.SelectKey, formatterResolver); + + return offset - startOffset; + } + + public SelectRequest Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + throw new System.NotImplementedException(); + } + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/SpaceField.cs b/src/progaudi.tarantool/Model/SpaceField.cs index ee007589..0b3f9fc0 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 MessagePack; namespace ProGaudi.Tarantool.Client.Model { + [MessagePackObject] public class SpaceField { public SpaceField(string name, FieldType type) @@ -10,8 +11,15 @@ public SpaceField(string name, FieldType type) Type = type; } + [Key("name")] public string Name { get; } + [Key("type")] public FieldType Type { get; } + + 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 index 8d9b7ca1..3d13aec1 100644 --- a/src/progaudi.tarantool/Model/SpaceOptions.cs +++ b/src/progaudi.tarantool/Model/SpaceOptions.cs @@ -1,11 +1,16 @@ -using ProGaudi.MsgPack.Light; +using MessagePack; namespace ProGaudi.Tarantool.Client.Model { - [MsgPackMap] + [MessagePackObject] public class SpaceOptions { - [MsgPackMapElement("temporary")] + [Key("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/SqlInfo.cs b/src/progaudi.tarantool/Model/SqlInfo.cs new file mode 100644 index 00000000..016f6d1d --- /dev/null +++ b/src/progaudi.tarantool/Model/SqlInfo.cs @@ -0,0 +1,58 @@ +using MessagePack; +using MessagePack.Formatters; + +namespace ProGaudi.Tarantool.Client.Model +{ + [MessagePackFormatter(typeof(Formatter))] + public class SqlInfo + { + public SqlInfo(int rowCount) + { + RowCount = rowCount; + } + + public int RowCount { get; } + + public sealed class Formatter : IMessagePackFormatter + { + public int Serialize(ref byte[] bytes, int offset, SqlInfo value, IFormatterResolver formatterResolver) + { + throw new System.NotImplementedException(); + } + + public SqlInfo Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + if (MessagePackBinary.IsNil(bytes, offset)) + { + readSize = 1; + return null; + } + + var startOffset = offset; + var length = MessagePackBinary.ReadMapHeaderRaw(bytes, offset, out readSize); + offset += readSize; + + var result = default(SqlInfo); + for (var i = 0; i < length; i++) + { + var key = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); + offset += readSize; + switch (key) + { + case Keys.SqlRowCount: + result = new SqlInfo(MessagePackBinary.ReadInt32(bytes, offset, out readSize)); + offset += readSize; + break; + default: + offset += MessagePackBinary.ReadNext(bytes, offset); + break; + } + } + + readSize = offset - startOffset; + + return result; + } + } + } +} \ No newline at end of file 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/Enums/StorageEngine.cs b/src/progaudi.tarantool/Model/StorageEngine.cs similarity index 56% rename from src/progaudi.tarantool/Model/Enums/StorageEngine.cs rename to src/progaudi.tarantool/Model/StorageEngine.cs index e10bc111..5b7eef34 100644 --- a/src/progaudi.tarantool/Model/Enums/StorageEngine.cs +++ b/src/progaudi.tarantool/Model/StorageEngine.cs @@ -1,5 +1,6 @@ -namespace ProGaudi.Tarantool.Client.Model.Enums +namespace ProGaudi.Tarantool.Client.Model { + [EnumAsString(true)] public enum StorageEngine { Memtx, 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/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/RequestLength.cs b/src/progaudi.tarantool/RequestLength.cs new file mode 100644 index 00000000..9465d8ca --- /dev/null +++ b/src/progaudi.tarantool/RequestLength.cs @@ -0,0 +1,33 @@ +using MessagePack; +using MessagePack.Formatters; + +namespace ProGaudi.Tarantool.Client +{ + [MessagePackFormatter(typeof(Formatter))] + public readonly struct RequestLength + { + private readonly uint _value; + + public RequestLength(byte[] header, byte[] body) => _value = (uint) (header.Length + body.Length); + + public RequestLength(uint length) => _value = length; + + public static implicit operator uint(RequestLength length) + { + return length._value; + } + + public sealed class Formatter : IMessagePackFormatter + { + public int Serialize(ref byte[] bytes, int offset, RequestLength value, IFormatterResolver formatterResolver) + { + return MessagePackBinary.WriteUInt32ForceUInt32Block(ref bytes, offset, value); + } + + public RequestLength Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + return new RequestLength(MessagePackBinary.ReadUInt32(bytes, offset, out readSize)); + } + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/RequestWriter.cs b/src/progaudi.tarantool/RequestWriter.cs index ace44246..00b4246b 100644 --- a/src/progaudi.tarantool/RequestWriter.cs +++ b/src/progaudi.tarantool/RequestWriter.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading; +using MessagePack; using ProGaudi.Tarantool.Client.Model; namespace ProGaudi.Tarantool.Client @@ -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(); @@ -43,14 +44,14 @@ public void BeginWriting() public bool IsConnected => !_disposed; - public void Write(ArraySegment header, ArraySegment body) + public void Write(byte[] header, byte[] body) { if (_disposed) { throw new ObjectDisposedException(nameof(ResponseReader)); } - _clientOptions?.LogWriter?.WriteLine($"Enqueuing request: headers {header.Count} bytes, body {body.Count} bytes."); + _clientOptions?.LogWriter?.WriteLine($"Enqueuing request: headers {header.Length} bytes, body {body.Length} bytes."); bool shouldSignal; lock (_lock) { @@ -97,12 +98,7 @@ private void WriteFunction() private void WriteRequests(int limit) { - void WriteBuffer(ArraySegment buffer) - { - _physicalConnection.Write(buffer.Array, buffer.Offset, buffer.Count); - } - - Tuple, ArraySegment> GetRequest() + Tuple GetRequest() { lock (_lock) { @@ -113,16 +109,20 @@ Tuple, ArraySegment> GetRequest() return null; } - Tuple, ArraySegment> request; + Tuple request; var count = 0; while ((request = GetRequest()) != null) { - _clientOptions?.LogWriter?.WriteLine($"Writing request: headers {request.Item1.Count} bytes, body {request.Item2.Count} bytes."); + var header = request.Item1; + var body = request.Item2; + _clientOptions?.LogWriter?.WriteLine($"Writing request: headers {header.Length} bytes, body {body.Length} bytes."); - WriteBuffer(request.Item1); - WriteBuffer(request.Item2); + var length = MessagePackSerializer.Serialize(new RequestLength(header, body)); + _physicalConnection.Write(length, 0, length.Length); + _physicalConnection.Write(header, 0, header.Length); + _physicalConnection.Write(body, 0, body.Length); - _clientOptions?.LogWriter?.WriteLine($"Wrote request: headers {request.Item1.Count} bytes, body {request.Item2.Count} bytes."); + _clientOptions?.LogWriter?.WriteLine($"Wrote request: headers {header.Length} bytes, body {body.Length} bytes."); count++; if (limit > 0 && count > limit) diff --git a/src/progaudi.tarantool/ResponseReader.cs b/src/progaudi.tarantool/ResponseReader.cs index 93d15334..904567df 100644 --- a/src/progaudi.tarantool/ResponseReader.cs +++ b/src/progaudi.tarantool/ResponseReader.cs @@ -1,17 +1,11 @@ using System; 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 MessagePack; 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 @@ -20,8 +14,8 @@ internal class ResponseReader : IResponseReader { private readonly IPhysicalConnection _physicalConnection; - private readonly Dictionary> _pendingRequests = - new Dictionary>(); + private readonly Dictionary> _pendingRequests = + new Dictionary>(); private readonly ReaderWriterLockSlim _pendingRequestsLock = new ReaderWriterLockSlim(); @@ -72,7 +66,7 @@ public void Dispose() } } - public Task GetResponseTask(RequestId requestId) + public Task<(byte[] result, int bodyStart)> GetResponseTask(RequestId requestId) { try { @@ -88,7 +82,7 @@ public Task GetResponseTask(RequestId requestId) throw ExceptionHelper.RequestWithSuchIdAlreadySent(requestId); } - var tcs = new TaskCompletionSource(); + var tcs = new TaskCompletionSource<(byte[] result, int bodyStart)>(); _pendingRequests.Add(requestId, tcs); return tcs.Task; @@ -109,7 +103,7 @@ public void BeginReading() readingTask.ContinueWith(EndReading); } - private TaskCompletionSource PopResponseCompletionSource(RequestId requestId) + private TaskCompletionSource<(byte[] result, int bodyStart)> PopResponseCompletionSource(RequestId requestId) { try { @@ -215,8 +209,8 @@ private int TryParseResponses() private void MatchResult(byte[] result) { - var resultStream = new MemoryStream(result); - var header= MsgPackSerializer.Deserialize(resultStream, _clientOptions.MsgPackContext); + var headerFormatter = MessagePackSerializer.DefaultResolver.GetFormatter(); + var header = headerFormatter.Deserialize(result, 0, MessagePackSerializer.DefaultResolver, out var readSize); var tcs = PopResponseCompletionSource(header.RequestId); if (tcs == null) @@ -226,15 +220,16 @@ private void MatchResult(byte[] result) return; } - if ((header.Code & CommandCode.ErrorMask) == CommandCode.ErrorMask) + _clientOptions.LogWriter?.WriteLine($"Match for request with id {header.RequestId} found."); + if ((header.Code & CommandCodes.ErrorMask) == CommandCodes.ErrorMask) { - var errorResponse = MsgPackSerializer.Deserialize(resultStream, _clientOptions.MsgPackContext); + var errorFormatter = MessagePackSerializer.DefaultResolver.GetFormatter(); + var errorResponse = errorFormatter.Deserialize(result, readSize, MessagePackSerializer.DefaultResolver, out _); tcs.SetException(ExceptionHelper.TarantoolError(header, errorResponse)); } else { - _clientOptions.LogWriter?.WriteLine($"Match for request with id {header.RequestId} found."); - tcs.SetResult(resultStream); + tcs.SetResult((result, readSize)); } } @@ -295,7 +290,7 @@ private byte[] TryParseResponse() var headerSizeBuffer = new byte[Constants.PacketSizeBufferSize]; Array.Copy(_buffer, _parsingOffset, headerSizeBuffer, 0, Constants.PacketSizeBufferSize); - var packetSize = (int)MsgPackSerializer.Deserialize(headerSizeBuffer, _clientOptions.MsgPackContext); + var packetSize = (int)MessagePackBinary.ReadUInt64(_buffer, _parsingOffset, out _); return packetSize; } diff --git a/src/progaudi.tarantool/Schema.cs b/src/progaudi.tarantool/Schema.cs index 00cbe251..27d4737c 100644 --- a/src/progaudi.tarantool/Schema.cs +++ b/src/progaudi.tarantool/Schema.cs @@ -2,8 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.Requests; +using ProGaudi.Tarantool.Client.Model; namespace ProGaudi.Tarantool.Client { @@ -15,7 +14,6 @@ public class Schema : ISchema internal const uint PrimaryIndexId = 0; - private readonly ILogicalConnection _logicalConnection; private NameIdLazyWrapper _spaces; private Dictionary _indicesBySpace = new Dictionary(); @@ -25,9 +23,11 @@ public class Schema : ISchema public Schema(ILogicalConnection logicalConnection) { - _logicalConnection = logicalConnection; + Connection = logicalConnection; } + public ILogicalConnection Connection { get; } + public DateTimeOffset LastReloadTime { get; private set; } public bool TryGetSpace(string name, out ISpace space) @@ -78,7 +78,7 @@ private async Task Select(uint spaceId, Iterator iterator = Iterator.All { 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/Space.cs b/src/progaudi.tarantool/Space.cs index bd312a46..3c56f114 100644 --- a/src/progaudi.tarantool/Space.cs +++ b/src/progaudi.tarantool/Space.cs @@ -3,8 +3,6 @@ using System.Linq; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; -using ProGaudi.Tarantool.Client.Model.UpdateOperations; namespace ProGaudi.Tarantool.Client { @@ -12,13 +10,13 @@ public class Space : ISpace { private readonly Schema _schema; private readonly SpaceMeta _meta; - private readonly Lazy> _indices; + private readonly Lazy> _indices; 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)); + _indices = new Lazy>(() => new NameIdLazyWrapper(indexMetas.Select(x => (IndexMeta?)x).ToArray(), x => x.Value.Id, x => x.Value.Name)); } public override string ToString() @@ -52,13 +50,13 @@ public bool TryGetIndex(string name, out IIndex inde { var meta = _indices.Value[name]; - if (meta == default) + if (meta == null) { index = default; return false; } - index = new Index(meta, _schema); + index = new Index(meta.Value, _schema); return true; } @@ -66,22 +64,23 @@ public bool TryGetIndex(uint id, out IIndex index) { var meta = _indices.Value[id]; - if (meta == default) + if (meta == null) { index = default; return false; } - index = new Index(meta, _schema); + index = new Index(meta.Value, _schema); return true; } - public Task Insert(ref T tuple) + public async Task Insert(T tuple) { - throw new NotImplementedException(); + var result = await _schema.Connection.SendRequest, T>(new InsertRequest(Id, tuple)); + return result.Data[0]; } - public Task Insert(ref TInsertable tuple) + public Task Insert(in TInsertable tuple) { throw new NotImplementedException(); } diff --git a/src/progaudi.tarantool/SpaceMeta.cs b/src/progaudi.tarantool/SpaceMeta.cs index 55f6c5bc..db1bb8e6 100644 --- a/src/progaudi.tarantool/SpaceMeta.cs +++ b/src/progaudi.tarantool/SpaceMeta.cs @@ -1,32 +1,35 @@ -using ProGaudi.MsgPack.Light; +using MessagePack; using ProGaudi.Tarantool.Client.Model; -using ProGaudi.Tarantool.Client.Model.Enums; namespace ProGaudi.Tarantool.Client { - // TODO: fix serializer to support struct. - [MsgPackArray] - public class SpaceMeta + [MessagePackObject] + public struct SpaceMeta { - [MsgPackArrayElement(0)] + [Key(0)] public uint Id { get; set; } - [MsgPackArrayElement(1)] + [Key(1)] public int OwnerId { get; set; } - [MsgPackArrayElement(2)] + [Key(2)] public string Name { get; set; } - [MsgPackArrayElement(3)] + [Key(3)] public StorageEngine Engine { get; set; } - [MsgPackArrayElement(4)] + [Key(4)] public uint FieldCount { get; set; } - [MsgPackArrayElement(5)] + [Key(5)] public SpaceOptions Options { get; set; } - [MsgPackArrayElement(6)] + [Key(6)] public SpaceField[] Fields { get; set; } + + public override string ToString() + { + return $"{Name}({Id}, {Engine}, {Options.Temporary})."; + } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs b/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs deleted file mode 100644 index dfee34e8..00000000 --- a/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs +++ /dev/null @@ -1,69 +0,0 @@ -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 -{ - public class TarantoolConvertersRegistrator - { - public static void Register(MsgPackContext context) - { - context.RegisterConverter(new EnumConverter()); - 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 StringSliceOperationConverter()); - context.RegisterGenericConverter(typeof(UpdateOperationConverter<>)); - - context.RegisterConverter(new ResponseHeaderConverter()); - context.RegisterConverter(new RequestHeaderConverter()); - - context.RegisterConverter(new AuthenticationPacketConverter()); - context.RegisterConverter(new EmptyResponseConverter()); - context.RegisterConverter(new ErrorResponsePacketConverter()); - - context.RegisterConverter(new SpaceFieldConverter()); - context.RegisterConverter(new IndexPartConverter()); - context.RegisterConverter(new IndexCreationOptionsConverter()); - 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(SystemTupleConverter<>)); - context.RegisterGenericConverter(typeof(SystemTupleConverter<,>)); - context.RegisterGenericConverter(typeof(SystemTupleConverter<,,>)); - context.RegisterGenericConverter(typeof(SystemTupleConverter<,,,>)); - context.RegisterGenericConverter(typeof(SystemTupleConverter<,,,,>)); - context.RegisterGenericConverter(typeof(SystemTupleConverter<,,,,,>)); - context.RegisterGenericConverter(typeof(SystemTupleConverter<,,,,,,>)); - - context.RegisterGenericConverter(typeof(ValueTupleConverter<>)); - context.RegisterGenericConverter(typeof(ValueTupleConverter<,>)); - context.RegisterGenericConverter(typeof(ValueTupleConverter<,,>)); - context.RegisterGenericConverter(typeof(ValueTupleConverter<,,,>)); - context.RegisterGenericConverter(typeof(ValueTupleConverter<,,,,>)); - context.RegisterGenericConverter(typeof(ValueTupleConverter<,,,,,>)); - context.RegisterGenericConverter(typeof(ValueTupleConverter<,,,,,,>)); - } - } -} \ 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/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 a9c42ddd..9e0c39bb 100644 --- a/src/progaudi.tarantool/progaudi.tarantool.csproj +++ b/src/progaudi.tarantool/progaudi.tarantool.csproj @@ -29,7 +29,8 @@ - + + @@ -54,16 +55,6 @@ - - SystemTupleConverters.tt - True - True - - - ValueTupleConverters.tt - True - True - True True diff --git a/src/progaudi.tarantool/progaudi.tarantool.csproj.DotSettings b/src/progaudi.tarantool/progaudi.tarantool.csproj.DotSettings index 58ad6c88..96331d1c 100644 --- a/src/progaudi.tarantool/progaudi.tarantool.csproj.DotSettings +++ b/src/progaudi.tarantool/progaudi.tarantool.csproj.DotSettings @@ -1,2 +1,2 @@  - CSharp71 \ No newline at end of file + 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..8b3b952f --- /dev/null +++ b/src/tests/Program.cs @@ -0,0 +1,77 @@ +using System; +using System.Linq; +using System.Text; +using MessagePack; +using MessagePack.Formatters; +using ProGaudi.MsgPack.Light; + +using static MessagePack.MessagePackBinary; + +namespace tests +{ + [MessagePackObject] + [MsgPackArray] + public struct A + { + [MsgPackArrayElement(0)] + [Key("W")] + public int V { get; set; } + } + + public enum B + { + C, + D + } + + [Flags] + public enum E + { + F1 = 0x1, + F2 = 0x2, + F3 = 0x4 + } + + public class CallRequest + { + public CallRequest(string functionName, T tuple) + { + FunctionName = functionName; + Tuple = tuple; + } + + public string FunctionName { get; } + + public T Tuple { get; } + + internal class Formatter : IMessagePackFormatter> + { + public int Serialize(ref byte[] bytes, int offset, CallRequest value, IFormatterResolver formatterResolver) + { + offset = WriteFixedMapHeaderUnsafe(ref bytes, offset, 2); + offset = WriteInt32ForceInt32Block(ref bytes, offset, 0x10); + offset = WriteString(ref bytes, offset, value.FunctionName); + offset = WriteInt32ForceInt32Block(ref bytes, offset, 0x11); + Console.WriteLine(true); + return formatterResolver.GetFormatter().Serialize(ref bytes, offset, value.Tuple, formatterResolver); + } + + public CallRequest Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + { + throw new NotImplementedException(); + } + } + } + + class Program + { + static void Main(string[] args) + { + //var c = new MsgPackContext(); + //c.DiscoverConverters(); + //var b = MessagePackSerializer.Serialize(new CallRequest("123", new A { V = 1 }), new PackerResolver()); + + //Console.WriteLine(b.Aggregate(new StringBuilder(), (sb, b1) => sb.AppendFormat("{0:x2} ", b1))); + } + } +} diff --git a/src/tests/tests.csproj b/src/tests/tests.csproj new file mode 100644 index 00000000..96e43eab --- /dev/null +++ b/src/tests/tests.csproj @@ -0,0 +1,21 @@ + + + + Exe + netcoreapp2.0 + + + + 7.2 + + + + 7.2 + + + + + + + + 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 From b46e3743cdb5b4bd8b603453e26bc23d5cfb4435 Mon Sep 17 00:00:00 2001 From: aensidhe Date: Tue, 12 Jun 2018 21:27:08 +0300 Subject: [PATCH 06/11] Make structs readonly --- src/progaudi.tarantool/IndexMeta.cs | 50 +++++++++++++++++---- src/progaudi.tarantool/Model/RequestId.cs | 22 ++++++++- src/progaudi.tarantool/ResponseReader.cs | 6 +-- src/progaudi.tarantool/SpaceMeta.cs | 54 +++++++++++++++++++---- 4 files changed, 107 insertions(+), 25 deletions(-) diff --git a/src/progaudi.tarantool/IndexMeta.cs b/src/progaudi.tarantool/IndexMeta.cs index cebb952f..b1a0e679 100644 --- a/src/progaudi.tarantool/IndexMeta.cs +++ b/src/progaudi.tarantool/IndexMeta.cs @@ -1,34 +1,66 @@ -using System.Collections.Generic; +using System; using MessagePack; using ProGaudi.Tarantool.Client.Model; namespace ProGaudi.Tarantool.Client { - // TODO: fix serializer to support struct. [MessagePackObject] - public struct IndexMeta + public readonly struct 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; + } + [Key(0)] - public uint SpaceId { get; set; } + public uint SpaceId { get; } [Key(1)] - public uint Id { get; set; } + public uint Id { get; } [Key(2)] - public string Name { get; set; } + public string Name { get; } [Key(3)] - public IndexType Type { get; set; } + public IndexType Type { get; } [Key(4)] - public IndexCreationOptions Options { get; set; } + public IndexCreationOptions Options { get; } [Key(5)] - public IndexPart[] Parts { get; set; } + public IndexPart[] Parts { get; } 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/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/ResponseReader.cs b/src/progaudi.tarantool/ResponseReader.cs index 904567df..89eb09c5 100644 --- a/src/progaudi.tarantool/ResponseReader.cs +++ b/src/progaudi.tarantool/ResponseReader.cs @@ -288,11 +288,7 @@ private byte[] TryParseResponse() return null; } - var headerSizeBuffer = new byte[Constants.PacketSizeBufferSize]; - Array.Copy(_buffer, _parsingOffset, headerSizeBuffer, 0, Constants.PacketSizeBufferSize); - var packetSize = (int)MessagePackBinary.ReadUInt64(_buffer, _parsingOffset, out _); - - return packetSize; + return (int)MessagePackBinary.ReadUInt64(_buffer, _parsingOffset, out _); } private bool PacketCompletelyRead(int packetSize) diff --git a/src/progaudi.tarantool/SpaceMeta.cs b/src/progaudi.tarantool/SpaceMeta.cs index db1bb8e6..800dbc40 100644 --- a/src/progaudi.tarantool/SpaceMeta.cs +++ b/src/progaudi.tarantool/SpaceMeta.cs @@ -1,35 +1,71 @@ -using MessagePack; +using System; +using MessagePack; using ProGaudi.Tarantool.Client.Model; namespace ProGaudi.Tarantool.Client { [MessagePackObject] - public struct SpaceMeta + public struct SpaceMeta : IEquatable { + 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; + } + [Key(0)] - public uint Id { get; set; } + public uint Id { get; } [Key(1)] - public int OwnerId { get; set; } + public int OwnerId { get; } [Key(2)] - public string Name { get; set; } + public string Name { get; } [Key(3)] - public StorageEngine Engine { get; set; } + public StorageEngine Engine { get; } [Key(4)] - public uint FieldCount { get; set; } + public uint FieldCount { get; } [Key(5)] - public SpaceOptions Options { get; set; } + public SpaceOptions Options { get; } [Key(6)] - public SpaceField[] Fields { get; set; } + public SpaceField[] Fields { get; } 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 From d2e07857549527368494de57572342d7bac65599 Mon Sep 17 00:00:00 2001 From: aensidhe Date: Tue, 12 Jun 2018 22:21:30 +0300 Subject: [PATCH 07/11] It seems, that changing MessagePack Serializer does not change anything. --- .dockerignore | 7 ++++ Dockerfile | 14 ++++++-- Dockerfile.alpine | 16 +++++++++ docker-compose.yml | 20 +++++++++++ progaudi.tarantool.sln | 28 ---------------- samples/go-insert/Dockerfile | 9 +++++ samples/go-insert/app.go | 33 +++++++++++++++++++ samples/insert-performance/Program.cs | 4 ++- .../insert-performance.csproj | 2 +- .../NetworkStreamPhysicalConnection.cs | 17 +++------- 10 files changed, 105 insertions(+), 45 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile.alpine create mode 100644 samples/go-insert/Dockerfile create mode 100644 samples/go-insert/app.go 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/docker-compose.yml b/docker-compose.yml index ef22a4b1..681056de 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,3 +38,23 @@ services: # 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_8 diff --git a/progaudi.tarantool.sln b/progaudi.tarantool.sln index bb674d63..b39dda44 100644 --- a/progaudi.tarantool.sln +++ b/progaudi.tarantool.sln @@ -10,12 +10,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution global.json = global.json EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "progaudi.tarantool.benchmark", "src\progaudi.tarantool.benchmark\progaudi.tarantool.benchmark.csproj", "{CD96AC2D-505F-4FDF-82FB-76F51CC28FF4}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "insert-performance", "samples\insert-performance\insert-performance.csproj", "{9A42ABC2-AA01-4A89-9DBF-ACBBC7E0F461}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tests", "src\tests\tests.csproj", "{4B1516E4-71FF-479B-A821-203FF2C97582}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -38,18 +34,6 @@ 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 - {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 @@ -62,18 +46,6 @@ Global {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 - {4B1516E4-71FF-479B-A821-203FF2C97582}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4B1516E4-71FF-479B-A821-203FF2C97582}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4B1516E4-71FF-479B-A821-203FF2C97582}.Debug|x64.ActiveCfg = Debug|Any CPU - {4B1516E4-71FF-479B-A821-203FF2C97582}.Debug|x64.Build.0 = Debug|Any CPU - {4B1516E4-71FF-479B-A821-203FF2C97582}.Debug|x86.ActiveCfg = Debug|Any CPU - {4B1516E4-71FF-479B-A821-203FF2C97582}.Debug|x86.Build.0 = Debug|Any CPU - {4B1516E4-71FF-479B-A821-203FF2C97582}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4B1516E4-71FF-479B-A821-203FF2C97582}.Release|Any CPU.Build.0 = Release|Any CPU - {4B1516E4-71FF-479B-A821-203FF2C97582}.Release|x64.ActiveCfg = Release|Any CPU - {4B1516E4-71FF-479B-A821-203FF2C97582}.Release|x64.Build.0 = Release|Any CPU - {4B1516E4-71FF-479B-A821-203FF2C97582}.Release|x86.ActiveCfg = Release|Any CPU - {4B1516E4-71FF-479B-A821-203FF2C97582}.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 index 9775411a..0a2efaa0 100644 --- a/samples/insert-performance/Program.cs +++ b/samples/insert-performance/Program.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Threading.Tasks; using ProGaudi.Tarantool.Client; using ProGaudi.Tarantool.Client.Model; @@ -11,7 +12,7 @@ class Program static void Main() { var log = new TextWriterLog(Console.Out); - var options = new ClientOptions("localhost:3301"); + var options = new ClientOptions(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "localhost:3301" : "tarantool_1_8:3301"); try { using (var box = new Box(options)) @@ -28,6 +29,7 @@ static void Main() if (i % 1000 == 999) { Task.WaitAll(lst); + //return; } if (i % 10000 == 9999) diff --git a/samples/insert-performance/insert-performance.csproj b/samples/insert-performance/insert-performance.csproj index 1cc692ee..77f3daa8 100644 --- a/samples/insert-performance/insert-performance.csproj +++ b/samples/insert-performance/insert-performance.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.0 + netcoreapp2.1 diff --git a/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs b/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs index be8b04f7..8202e9a8 100644 --- a/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs +++ b/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Net.Sockets; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; @@ -37,9 +38,10 @@ 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."); @@ -63,18 +65,9 @@ public async Task ReadAsync(byte[] buffer, int offset, int count) return await _stream.ReadAsync(buffer, offset, count).ConfigureAwait(false); } - 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); - } - public bool IsConnected => !_disposed && _stream != null; + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void CheckConnectionStatus() { if (_disposed) From abcd0b1a25140c0721972ee75e4378cd44a48f65 Mon Sep 17 00:00:00 2001 From: aensidhe Date: Mon, 18 Jun 2018 14:24:07 +0300 Subject: [PATCH 08/11] 2x gains or not? --- global.json | 2 +- progaudi.tarantool.sln | 14 ++ samples/insert-performance/Program.cs | 7 +- src/progaudi.tarantool/AwaitableSocket.cs | 131 +++++++++++++++++ src/progaudi.tarantool/IPhysicalConnection.cs | 5 +- src/progaudi.tarantool/IRequestWriter.cs | 2 +- src/progaudi.tarantool/IResponseReader.cs | 1 - src/progaudi.tarantool/LogicalConnection.cs | 43 ++++-- .../NetworkStreamPhysicalConnection.cs | 6 +- src/progaudi.tarantool/RequestWriter.cs | 41 ++---- src/progaudi.tarantool/ResponseReader.cs | 137 +++++++++++++----- .../SocketPhysicalConnection.cs | 66 +++++++++ .../progaudi.tarantool.csproj | 11 +- src/tests/Program.cs | 96 +++++------- src/tests/tests.csproj | 17 +-- 15 files changed, 417 insertions(+), 162 deletions(-) create mode 100644 src/progaudi.tarantool/AwaitableSocket.cs create mode 100644 src/progaudi.tarantool/SocketPhysicalConnection.cs diff --git a/global.json b/global.json index 386035de..d65e3d5d 100644 --- a/global.json +++ b/global.json @@ -2,4 +2,4 @@ "sdk": { "version": "2.1.300" } -} \ No newline at end of file +} diff --git a/progaudi.tarantool.sln b/progaudi.tarantool.sln index b39dda44..e33c0bd9 100644 --- a/progaudi.tarantool.sln +++ b/progaudi.tarantool.sln @@ -12,6 +12,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject 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 Debug|Any CPU = Debug|Any CPU @@ -46,6 +48,18 @@ Global {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/insert-performance/Program.cs b/samples/insert-performance/Program.cs index 0a2efaa0..02d25ee4 100644 --- a/samples/insert-performance/Program.cs +++ b/samples/insert-performance/Program.cs @@ -18,8 +18,7 @@ static void Main() using (var box = new Box(options)) { box.Connect().GetAwaiter().GetResult(); - var schema = box.GetSchema(); - schema.TryGetSpace<(int, (int, int), int)>("pivot", out var space); + box.Schema.TryGetSpace<(int, (int, int), int)>("pivot", out var space); var lst = new Task[1000]; var sw = Stopwatch.StartNew(); for (var i = 0; i < 1_000_000; i++) @@ -35,6 +34,10 @@ static void Main() if (i % 10000 == 9999) { Console.Write("*"); + if (i % 100000 == 99999) + { + Console.WriteLine(); + } } } sw.Stop(); 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/IPhysicalConnection.cs b/src/progaudi.tarantool/IPhysicalConnection.cs index 65c8c9d6..0baef61a 100644 --- a/src/progaudi.tarantool/IPhysicalConnection.cs +++ b/src/progaudi.tarantool/IPhysicalConnection.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; @@ -14,6 +15,8 @@ public interface IPhysicalConnection : IDisposable Task ReadAsync(byte[] buffer, int offset, int count); - void Write(byte[] buffer, int offset, int count); + void Write(in ArraySegment 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 5f8659c1..928e4dc7 100644 --- a/src/progaudi.tarantool/IRequestWriter.cs +++ b/src/progaudi.tarantool/IRequestWriter.cs @@ -8,6 +8,6 @@ internal interface IRequestWriter : IDisposable bool IsConnected { get; } - void Write(byte[] header, byte[] body); + void Write(in ArraySegment body); } } \ No newline at end of file diff --git a/src/progaudi.tarantool/IResponseReader.cs b/src/progaudi.tarantool/IResponseReader.cs index 1726afd5..7c3c6e87 100644 --- a/src/progaudi.tarantool/IResponseReader.cs +++ b/src/progaudi.tarantool/IResponseReader.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; diff --git a/src/progaudi.tarantool/LogicalConnection.cs b/src/progaudi.tarantool/LogicalConnection.cs index 11f3a894..51db08fd 100644 --- a/src/progaudi.tarantool/LogicalConnection.cs +++ b/src/progaudi.tarantool/LogicalConnection.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -99,17 +100,21 @@ public Task SendRequestWithEmptyResponse(TRequest request, TimeSpan? t public async Task> SendRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { - var (result, bodyStart) = await SendRequestImpl(request, timeout).ConfigureAwait(false); + var (buffer, bodyStart) = await SendRequestImpl(request, timeout).ConfigureAwait(false); var formatter = MessagePackSerializer.DefaultResolver.GetFormatter>(); - return formatter.Deserialize(result, bodyStart, MessagePackSerializer.DefaultResolver, out _); + var result = formatter.Deserialize(buffer, bodyStart, MessagePackSerializer.DefaultResolver, out _); + ArrayPool.Shared.Return(buffer); + return result; } public async Task SendRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { - var (result, bodyStart) = await SendRequestImpl(request, timeout).ConfigureAwait(false); + var (buffer, bodyStart) = await SendRequestImpl(request, timeout).ConfigureAwait(false); var formatter = MessagePackSerializer.DefaultResolver.GetFormatter(); - return formatter.Deserialize(result, bodyStart, MessagePackSerializer.DefaultResolver, out _); + var result = formatter.Deserialize(buffer, bodyStart, MessagePackSerializer.DefaultResolver, out _); + ArrayPool.Shared.Return(buffer); + return result; } public async Task SendRawRequest(TRequest request, TimeSpan? timeout = null) @@ -142,13 +147,22 @@ private async Task LoginIfNotGuest(GreetingsResponse greetings) throw new ObjectDisposedException(nameof(LogicalConnection)); } - var bodyBuffer = MessagePackSerializer.Serialize(request); + var originalBuffer = ArrayPool.Shared.Rent(40000); + var bodyBuffer = originalBuffer; + var offset = 5; var requestId = _requestIdCounter.GetRequestId(); + offset += MessagePackBinary.WriteFixedMapHeaderUnsafe(ref bodyBuffer, offset, 2); + offset += MessagePackBinary.WriteUInt32(ref bodyBuffer, offset, Keys.Code); + offset += MessagePackBinary.WriteUInt32(ref bodyBuffer, offset, (uint) request.Code); + offset += MessagePackBinary.WriteUInt32(ref bodyBuffer, offset, Keys.Sync); + offset += MessagePackBinary.WriteUInt64(ref bodyBuffer, offset, requestId.Value); + + var formatter = MessagePackSerializer.DefaultResolver.GetFormatter(); + offset += formatter.Serialize(ref bodyBuffer, offset, request, MessagePackSerializer.DefaultResolver); + MessagePackBinary.WriteUInt32ForceUInt32Block(ref bodyBuffer, 0, (uint) (offset - 5)); var responseTask = _responseReader.GetResponseTask(requestId); - - var headerBuffer = MessagePackSerializer.Serialize(new RequestHeader(request.Code, requestId)); - _requestWriter.Write(headerBuffer, bodyBuffer); + _requestWriter.Write(new ArraySegment(bodyBuffer, 0, offset)); try { @@ -158,14 +172,15 @@ private async Task LoginIfNotGuest(GreetingsResponse greetings) responseTask = responseTask.WithCancellation(cts.Token); } - var (result, bodyStart) = await responseTask.ConfigureAwait(false); - _logWriter?.WriteLine($"Response with requestId {requestId} is recieved, length: {result.Length}."); + var result = await responseTask.ConfigureAwait(false); + _logWriter?.WriteLine($"Response with requestId {requestId} is recieved, length: {result.result.Length}."); - return (result, bodyStart); + return result; } catch (ArgumentException) { - _logWriter?.WriteLine($"Response with requestId {requestId} failed, header:\n{headerBuffer.ToReadableString()} \n body: \n{bodyBuffer.ToReadableString()}"); + _logWriter?.WriteLine( + $"Response with requestId {requestId} failed, body: \n{bodyBuffer.ToReadableString()}"); throw; } catch (TimeoutException) @@ -173,6 +188,10 @@ private async Task LoginIfNotGuest(GreetingsResponse greetings) PingsFailedByTimeoutCount++; throw; } + finally + { + ArrayPool.Shared.Return(originalBuffer); + } } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs b/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs index 8202e9a8..aabbe5b2 100644 --- a/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs +++ b/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs @@ -47,12 +47,14 @@ public async Task Connect(ClientOptions options) options.LogWriter?.WriteLine("Socket connection established."); } - public void Write(byte[] buffer, int offset, int count) + public void Write(in ArraySegment buffer) { CheckConnectionStatus(); - _stream.Write(buffer, offset, count); + _stream.Write(buffer.Array, buffer.Offset, buffer.Count); } + public Stream Stream => _stream; + public async Task Flush() { CheckConnectionStatus(); diff --git a/src/progaudi.tarantool/RequestWriter.cs b/src/progaudi.tarantool/RequestWriter.cs index 00b4246b..daa8d5c7 100644 --- a/src/progaudi.tarantool/RequestWriter.cs +++ b/src/progaudi.tarantool/RequestWriter.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Threading; -using MessagePack; using ProGaudi.Tarantool.Client.Model; namespace ProGaudi.Tarantool.Client @@ -10,7 +9,7 @@ internal class RequestWriter : IRequestWriter { private readonly ClientOptions _clientOptions; private readonly IPhysicalConnection _physicalConnection; - private readonly Queue> _buffer; + private readonly Queue> _buffer; private readonly object _lock = new object(); private readonly Thread _thread; private readonly ManualResetEventSlim _exitEvent; @@ -21,7 +20,7 @@ public RequestWriter(ClientOptions clientOptions, IPhysicalConnection physicalCo { _clientOptions = clientOptions; _physicalConnection = physicalConnection; - _buffer = new Queue>(); + _buffer = new Queue>(); _thread = new Thread(WriteFunction) { IsBackground = true, @@ -35,7 +34,7 @@ public void BeginWriting() { if (_disposed) { - throw new ObjectDisposedException(nameof(ResponseReader)); + throw new ObjectDisposedException(nameof(RequestWriter)); } _clientOptions?.LogWriter?.WriteLine("Starting thread"); @@ -44,18 +43,18 @@ public void BeginWriting() public bool IsConnected => !_disposed; - public void Write(byte[] header, byte[] body) + public void Write(in ArraySegment body) { if (_disposed) { - throw new ObjectDisposedException(nameof(ResponseReader)); + throw new ObjectDisposedException(nameof(RequestWriter)); } - _clientOptions?.LogWriter?.WriteLine($"Enqueuing request: headers {header.Length} bytes, body {body.Length} bytes."); + _clientOptions?.LogWriter?.WriteLine($"Enqueuing request: body {body.Count} bytes."); bool shouldSignal; lock (_lock) { - _buffer.Enqueue(Tuple.Create(header, body)); + _buffer.Enqueue(body); shouldSignal = _buffer.Count == 1; } @@ -98,31 +97,23 @@ private void WriteFunction() private void WriteRequests(int limit) { - Tuple GetRequest() + var count = 0; + while (true) { + ArraySegment request; lock (_lock) { if (_buffer.Count > 0) - return _buffer.Dequeue(); + request = _buffer.Dequeue(); + else + break; } - return null; - } - - Tuple request; - var count = 0; - while ((request = GetRequest()) != null) - { - var header = request.Item1; - var body = request.Item2; - _clientOptions?.LogWriter?.WriteLine($"Writing request: headers {header.Length} bytes, body {body.Length} bytes."); + _clientOptions?.LogWriter?.WriteLine($"Writing request: {request.Count} bytes."); - var length = MessagePackSerializer.Serialize(new RequestLength(header, body)); - _physicalConnection.Write(length, 0, length.Length); - _physicalConnection.Write(header, 0, header.Length); - _physicalConnection.Write(body, 0, body.Length); + _physicalConnection.Write(request); - _clientOptions?.LogWriter?.WriteLine($"Wrote request: headers {header.Length} bytes, body {body.Length} bytes."); + _clientOptions?.LogWriter?.WriteLine($"Wrote request: {request.Count} bytes."); count++; if (limit > 0 && count > limit) diff --git a/src/progaudi.tarantool/ResponseReader.cs b/src/progaudi.tarantool/ResponseReader.cs index 89eb09c5..e5d413f1 100644 --- a/src/progaudi.tarantool/ResponseReader.cs +++ b/src/progaudi.tarantool/ResponseReader.cs @@ -1,5 +1,7 @@ using System; +using System.Buffers; using System.Collections.Generic; +using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -12,6 +14,14 @@ namespace ProGaudi.Tarantool.Client { internal class ResponseReader : IResponseReader { + private static readonly AsyncCallback EndRead = result => + { + ResponseReader physical; + if (result.CompletedSynchronously || (physical = result.AsyncState as ResponseReader) == null) return; + if (physical.EndReading(result)) + physical.BeginReading(); + }; + private readonly IPhysicalConnection _physicalConnection; private readonly Dictionary> _pendingRequests = @@ -20,6 +30,7 @@ internal class ResponseReader : IResponseReader private readonly ReaderWriterLockSlim _pendingRequestsLock = new ReaderWriterLockSlim(); private readonly ClientOptions _clientOptions; + private readonly byte[] _originalBuffer; private byte[] _buffer; @@ -29,11 +40,39 @@ internal class ResponseReader : IResponseReader private bool _disposed; + private readonly Thread _thread; + + private readonly ManualResetEventSlim _exitEvent; + public ResponseReader(ClientOptions clientOptions, IPhysicalConnection physicalConnection) { _physicalConnection = physicalConnection; _clientOptions = clientOptions; - _buffer = new byte[clientOptions.ConnectionOptions.ReadStreamBufferSize]; + _originalBuffer = _buffer = ArrayPool.Shared.Rent(1024 * 1024); + _thread = new Thread(ReadingFunc) + { + IsBackground = true, + Name = $"Tarantool :: {clientOptions.Name} :: Write" + }; + _exitEvent = new ManualResetEventSlim(false); + } + + private void ReadingFunc() + { + while (true) + { + if (_exitEvent.IsSet) + { + return; + } + + var result = _physicalConnection.Stream.BeginRead(_buffer, 0, _buffer.Length, EndRead, this); + if (result.CompletedSynchronously) + { + if (!EndReading(result)) + return; + } + } } public bool IsConnected => !_disposed; @@ -46,37 +85,36 @@ public void Dispose() } _disposed = true; + _disposed = true; + _exitEvent.Set(); + _thread.Join(); + _exitEvent.Dispose(); - 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) { - response.SetException(new ObjectDisposedException(nameof(ResponseReader))); + response.SetException(exception); } _pendingRequests.Clear(); } - finally - { - _pendingRequestsLock.ExitWriteLock(); - } } public Task<(byte[] result, int bodyStart)> GetResponseTask(RequestId requestId) { - 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); @@ -87,43 +125,65 @@ public void Dispose() return tcs.Task; } - finally - { - _pendingRequestsLock.ExitWriteLock(); - } } public void BeginReading() { - var freeBufferSpace = EnsureSpaceAndComputeBytesToRead(); - - _clientOptions.LogWriter?.WriteLine($"Begin reading from connection to buffer, bytes count: {freeBufferSpace}"); + if (_disposed) + { + throw new ObjectDisposedException(nameof(ResponseReader)); + } - var readingTask = _physicalConnection.ReadAsync(_buffer, _readingOffset, freeBufferSpace); - readingTask.ContinueWith(EndReading); + try + { + bool keepReading; + do + { + keepReading = false; + var space = EnsureSpaceAndComputeBytesToRead(); + var result = _physicalConnection.Stream.BeginRead(_buffer, _readingOffset, space, EndRead, this); + if (result.CompletedSynchronously) + { + keepReading = EndReading(result); + } + } while (keepReading); + } + catch (IOException ex) + { + _clientOptions?.LogWriter?.WriteLine("Could not connect: " + ex.Message); + } } private TaskCompletionSource<(byte[] result, int bodyStart)> PopResponseCompletionSource(RequestId requestId) { - try + if (_disposed) { - _pendingRequestsLock.EnterWriteLock(); + throw new ObjectDisposedException(nameof(ResponseReader)); + } - if (_disposed) - { - throw new ObjectDisposedException(nameof(ResponseReader)); - } + TaskCompletionSource<(byte[] result, int bodyStart)> request; - if (_pendingRequests.TryGetValue(requestId, out var request)) + lock (_pendingRequestsLock) + { + if (_pendingRequests.TryGetValue(requestId, out request)) { _pendingRequests.Remove(requestId); } + } - return request; + return request; + } + + private bool EndReading(IAsyncResult ar) + { + try + { + var bytesRead = _physicalConnection?.Stream.EndRead(ar) ?? 0; + return ProcessReadBytes(bytesRead); } - finally + catch (Exception) { - _pendingRequestsLock.ExitWriteLock(); + return false; } } @@ -158,9 +218,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(); @@ -268,11 +330,10 @@ private byte[] TryParseResponse() if (PacketCompletelyRead(packetSize.Value)) { _parsingOffset += Constants.PacketSizeBufferSize; - var responseBuffer = new byte[packetSize.Value]; + var responseBuffer = ArrayPool.Shared.Rent(packetSize.Value); Array.Copy(_buffer, _parsingOffset, responseBuffer, 0, packetSize.Value); _parsingOffset += packetSize.Value; - return responseBuffer; } @@ -304,6 +365,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/SocketPhysicalConnection.cs b/src/progaudi.tarantool/SocketPhysicalConnection.cs new file mode 100644 index 00000000..c58a0f83 --- /dev/null +++ b/src/progaudi.tarantool/SocketPhysicalConnection.cs @@ -0,0 +1,66 @@ +using System; +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 ArraySegment 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/progaudi.tarantool.csproj b/src/progaudi.tarantool/progaudi.tarantool.csproj index 9e0c39bb..10f13360 100644 --- a/src/progaudi.tarantool/progaudi.tarantool.csproj +++ b/src/progaudi.tarantool/progaudi.tarantool.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -27,12 +27,11 @@ true true - - - + + @@ -71,8 +70,4 @@ ValueTupleConverters.tt - - - $(DefineConstants);PROGAUDI_NETCORE - \ No newline at end of file diff --git a/src/tests/Program.cs b/src/tests/Program.cs index 8b3b952f..9b884eda 100644 --- a/src/tests/Program.cs +++ b/src/tests/Program.cs @@ -1,77 +1,49 @@ using System; -using System.Linq; +using System.Net; +using System.Net.Sockets; using System.Text; -using MessagePack; -using MessagePack.Formatters; -using ProGaudi.MsgPack.Light; - -using static MessagePack.MessagePackBinary; +using System.Threading; +using System.Threading.Tasks; +using Npgsql; namespace tests { - [MessagePackObject] - [MsgPackArray] - public struct A - { - [MsgPackArrayElement(0)] - [Key("W")] - public int V { get; set; } - } - - public enum B + internal class Program { - C, - D - } + private static void Main() => MainAsync().GetAwaiter().GetResult(); - [Flags] - public enum E - { - F1 = 0x1, - F2 = 0x2, - F3 = 0x4 - } - - public class CallRequest - { - public CallRequest(string functionName, T tuple) + private static async Task MainAsync() { - FunctionName = functionName; - Tuple = tuple; - } - - public string FunctionName { get; } - - public T Tuple { get; } - - internal class Formatter : IMessagePackFormatter> - { - public int Serialize(ref byte[] bytes, int offset, CallRequest value, IFormatterResolver formatterResolver) + try { - offset = WriteFixedMapHeaderUnsafe(ref bytes, offset, 2); - offset = WriteInt32ForceInt32Block(ref bytes, offset, 0x10); - offset = WriteString(ref bytes, offset, value.FunctionName); - offset = WriteInt32ForceInt32Block(ref bytes, offset, 0x11); - Console.WriteLine(true); - return formatterResolver.GetFormatter().Serialize(ref bytes, offset, value.Tuple, formatterResolver); + 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])); } - - public CallRequest Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + catch (Exception e) { - throw new NotImplementedException(); + Console.WriteLine(e); } } } - - class Program - { - static void Main(string[] args) - { - //var c = new MsgPackContext(); - //c.DiscoverConverters(); - //var b = MessagePackSerializer.Serialize(new CallRequest("123", new A { V = 1 }), new PackerResolver()); - - //Console.WriteLine(b.Aggregate(new StringBuilder(), (sb, b1) => sb.AppendFormat("{0:x2} ", b1))); - } - } } diff --git a/src/tests/tests.csproj b/src/tests/tests.csproj index 96e43eab..3d9c9fe9 100644 --- a/src/tests/tests.csproj +++ b/src/tests/tests.csproj @@ -1,16 +1,9 @@ - + Exe - netcoreapp2.0 - - - - 7.2 - - - - 7.2 + netcoreapp2.1 + latest @@ -18,4 +11,8 @@ + + + + From 19a75a0158f30a949bea470c51acacf283462359 Mon Sep 17 00:00:00 2001 From: aensidhe Date: Mon, 18 Jun 2018 21:01:39 +0300 Subject: [PATCH 09/11] Remove array copy. --- samples/insert-performance/Program.cs | 18 ++- src/progaudi.tarantool/ILogicalConnection.cs | 3 +- src/progaudi.tarantool/IResponseReader.cs | 2 +- src/progaudi.tarantool/LogicalConnection.cs | 53 ++++--- .../LogicalConnectionManager.cs | 3 +- src/progaudi.tarantool/RequestWriter.cs | 2 + src/progaudi.tarantool/ResponseReader.cs | 133 +++++------------- 7 files changed, 83 insertions(+), 131 deletions(-) diff --git a/samples/insert-performance/Program.cs b/samples/insert-performance/Program.cs index 02d25ee4..714c9a26 100644 --- a/samples/insert-performance/Program.cs +++ b/samples/insert-performance/Program.cs @@ -12,7 +12,8 @@ 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)) @@ -20,11 +21,11 @@ static void Main() box.Connect().GetAwaiter().GetResult(); box.Schema.TryGetSpace<(int, (int, int), int)>("pivot", out var space); var lst = new Task[1000]; - var sw = Stopwatch.StartNew(); + 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); @@ -40,16 +41,19 @@ static void Main() } } } - sw.Stop(); - - Console.WriteLine(); - Console.WriteLine(sw.ElapsedMilliseconds); } } catch (Exception e) { Console.WriteLine(e); } + finally + { + sw.Stop(); + + Console.WriteLine(); + Console.WriteLine(sw.ElapsedMilliseconds); + } } } } diff --git a/src/progaudi.tarantool/ILogicalConnection.cs b/src/progaudi.tarantool/ILogicalConnection.cs index 76439d7d..9a5b73ab 100644 --- a/src/progaudi.tarantool/ILogicalConnection.cs +++ b/src/progaudi.tarantool/ILogicalConnection.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; @@ -19,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/IResponseReader.cs b/src/progaudi.tarantool/IResponseReader.cs index 7c3c6e87..9b3bdc75 100644 --- a/src/progaudi.tarantool/IResponseReader.cs +++ b/src/progaudi.tarantool/IResponseReader.cs @@ -9,7 +9,7 @@ public interface IResponseReader : IDisposable { void BeginReading(); - Task<(byte[] result, int bodyStart)> GetResponseTask(RequestId requestId); + Task GetResponseTask(RequestId requestId, Func, TResponse> responseCreator); bool IsConnected { get; } } diff --git a/src/progaudi.tarantool/LogicalConnection.cs b/src/progaudi.tarantool/LogicalConnection.cs index 51db08fd..b7eecb6b 100644 --- a/src/progaudi.tarantool/LogicalConnection.cs +++ b/src/progaudi.tarantool/LogicalConnection.cs @@ -11,6 +11,13 @@ namespace ProGaudi.Tarantool.Client { internal class LogicalConnection : ILogicalConnection { + private static readonly Func, IMemoryOwner> RawByteCreator = x => + { + var owner = MemoryPool.Shared.Rent(x.Count); + x.AsMemory().CopyTo(owner.Memory); + return owner; + }; + private readonly ClientOptions _clientOptions; private readonly RequestIdCounter _requestIdCounter; @@ -94,33 +101,33 @@ public bool IsConnected() public Task SendRequestWithEmptyResponse(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { - return SendRequestImpl(request, timeout); + 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 (buffer, bodyStart) = await SendRequestImpl(request, timeout).ConfigureAwait(false); - var formatter = MessagePackSerializer.DefaultResolver.GetFormatter>(); - var result = formatter.Deserialize(buffer, bodyStart, MessagePackSerializer.DefaultResolver, out _); - ArrayPool.Shared.Return(buffer); - return result; + return SendRequestImpl(request, buffer => + { + var formatter = MessagePackSerializer.DefaultResolver.GetFormatter>(); + return formatter.Deserialize(buffer.Array, buffer.Offset, MessagePackSerializer.DefaultResolver, out _); + }, timeout); } - public async Task SendRequest(TRequest request, TimeSpan? timeout = null) + public Task SendRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { - var (buffer, bodyStart) = await SendRequestImpl(request, timeout).ConfigureAwait(false); - var formatter = MessagePackSerializer.DefaultResolver.GetFormatter(); - var result = formatter.Deserialize(buffer, bodyStart, MessagePackSerializer.DefaultResolver, out _); - ArrayPool.Shared.Return(buffer); - return result; + return SendRequestImpl(request, buffer => + { + var formatter = MessagePackSerializer.DefaultResolver.GetFormatter(); + return formatter.Deserialize(buffer.Array, buffer.Offset, MessagePackSerializer.DefaultResolver, out _); + }, 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)).result; + return SendRequestImpl(request, RawByteCreator, timeout); } private async Task LoginIfNotGuest(GreetingsResponse greetings) @@ -139,7 +146,7 @@ private async Task LoginIfNotGuest(GreetingsResponse greetings) _clientOptions.LogWriter?.WriteLine($"Authentication request send: {authenticateRequest}"); } - private async Task<(byte[] result, int bodyStart)> SendRequestImpl(TRequest request, TimeSpan? timeout) + private Task SendRequestImpl(TRequest request, Func, TResponse> response, TimeSpan? timeout) where TRequest : IRequest { if (_disposed) @@ -161,7 +168,7 @@ private async Task LoginIfNotGuest(GreetingsResponse greetings) var formatter = MessagePackSerializer.DefaultResolver.GetFormatter(); offset += formatter.Serialize(ref bodyBuffer, offset, request, MessagePackSerializer.DefaultResolver); MessagePackBinary.WriteUInt32ForceUInt32Block(ref bodyBuffer, 0, (uint) (offset - 5)); - var responseTask = _responseReader.GetResponseTask(requestId); + var responseTask = _responseReader.GetResponseTask(requestId, response); _requestWriter.Write(new ArraySegment(bodyBuffer, 0, offset)); try @@ -172,15 +179,11 @@ private async Task LoginIfNotGuest(GreetingsResponse greetings) responseTask = responseTask.WithCancellation(cts.Token); } - var result = await responseTask.ConfigureAwait(false); - _logWriter?.WriteLine($"Response with requestId {requestId} is recieved, length: {result.result.Length}."); - - return result; + return responseTask; } catch (ArgumentException) { - _logWriter?.WriteLine( - $"Response with requestId {requestId} failed, body: \n{bodyBuffer.ToReadableString()}"); + _logWriter?.WriteLine($"Request with requestId {requestId} failed, body: \n{bodyBuffer.ToReadableString()}"); throw; } catch (TimeoutException) @@ -188,10 +191,6 @@ private async Task LoginIfNotGuest(GreetingsResponse greetings) PingsFailedByTimeoutCount++; throw; } - finally - { - ArrayPool.Shared.Return(originalBuffer); - } } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/LogicalConnectionManager.cs b/src/progaudi.tarantool/LogicalConnectionManager.cs index c727eddb..5d8d7d62 100644 --- a/src/progaudi.tarantool/LogicalConnectionManager.cs +++ b/src/progaudi.tarantool/LogicalConnectionManager.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Threading; using System.Threading.Tasks; @@ -165,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/RequestWriter.cs b/src/progaudi.tarantool/RequestWriter.cs index daa8d5c7..3cc49e01 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; @@ -112,6 +113,7 @@ private void WriteRequests(int limit) _clientOptions?.LogWriter?.WriteLine($"Writing request: {request.Count} bytes."); _physicalConnection.Write(request); + ArrayPool.Shared.Return(request.Array); _clientOptions?.LogWriter?.WriteLine($"Wrote request: {request.Count} bytes."); diff --git a/src/progaudi.tarantool/ResponseReader.cs b/src/progaudi.tarantool/ResponseReader.cs index e5d413f1..5b0c3411 100644 --- a/src/progaudi.tarantool/ResponseReader.cs +++ b/src/progaudi.tarantool/ResponseReader.cs @@ -24,8 +24,7 @@ internal class ResponseReader : IResponseReader private readonly IPhysicalConnection _physicalConnection; - private readonly Dictionary> _pendingRequests = - new Dictionary>(); + private readonly Dictionary> _pendingRequests = new Dictionary>(); private readonly ReaderWriterLockSlim _pendingRequestsLock = new ReaderWriterLockSlim(); @@ -40,39 +39,11 @@ internal class ResponseReader : IResponseReader private bool _disposed; - private readonly Thread _thread; - - private readonly ManualResetEventSlim _exitEvent; - public ResponseReader(ClientOptions clientOptions, IPhysicalConnection physicalConnection) { _physicalConnection = physicalConnection; _clientOptions = clientOptions; _originalBuffer = _buffer = ArrayPool.Shared.Rent(1024 * 1024); - _thread = new Thread(ReadingFunc) - { - IsBackground = true, - Name = $"Tarantool :: {clientOptions.Name} :: Write" - }; - _exitEvent = new ManualResetEventSlim(false); - } - - private void ReadingFunc() - { - while (true) - { - if (_exitEvent.IsSet) - { - return; - } - - var result = _physicalConnection.Stream.BeginRead(_buffer, 0, _buffer.Length, EndRead, this); - if (result.CompletedSynchronously) - { - if (!EndReading(result)) - return; - } - } } public bool IsConnected => !_disposed; @@ -86,9 +57,6 @@ public void Dispose() _disposed = true; _disposed = true; - _exitEvent.Set(); - _thread.Join(); - _exitEvent.Dispose(); ArrayPool.Shared.Return(_originalBuffer); var exception = new ObjectDisposedException(nameof(ResponseReader)); @@ -97,16 +65,20 @@ public void Dispose() { _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(exception); + value.Item2(exception); } _pendingRequests.Clear(); } } - public Task<(byte[] result, int bodyStart)> GetResponseTask(RequestId requestId) + private delegate void ResultSetter(in ArraySegment response); + + private delegate void ExceptionSetter(Exception ex); + + public Task GetResponseTask(RequestId requestId, Func, TResponse> responseCreator) { if (_disposed) { @@ -120,10 +92,16 @@ public void Dispose() throw ExceptionHelper.RequestWithSuchIdAlreadySent(requestId); } - var tcs = new TaskCompletionSource<(byte[] result, int bodyStart)>(); - _pendingRequests.Add(requestId, tcs); + var tcs = new TaskCompletionSource(); + var completionPair = Tuple.Create(ResultSetterImpl, ExceptionSetterImpl); + + _pendingRequests.Add(requestId, completionPair); return tcs.Task; + + void ResultSetterImpl(in ArraySegment response) => tcs.SetResult(responseCreator(response)); + + void ExceptionSetterImpl(Exception ex) => tcs.SetException(ex); } } @@ -154,14 +132,14 @@ public void BeginReading() } } - private TaskCompletionSource<(byte[] result, int bodyStart)> PopResponseCompletionSource(RequestId requestId) + private Tuple PopResponseCompletionSource(RequestId requestId) { if (_disposed) { throw new ObjectDisposedException(nameof(ResponseReader)); } - TaskCompletionSource<(byte[] result, int bodyStart)> request; + Tuple request; lock (_pendingRequestsLock) { @@ -187,30 +165,6 @@ private bool EndReading(IAsyncResult ar) } } - private void EndReading(Task readWork) - { - if (_disposed) - { - _clientOptions.LogWriter?.WriteLine("Attempt to end reading in disposed state... Exiting."); - return; - } - - if (readWork.Status == TaskStatus.RanToCompletion) - { - var readBytesCount = readWork.Result; - _clientOptions.LogWriter?.WriteLine($"End reading from connection, read bytes count: {readBytesCount}"); - - if (ProcessReadBytes(readBytesCount)) - { - BeginReading(); - return; - } - } - - _clientOptions.LogWriter?.WriteLine($"Connection read failed: {readWork.Exception}"); - Dispose(); - } - private bool ProcessReadBytes(int readBytesCount) { if (readBytesCount <= 0) @@ -257,7 +211,7 @@ private int TryParseResponses() do { var response = TryParseResponse(); - nonEmptyResult = response != null; + nonEmptyResult = response != Empty; if (!nonEmptyResult) { continue; @@ -269,10 +223,10 @@ private int TryParseResponses() return messageCount; } - private void MatchResult(byte[] result) + private void MatchResult(in ArraySegment result) { var headerFormatter = MessagePackSerializer.DefaultResolver.GetFormatter(); - var header = headerFormatter.Deserialize(result, 0, MessagePackSerializer.DefaultResolver, out var readSize); + var header = headerFormatter.Deserialize(result.Array, result.Offset, MessagePackSerializer.DefaultResolver, out var headerLength); var tcs = PopResponseCompletionSource(header.RequestId); if (tcs == null) @@ -286,16 +240,16 @@ private void MatchResult(byte[] result) if ((header.Code & CommandCodes.ErrorMask) == CommandCodes.ErrorMask) { var errorFormatter = MessagePackSerializer.DefaultResolver.GetFormatter(); - var errorResponse = errorFormatter.Deserialize(result, readSize, MessagePackSerializer.DefaultResolver, out _); - tcs.SetException(ExceptionHelper.TarantoolError(header, errorResponse)); + var errorResponse = errorFormatter.Deserialize(result.Array, result.Offset + headerLength, MessagePackSerializer.DefaultResolver, out _); + tcs.Item2(ExceptionHelper.TarantoolError(header, errorResponse)); } else { - tcs.SetResult((result, readSize)); + tcs.Item1(new ArraySegment(result.Array, result.Offset + headerLength, result.Count - headerLength)); } } - 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; @@ -312,44 +266,35 @@ 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(); + var packetSize = _readingOffset - _parsingOffset < Constants.PacketSizeBufferSize + ? 0 + : (int)MessagePackBinary.ReadUInt64(_buffer, _parsingOffset, out _); - if (!packetSize.HasValue) + if (packetSize == 0) { _clientOptions.LogWriter?.WriteLine($"Can't read packet length, has less than {Constants.PacketSizeBufferSize} bytes."); - return null; + return Empty; } - if (PacketCompletelyRead(packetSize.Value)) + if (PacketCompletelyRead(packetSize)) { - _parsingOffset += Constants.PacketSizeBufferSize; - var responseBuffer = ArrayPool.Shared.Rent(packetSize.Value); - Array.Copy(_buffer, _parsingOffset, responseBuffer, 0, packetSize.Value); - _parsingOffset += packetSize.Value; + var offset = _parsingOffset += Constants.PacketSizeBufferSize; + _parsingOffset += packetSize; - return responseBuffer; + return new ArraySegment(_buffer, offset, packetSize); } - _clientOptions.LogWriter?.WriteLine($"Packet with length {packetSize} is not completely read."); - - return null; - } - - private int? GetPacketSize() - { - if (_readingOffset - _parsingOffset < Constants.PacketSizeBufferSize) - { - return null; - } + _clientOptions.LogWriter?.WriteLine($"Packet with length {packetSize} is not completely read."); - return (int)MessagePackBinary.ReadUInt64(_buffer, _parsingOffset, out _); + return Empty; } private bool PacketCompletelyRead(int packetSize) From 08bd91077a12a50664f42d2691e7d76fbd607f4a Mon Sep 17 00:00:00 2001 From: aensidhe Date: Mon, 18 Jun 2018 21:29:30 +0300 Subject: [PATCH 10/11] Fix naming. --- src/progaudi.tarantool/ResponseReader.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/progaudi.tarantool/ResponseReader.cs b/src/progaudi.tarantool/ResponseReader.cs index 5b0c3411..a2f70ffd 100644 --- a/src/progaudi.tarantool/ResponseReader.cs +++ b/src/progaudi.tarantool/ResponseReader.cs @@ -16,10 +16,10 @@ internal class ResponseReader : IResponseReader { private static readonly AsyncCallback EndRead = result => { - ResponseReader physical; - if (result.CompletedSynchronously || (physical = result.AsyncState as ResponseReader) == null) return; - if (physical.EndReading(result)) - physical.BeginReading(); + ResponseReader reader; + if (result.CompletedSynchronously || (reader = result.AsyncState as ResponseReader) == null) return; + if (reader.EndReading(result)) + reader.BeginReading(); }; private readonly IPhysicalConnection _physicalConnection; From 190ca62be28666e2e9dcbdd317203fd8084c6c90 Mon Sep 17 00:00:00 2001 From: aensidhe Date: Tue, 19 Jun 2018 00:59:28 +0300 Subject: [PATCH 11/11] Return back to MsgPack.Light. --- samples/insert-performance/Program.cs | 3 +- src/progaudi.tarantool/Box.cs | 20 +- .../Converters/SystemTupleConverters.cs | 471 ++++++++++++++++++ .../Converters/ValueTupleConverters.cs | 401 +++++++++++++++ src/progaudi.tarantool/IPhysicalConnection.cs | 3 +- src/progaudi.tarantool/IRequestWriter.cs | 3 +- src/progaudi.tarantool/IResponseReader.cs | 3 +- src/progaudi.tarantool/IndexMeta.cs | 34 +- src/progaudi.tarantool/LogicalConnection.cs | 46 +- .../Model/AuthenticationRequest.cs | 47 +- src/progaudi.tarantool/Model/CallRequest.cs | 33 +- src/progaudi.tarantool/Model/ClientOptions.cs | 17 +- src/progaudi.tarantool/Model/DataResponse.cs | 155 ++++-- .../Model/EnumAsStringFormatter.cs | 78 ++- src/progaudi.tarantool/Model/EnumConverter.cs | 98 ++++ src/progaudi.tarantool/Model/EnumResolver.cs | 43 -- src/progaudi.tarantool/Model/FieldMetadata.cs | 45 -- .../Model/IndexCreationOptions.cs | 12 +- src/progaudi.tarantool/Model/IndexPart.cs | 131 +++-- .../Model/InsertReplaceRequest.cs | 34 +- .../Model/PackerResolver.cs | 70 --- src/progaudi.tarantool/Model/RequestHeader.cs | 43 +- .../Model/ResponseHeader.cs | 52 +- src/progaudi.tarantool/Model/SelectRequest.cs | 62 ++- src/progaudi.tarantool/Model/SpaceField.cs | 16 +- src/progaudi.tarantool/Model/SpaceOptions.cs | 10 +- src/progaudi.tarantool/Model/SqlInfo.cs | 48 +- .../NetworkStreamPhysicalConnection.cs | 6 +- src/progaudi.tarantool/RequestLength.cs | 33 -- src/progaudi.tarantool/RequestWriter.cs | 15 +- src/progaudi.tarantool/ResponseReader.cs | 42 +- .../SocketPhysicalConnection.cs | 3 +- src/progaudi.tarantool/Space.cs | 8 +- src/progaudi.tarantool/SpaceMeta.cs | 38 +- .../TarantoolConvertersRegistrator.cs | 71 +++ src/progaudi.tarantool/TarantoolSegment.cs | 24 + src/progaudi.tarantool/Utils/ByteUtils.cs | 17 + .../progaudi.tarantool.csproj | 2 +- 38 files changed, 1622 insertions(+), 615 deletions(-) create mode 100644 src/progaudi.tarantool/Converters/SystemTupleConverters.cs create mode 100644 src/progaudi.tarantool/Converters/ValueTupleConverters.cs create mode 100644 src/progaudi.tarantool/Model/EnumConverter.cs delete mode 100644 src/progaudi.tarantool/Model/EnumResolver.cs delete mode 100644 src/progaudi.tarantool/Model/PackerResolver.cs delete mode 100644 src/progaudi.tarantool/RequestLength.cs create mode 100644 src/progaudi.tarantool/TarantoolConvertersRegistrator.cs create mode 100644 src/progaudi.tarantool/TarantoolSegment.cs diff --git a/samples/insert-performance/Program.cs b/samples/insert-performance/Program.cs index 714c9a26..bc2d0b42 100644 --- a/samples/insert-performance/Program.cs +++ b/samples/insert-performance/Program.cs @@ -12,7 +12,8 @@ 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");//, log); + 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 { diff --git a/src/progaudi.tarantool/Box.cs b/src/progaudi.tarantool/Box.cs index eb989ce5..0b329e8f 100644 --- a/src/progaudi.tarantool/Box.cs +++ b/src/progaudi.tarantool/Box.cs @@ -1,8 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using MessagePack; -using MessagePack.Resolvers; +using ProGaudi.MsgPack.Light; using ProGaudi.Tarantool.Client.Model; using ProGaudi.Tarantool.Client.Utils; @@ -21,6 +20,7 @@ public class Box : IBox public Box(ClientOptions options) { _clientOptions = options; + TarantoolConvertersRegistrator.Register(options.MsgPackContext); _logicalConnection = new LogicalConnection(options, new RequestIdCounter()); Metrics = new Metrics(_logicalConnection); @@ -133,21 +133,5 @@ public Task> Eval(string expression // return _logicalConnection.SendRequest(new ExecuteSqlRequest(query, parameters)); //} - - static Box() - { - CompositeResolver.RegisterAndSetAsDefault( - BuiltinResolver.Instance, - AttributeFormatterResolver.Instance, - PackerResolver.Instance, - EnumResolver.Instance, - - DynamicEnumResolver.Instance, - DynamicGenericResolver.Instance, - DynamicObjectResolver.Instance, - - PrimitiveObjectResolver.Instance - ); - } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/SystemTupleConverters.cs b/src/progaudi.tarantool/Converters/SystemTupleConverters.cs new file mode 100644 index 00000000..dd115f92 --- /dev/null +++ b/src/progaudi.tarantool/Converters/SystemTupleConverters.cs @@ -0,0 +1,471 @@ + + +using System; +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Utils; + +namespace ProGaudi.Tarantool.Client.Converters{ + + public class SystemTupleConverter :IMsgPackConverter> + { + private IMsgPackConverter _nullConverter; + + private IMsgPackConverter _t1Converter; + + public void Initialize(MsgPackContext context) + { + _nullConverter = context.NullConverter; + + _t1Converter = context.GetConverter(); + } + + public void Write(Tuple value, IMsgPackWriter writer) + { + if (value == null) + { + _nullConverter.Write(null, writer); + return; + } + + writer.WriteArrayHeader(1); + + _t1Converter.Write(value.Item1, writer); + } + + public Tuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return null; + } + + const uint expected = 1; + if (actual != expected) + { + throw ExceptionHelper.InvalidArrayLength(expected, actual); + } + + var item1 = _t1Converter.Read(reader); + + return Tuple.Create( + item1 + ); + } + } + + public class SystemTupleConverter :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(Tuple 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 Tuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return null; + } + + const uint expected = 2; + if (actual != expected) + { + throw ExceptionHelper.InvalidArrayLength(expected, actual); + } + + var item1 = _t1Converter.Read(reader); + var item2 = _t2Converter.Read(reader); + + return Tuple.Create( + item1, + item2 + ); + } + } + + public class SystemTupleConverter :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(Tuple 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 Tuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return null; + } + + const uint expected = 3; + if (actual != expected) + { + throw ExceptionHelper.InvalidArrayLength(expected, actual); + } + + var item1 = _t1Converter.Read(reader); + var item2 = _t2Converter.Read(reader); + var item3 = _t3Converter.Read(reader); + + return Tuple.Create( + item1, + item2, + item3 + ); + } + } + + public class SystemTupleConverter :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(Tuple 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 Tuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return null; + } + + const uint expected = 4; + 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 Tuple.Create( + item1, + item2, + item3, + item4 + ); + } + } + + public class SystemTupleConverter :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(Tuple 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 Tuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return null; + } + + const uint expected = 5; + 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 Tuple.Create( + item1, + item2, + item3, + item4, + item5 + ); + } + } + + public class SystemTupleConverter :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(Tuple 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 Tuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return null; + } + + const uint expected = 6; + 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 Tuple.Create( + item1, + item2, + item3, + item4, + item5, + item6 + ); + } + } + + public class SystemTupleConverter :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(Tuple 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 Tuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return null; + } + + const uint expected = 7; + 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 Tuple.Create( + item1, + item2, + item3, + item4, + item5, + item6, + item7 + ); + } + } + +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Converters/ValueTupleConverters.cs b/src/progaudi.tarantool/Converters/ValueTupleConverters.cs new file mode 100644 index 00000000..29c96e75 --- /dev/null +++ b/src/progaudi.tarantool/Converters/ValueTupleConverters.cs @@ -0,0 +1,401 @@ + + +using System; +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Utils; + +namespace ProGaudi.Tarantool.Client.Converters{ + + public class ValueTupleConverter :IMsgPackConverter> + { + private IMsgPackConverter _t1Converter; + + public void Initialize(MsgPackContext context) + { + _t1Converter = context.GetConverter(); + } + + public void Write(ValueTuple value, IMsgPackWriter writer) + { + writer.WriteArrayHeader(1); + + _t1Converter.Write(value.Item1, writer); + } + + public ValueTuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return default(ValueTuple); + } + + const uint expected = 1; + if (actual != expected) + { + throw ExceptionHelper.InvalidArrayLength(expected, actual); + } + + var item1 = _t1Converter.Read(reader); + + return ValueTuple.Create( + item1 + ); + } + } + + public class ValueTupleConverter :IMsgPackConverter> + { + private IMsgPackConverter _t1Converter; + + private IMsgPackConverter _t2Converter; + + public void Initialize(MsgPackContext context) + { + _t1Converter = context.GetConverter(); + _t2Converter = context.GetConverter(); + } + + public void Write(ValueTuple value, IMsgPackWriter writer) + { + writer.WriteArrayHeader(2); + + _t1Converter.Write(value.Item1, writer); + _t2Converter.Write(value.Item2, writer); + } + + public ValueTuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return default(ValueTuple); + } + + const uint expected = 2; + if (actual != expected) + { + throw ExceptionHelper.InvalidArrayLength(expected, actual); + } + + var item1 = _t1Converter.Read(reader); + var item2 = _t2Converter.Read(reader); + + return ValueTuple.Create( + item1, + item2 + ); + } + } + + public class ValueTupleConverter :IMsgPackConverter> + { + private IMsgPackConverter _t1Converter; + + private IMsgPackConverter _t2Converter; + + private IMsgPackConverter _t3Converter; + + public void Initialize(MsgPackContext context) + { + _t1Converter = context.GetConverter(); + _t2Converter = context.GetConverter(); + _t3Converter = context.GetConverter(); + } + + public void Write(ValueTuple value, IMsgPackWriter writer) + { + writer.WriteArrayHeader(3); + + _t1Converter.Write(value.Item1, writer); + _t2Converter.Write(value.Item2, writer); + _t3Converter.Write(value.Item3, writer); + } + + public ValueTuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return default(ValueTuple); + } + + const uint expected = 3; + if (actual != expected) + { + throw ExceptionHelper.InvalidArrayLength(expected, actual); + } + + var item1 = _t1Converter.Read(reader); + var item2 = _t2Converter.Read(reader); + var item3 = _t3Converter.Read(reader); + + return ValueTuple.Create( + item1, + item2, + item3 + ); + } + } + + public class ValueTupleConverter :IMsgPackConverter> + { + private IMsgPackConverter _t1Converter; + + private IMsgPackConverter _t2Converter; + + private IMsgPackConverter _t3Converter; + + private IMsgPackConverter _t4Converter; + + public void Initialize(MsgPackContext context) + { + _t1Converter = context.GetConverter(); + _t2Converter = context.GetConverter(); + _t3Converter = context.GetConverter(); + _t4Converter = context.GetConverter(); + } + + public void Write(ValueTuple value, IMsgPackWriter writer) + { + writer.WriteArrayHeader(4); + + _t1Converter.Write(value.Item1, writer); + _t2Converter.Write(value.Item2, writer); + _t3Converter.Write(value.Item3, writer); + _t4Converter.Write(value.Item4, writer); + } + + public ValueTuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return default(ValueTuple); + } + + const uint expected = 4; + 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 ValueTuple.Create( + item1, + item2, + item3, + item4 + ); + } + } + + public class ValueTupleConverter :IMsgPackConverter> + { + private IMsgPackConverter _t1Converter; + + private IMsgPackConverter _t2Converter; + + private IMsgPackConverter _t3Converter; + + private IMsgPackConverter _t4Converter; + + private IMsgPackConverter _t5Converter; + + public void Initialize(MsgPackContext context) + { + _t1Converter = context.GetConverter(); + _t2Converter = context.GetConverter(); + _t3Converter = context.GetConverter(); + _t4Converter = context.GetConverter(); + _t5Converter = context.GetConverter(); + } + + public void Write(ValueTuple value, IMsgPackWriter writer) + { + 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 ValueTuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return default(ValueTuple); + } + + const uint expected = 5; + 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 ValueTuple.Create( + item1, + item2, + item3, + item4, + item5 + ); + } + } + + public class ValueTupleConverter :IMsgPackConverter> + { + private IMsgPackConverter _t1Converter; + + private IMsgPackConverter _t2Converter; + + private IMsgPackConverter _t3Converter; + + private IMsgPackConverter _t4Converter; + + private IMsgPackConverter _t5Converter; + + private IMsgPackConverter _t6Converter; + + public void Initialize(MsgPackContext context) + { + _t1Converter = context.GetConverter(); + _t2Converter = context.GetConverter(); + _t3Converter = context.GetConverter(); + _t4Converter = context.GetConverter(); + _t5Converter = context.GetConverter(); + _t6Converter = context.GetConverter(); + } + + public void Write(ValueTuple value, IMsgPackWriter writer) + { + 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 ValueTuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return default(ValueTuple); + } + + const uint expected = 6; + 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 ValueTuple.Create( + item1, + item2, + item3, + item4, + item5, + item6 + ); + } + } + + public class ValueTupleConverter :IMsgPackConverter> + { + 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) + { + _t1Converter = context.GetConverter(); + _t2Converter = context.GetConverter(); + _t3Converter = context.GetConverter(); + _t4Converter = context.GetConverter(); + _t5Converter = context.GetConverter(); + _t6Converter = context.GetConverter(); + _t7Converter = context.GetConverter(); + } + + public void Write(ValueTuple value, IMsgPackWriter writer) + { + 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 ValueTuple Read(IMsgPackReader reader) + { + var actual = reader.ReadArrayLength(); + if (actual == null) + { + return default(ValueTuple); + } + + const uint expected = 7; + 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 ValueTuple.Create( + item1, + item2, + item3, + item4, + item5, + item6, + item7 + ); + } + } + +} \ No newline at end of file diff --git a/src/progaudi.tarantool/IPhysicalConnection.cs b/src/progaudi.tarantool/IPhysicalConnection.cs index 0baef61a..db2c17b8 100644 --- a/src/progaudi.tarantool/IPhysicalConnection.cs +++ b/src/progaudi.tarantool/IPhysicalConnection.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.IO; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; @@ -15,7 +16,7 @@ public interface IPhysicalConnection : IDisposable Task ReadAsync(byte[] buffer, int offset, int count); - void Write(in ArraySegment buffer); + void Write(in ReadOnlySequence buffer); Stream Stream { get; } } diff --git a/src/progaudi.tarantool/IRequestWriter.cs b/src/progaudi.tarantool/IRequestWriter.cs index 928e4dc7..04d34225 100644 --- a/src/progaudi.tarantool/IRequestWriter.cs +++ b/src/progaudi.tarantool/IRequestWriter.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; namespace ProGaudi.Tarantool.Client { @@ -8,6 +9,6 @@ internal interface IRequestWriter : IDisposable bool IsConnected { get; } - void Write(in 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 9b3bdc75..996b24ed 100644 --- a/src/progaudi.tarantool/IResponseReader.cs +++ b/src/progaudi.tarantool/IResponseReader.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Threading.Tasks; using ProGaudi.Tarantool.Client.Model; @@ -9,7 +10,7 @@ public interface IResponseReader : IDisposable { void BeginReading(); - Task GetResponseTask(RequestId requestId, Func, TResponse> responseCreator); + Task GetResponseTask(RequestId requestId, Func responseCreator); bool IsConnected { get; } } diff --git a/src/progaudi.tarantool/IndexMeta.cs b/src/progaudi.tarantool/IndexMeta.cs index b1a0e679..ac58a212 100644 --- a/src/progaudi.tarantool/IndexMeta.cs +++ b/src/progaudi.tarantool/IndexMeta.cs @@ -1,11 +1,11 @@ using System; -using MessagePack; +using ProGaudi.MsgPack.Light; using ProGaudi.Tarantool.Client.Model; namespace ProGaudi.Tarantool.Client { - [MessagePackObject] - public readonly struct IndexMeta : IEquatable + [MsgPackArray] + public class IndexMeta : IEquatable { public IndexMeta(uint spaceId, uint id, string name, IndexType type, IndexCreationOptions options, IndexPart[] parts) { @@ -17,23 +17,27 @@ public IndexMeta(uint spaceId, uint id, string name, IndexType type, IndexCreati Parts = parts; } - [Key(0)] - public uint SpaceId { get; } + public IndexMeta() + { + } + + [MsgPackArrayElement(0)] + public uint SpaceId { get; set; } - [Key(1)] - public uint Id { get; } + [MsgPackArrayElement(1)] + public uint Id { get; set; } - [Key(2)] - public string Name { get; } + [MsgPackArrayElement(2)] + public string Name { get; set; } - [Key(3)] - public IndexType Type { get; } + [MsgPackArrayElement(3)] + public IndexType Type { get; set; } - [Key(4)] - public IndexCreationOptions Options { get; } + [MsgPackArrayElement(4)] + public IndexCreationOptions Options { get; set; } - [Key(5)] - public IndexPart[] Parts { get; } + [MsgPackArrayElement(5)] + public IndexPart[] Parts { get; set; } public override string ToString() { diff --git a/src/progaudi.tarantool/LogicalConnection.cs b/src/progaudi.tarantool/LogicalConnection.cs index b7eecb6b..34db864c 100644 --- a/src/progaudi.tarantool/LogicalConnection.cs +++ b/src/progaudi.tarantool/LogicalConnection.cs @@ -1,9 +1,11 @@ using System; using System.Buffers; +using System.Buffers.Binary; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MessagePack; +using ProGaudi.MsgPack.Light; using ProGaudi.Tarantool.Client.Model; using ProGaudi.Tarantool.Client.Utils; @@ -11,10 +13,11 @@ namespace ProGaudi.Tarantool.Client { internal class LogicalConnection : ILogicalConnection { - private static readonly Func, IMemoryOwner> RawByteCreator = x => + private static readonly Func> RawByteCreator = x => { - var owner = MemoryPool.Shared.Rent(x.Count); - x.AsMemory().CopyTo(owner.Memory); + 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; }; @@ -107,21 +110,13 @@ public Task SendRequestWithEmptyResponse(TRequest request, TimeSpan? t public Task> SendRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { - return SendRequestImpl(request, buffer => - { - var formatter = MessagePackSerializer.DefaultResolver.GetFormatter>(); - return formatter.Deserialize(buffer.Array, buffer.Offset, MessagePackSerializer.DefaultResolver, out _); - }, timeout); + return SendRequestImpl(request, buffer => MsgPackSerializer.Deserialize>(buffer, _clientOptions.MsgPackContext), timeout); } public Task SendRequest(TRequest request, TimeSpan? timeout = null) where TRequest : IRequest { - return SendRequestImpl(request, buffer => - { - var formatter = MessagePackSerializer.DefaultResolver.GetFormatter(); - return formatter.Deserialize(buffer.Array, buffer.Offset, MessagePackSerializer.DefaultResolver, out _); - }, timeout); + return SendRequestImpl(request, buffer => MsgPackSerializer.Deserialize(buffer, _clientOptions.MsgPackContext), timeout); } public Task> SendRawRequest(TRequest request, TimeSpan? timeout = null) @@ -146,7 +141,7 @@ private async Task LoginIfNotGuest(GreetingsResponse greetings) _clientOptions.LogWriter?.WriteLine($"Authentication request send: {authenticateRequest}"); } - private Task SendRequestImpl(TRequest request, Func, TResponse> response, TimeSpan? timeout) + private Task SendRequestImpl(TRequest request, Func response, TimeSpan? timeout) where TRequest : IRequest { if (_disposed) @@ -154,22 +149,15 @@ private Task SendRequestImpl(TRequest request, F throw new ObjectDisposedException(nameof(LogicalConnection)); } - var originalBuffer = ArrayPool.Shared.Rent(40000); - var bodyBuffer = originalBuffer; - var offset = 5; - var requestId = _requestIdCounter.GetRequestId(); - offset += MessagePackBinary.WriteFixedMapHeaderUnsafe(ref bodyBuffer, offset, 2); - offset += MessagePackBinary.WriteUInt32(ref bodyBuffer, offset, Keys.Code); - offset += MessagePackBinary.WriteUInt32(ref bodyBuffer, offset, (uint) request.Code); - offset += MessagePackBinary.WriteUInt32(ref bodyBuffer, offset, Keys.Sync); - offset += MessagePackBinary.WriteUInt64(ref bodyBuffer, offset, requestId.Value); - - var formatter = MessagePackSerializer.DefaultResolver.GetFormatter(); - offset += formatter.Serialize(ref bodyBuffer, offset, request, MessagePackSerializer.DefaultResolver); - MessagePackBinary.WriteUInt32ForceUInt32Block(ref bodyBuffer, 0, (uint) (offset - 5)); + 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); - _requestWriter.Write(new ArraySegment(bodyBuffer, 0, offset)); + var bodyBuffer = TarantoolSegment.CreateSequence(length, header, body); + _requestWriter.Write(bodyBuffer); try { diff --git a/src/progaudi.tarantool/Model/AuthenticationRequest.cs b/src/progaudi.tarantool/Model/AuthenticationRequest.cs index d48b3d5d..155305c0 100644 --- a/src/progaudi.tarantool/Model/AuthenticationRequest.cs +++ b/src/progaudi.tarantool/Model/AuthenticationRequest.cs @@ -1,13 +1,10 @@ using System; using System.Linq; -using MessagePack; -using MessagePack.Formatters; +using ProGaudi.MsgPack.Light; using ProGaudi.Tarantool.Client.Utils; -using static MessagePack.MessagePackBinary; namespace ProGaudi.Tarantool.Client.Model { - [MessagePackFormatter(typeof(Formatter))] public class AuthenticationRequest : IRequest { private AuthenticationRequest(string username, byte[] scramble) @@ -53,26 +50,42 @@ private static string ToReadableString(byte[] bytes) return string.Concat(bytes.Select(b => b.ToString("X2 "))); } - internal class Formatter : IMessagePackFormatter + internal class Formatter : IMsgPackConverter { - public int Serialize(ref byte[] bytes, int offset, AuthenticationRequest value, IFormatterResolver formatterResolver) + private IMsgPackConverter _keyConverter; + private IMsgPackConverter _bytesConverter; + private IMsgPackConverter _stringConverter; + private IMsgPackConverter _nullConverter; + + public void Initialize(MsgPackContext context) { - if (value == null) return WriteNil(ref bytes, offset); + _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); - var startOffset = offset; + _keyConverter.Write(Keys.Username, writer); + _stringConverter.Write(value.Username, writer); - offset += WriteFixedMapHeaderUnsafe(ref bytes, offset, 2); - offset += WriteUInt32(ref bytes, offset, Keys.Username); - offset += WriteString(ref bytes, offset, value.Username); - offset += WriteUInt32(ref bytes, offset, Keys.Tuple); - offset += WriteArrayHeader(ref bytes, offset, 2); - offset += WriteString(ref bytes, offset, "chap-sha1"); - offset += WriteBytes(ref bytes, offset, value.Scramble); + _keyConverter.Write(Keys.Tuple, writer); - return offset - startOffset; + writer.WriteArrayHeader(2); + _stringConverter.Write("chap-sha1", writer); + _bytesConverter.Write(value.Scramble, writer); } - public AuthenticationRequest Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + public AuthenticationRequest Read(IMsgPackReader reader) { throw new NotImplementedException(); } diff --git a/src/progaudi.tarantool/Model/CallRequest.cs b/src/progaudi.tarantool/Model/CallRequest.cs index 34317475..abd1df5f 100644 --- a/src/progaudi.tarantool/Model/CallRequest.cs +++ b/src/progaudi.tarantool/Model/CallRequest.cs @@ -1,7 +1,5 @@ using System; -using MessagePack; -using MessagePack.Formatters; -using static MessagePack.MessagePackBinary; +using ProGaudi.MsgPack.Light; namespace ProGaudi.Tarantool.Client.Model { @@ -22,22 +20,31 @@ public CallRequest(string functionName, T tuple, bool use17 = true) public CommandCodes Code => _use17 ? CommandCodes.Call : CommandCodes.OldCall; - internal class Formatter : IMessagePackFormatter> + internal class Formatter : IMsgPackConverter> { - public int Serialize(ref byte[] bytes, int offset, CallRequest value, IFormatterResolver formatterResolver) + 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) { - var startOffset = offset; + writer.WriteMapHeader(2); - offset += WriteFixedMapHeaderUnsafe(ref bytes, offset, 2); - offset += WriteUInt32(ref bytes, offset, Keys.FunctionName); - offset += WriteString(ref bytes, offset, value.FunctionName); - offset += WriteUInt32(ref bytes, offset, Keys.Tuple); - offset += formatterResolver.GetFormatter().Serialize(ref bytes, offset, value.Tuple, formatterResolver); + _keyConverter.Write(Keys.FunctionName, writer); + _stringConverter.Write(value.FunctionName, writer); - return offset - startOffset; + _keyConverter.Write(Keys.Tuple, writer); + _tupleConverter.Write(value.Tuple, writer); } - public CallRequest Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + public CallRequest Read(IMsgPackReader reader) { throw new NotImplementedException(); } diff --git a/src/progaudi.tarantool/Model/ClientOptions.cs b/src/progaudi.tarantool/Model/ClientOptions.cs index 6ff7ff64..200569c0 100644 --- a/src/progaudi.tarantool/Model/ClientOptions.cs +++ b/src/progaudi.tarantool/Model/ClientOptions.cs @@ -1,20 +1,23 @@ -namespace ProGaudi.Tarantool.Client.Model +using ProGaudi.MsgPack.Light; + +namespace ProGaudi.Tarantool.Client.Model { public class ClientOptions { - public ClientOptions(ILog log = null) - : this(new ConnectionOptions(), log) + public ClientOptions(ILog log = null, MsgPackContext context = null) + : this(new ConnectionOptions(), log, context) { } - public ClientOptions(string replicationSource, ILog log = null) - : this(new ConnectionOptions(replicationSource, log), log) + public ClientOptions(string replicationSource, ILog log = null, MsgPackContext context = null) + : this(new ConnectionOptions(replicationSource, log), log, context) { } - private ClientOptions(ConnectionOptions options, ILog log) + private ClientOptions(ConnectionOptions options, ILog log, MsgPackContext context) { ConnectionOptions = options; + MsgPackContext = context ?? new MsgPackContext(); if (log != null) { LogWriter = new LogWriterWrapper(this, log); @@ -23,6 +26,8 @@ private ClientOptions(ConnectionOptions options, ILog log) public ILog LogWriter { get; } + public MsgPackContext MsgPackContext { get; } + public ConnectionOptions ConnectionOptions { get; } public string Name { get; set; } diff --git a/src/progaudi.tarantool/Model/DataResponse.cs b/src/progaudi.tarantool/Model/DataResponse.cs index fdffa1d4..877fb31c 100644 --- a/src/progaudi.tarantool/Model/DataResponse.cs +++ b/src/progaudi.tarantool/Model/DataResponse.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; -using MessagePack; -using MessagePack.Formatters; +using System; +using System.Collections.Generic; +using ProGaudi.MsgPack.Light; using ProGaudi.Tarantool.Client.Utils; namespace ProGaudi.Tarantool.Client.Model @@ -14,43 +14,69 @@ public DataResponse(SqlInfo sqlInfo) public SqlInfo SqlInfo { get; } - public sealed class Formatter : IMessagePackFormatter + public sealed class Formatter : IMsgPackConverter { - public int Serialize(ref byte[] bytes, int offset, DataResponse value, IFormatterResolver formatterResolver) + 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 System.NotImplementedException(); + throw new NotSupportedException(); } - public DataResponse Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + public DataResponse Read(IMsgPackReader reader) { - if (MessagePackBinary.IsNil(bytes, offset)) + var length = reader.ReadMapLength(); + if (!(1u <= length && length <= 3)) { - readSize = 1; - return null; + throw ExceptionHelper.InvalidMapLength(length, 1u, 2u); } - var startOffset = offset; - var length = MessagePackBinary.ReadMapHeaderRaw(bytes, offset, out readSize); - offset += readSize; + var sqlInfo = default(SqlInfo); - var result = default(DataResponse); for (var i = 0; i < length; i++) { - var key = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); - offset += readSize; - switch (key) + var dataKey = _keyConverter.Read(reader); + switch (dataKey) { case Keys.SqlInfo: - result = new DataResponse(formatterResolver.GetFormatter().Deserialize(bytes, offset, formatterResolver, out readSize)); - offset += readSize; + sqlInfo = ReadSqlInfo(reader, _keyConverter, _intConverter); break; default: - offset += MessagePackBinary.ReadNext(bytes, offset); - break; + throw ExceptionHelper.UnexpectedKey(dataKey, Keys.SqlInfo); } } - readSize = offset - startOffset; + 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; } @@ -70,25 +96,34 @@ public DataResponse(T data, FieldMetadata[] metadata, SqlInfo sqlInfo) public IReadOnlyList MetaData { get; } - public new class Formatter : IMessagePackFormatter> + public new class Formatter : IMsgPackConverter> { - public int Serialize(ref byte[] bytes, int offset, DataResponse value, IFormatterResolver formatterResolver) + private IMsgPackConverter _keyConverter; + private IMsgPackConverter _dataConverter; + private IMsgPackConverter _stringConverter; + private IMsgPackConverter _intConverter; + + public void Initialize(MsgPackContext context) { - throw new System.NotImplementedException(); + _keyConverter = context.GetConverter(); + _dataConverter = context.GetConverter(); + _stringConverter = context.GetConverter(); + _intConverter = context.GetConverter(); } - public DataResponse Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + public void Write(DataResponse value, IMsgPackWriter writer) { - if (MessagePackBinary.IsNil(bytes, offset)) + throw new NotSupportedException(); + } + + DataResponse IMsgPackConverter>.Read(IMsgPackReader reader) + { + var length = reader.ReadMapLength(); + if (!(1u <= length && length <= 3)) { - readSize = 1; - return null; + throw ExceptionHelper.InvalidMapLength(length, 1u, 2u); } - var startOffset = offset; - var length = MessagePackBinary.ReadMapHeaderRaw(bytes, offset, out readSize); - offset += readSize; - var data = default(T); var dataWasSet = false; var metadata = default(FieldMetadata[]); @@ -96,26 +131,21 @@ public DataResponse Deserialize(byte[] bytes, int offset, IFormatterResolver for (var i = 0; i < length; i++) { - var key = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); - offset += readSize; - switch (key) + var dataKey = _keyConverter.Read(reader); + switch (dataKey) { case Keys.Data: - data = formatterResolver.GetFormatter().Deserialize(bytes, offset, formatterResolver, out readSize); + data = _dataConverter.Read(reader); dataWasSet = true; - offset += readSize; break; case Keys.Metadata: - metadata = formatterResolver.GetFormatter().Deserialize(bytes, offset, formatterResolver, out readSize); - offset += readSize; + metadata = ReadMetadata(reader); break; case Keys.SqlInfo: - sqlInfo = formatterResolver.GetFormatter().Deserialize(bytes, offset, formatterResolver, out readSize); - offset += readSize; + sqlInfo = DataResponse.Formatter.ReadSqlInfo(reader, _keyConverter, _intConverter); break; default: - offset += MessagePackBinary.ReadNext(bytes, offset); - break; + throw ExceptionHelper.UnexpectedKey(dataKey, Keys.Data, Keys.Metadata, Keys.SqlInfo); } } @@ -124,10 +154,43 @@ public DataResponse Deserialize(byte[] bytes, int offset, IFormatterResolver throw ExceptionHelper.NoDataInDataResponse(); } - readSize = offset - startOffset; - 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/EnumAsStringFormatter.cs b/src/progaudi.tarantool/Model/EnumAsStringFormatter.cs index 9010d25f..babc73a1 100644 --- a/src/progaudi.tarantool/Model/EnumAsStringFormatter.cs +++ b/src/progaudi.tarantool/Model/EnumAsStringFormatter.cs @@ -1,71 +1,67 @@ using System; -using System.Collections.Generic; -using MessagePack; -using MessagePack.Formatters; +using System.Reflection; +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Utils; namespace ProGaudi.Tarantool.Client.Model { - public sealed class EnumAsStringFormatter : IMessagePackFormatter + public sealed class EnumAsStringFormatter : IMsgPackConverter + where T : struct { - private readonly bool _ignoreCase; - private readonly Dictionary _nameValueMapping; - private readonly Dictionary _valueNameMapping; + private IMsgPackConverter _stringConverter; - public EnumAsStringFormatter(bool ignoreCase) + public void Initialize(MsgPackContext context) { - _ignoreCase = ignoreCase; - var names = Enum.GetNames(typeof(T)); - var values = Enum.GetValues(typeof(T)); - - _nameValueMapping = new Dictionary(names.Length, ignoreCase ? (IEqualityComparer) IgnoreCaseComparer.Instance : EqualityComparer.Default); - _valueNameMapping = new Dictionary(names.Length); - - for (var i = 0; i < names.Length; i++) - { - _nameValueMapping[names[i]] = (T)values.GetValue(i); - _valueNameMapping[(T)values.GetValue(i)] = names[i]; - } + _stringConverter = context.GetConverter(); } - public int Serialize(ref byte[] bytes, int offset, T value, IFormatterResolver formatterResolver) + static EnumAsStringFormatter() { - if (!_valueNameMapping.TryGetValue(value, out var name)) + var enumTypeInfo = typeof(T).GetTypeInfo(); + if (!enumTypeInfo.IsEnum) { - name = value.ToString(); // fallback for flags etc, But Enum.ToString is too slow. + throw ExceptionHelper.EnumExpected(enumTypeInfo); } - - return MessagePackBinary.WriteString(ref bytes, offset, name); } - public T Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + public void Write(T value, IMsgPackWriter writer) { - var name = MessagePackBinary.ReadString(bytes, offset, out readSize); + _stringConverter.Write(value.ToString(), writer); + } - if (!_nameValueMapping.TryGetValue(name, out var value)) - { - value = (T)Enum.Parse(typeof(T), name, _ignoreCase); // Enum.Parse is too slow - } + public T Read(IMsgPackReader reader) + { + var stringValue = _stringConverter.Read(reader); - return value; + return Parse(typeof (T), stringValue, true); } - private class IgnoreCaseComparer : IEqualityComparer + public static T Parse(Type type, string stringValue, bool ignoreCase) { - public static readonly IgnoreCaseComparer Instance = new IgnoreCaseComparer(); + T output; + string enumStringValue = null; - private IgnoreCaseComparer() + if (!type.GetTypeInfo().IsEnum) { + throw ExceptionHelper.EnumExpected(type); } - public bool Equals(string x, string y) + if (!Enum.TryParse(stringValue, ignoreCase, out output)) { - return string.Compare(x, y, StringComparison.InvariantCultureIgnoreCase) == 0; - } - public int GetHashCode(string obj) - { - return obj.ToLowerInvariant().GetHashCode(); + //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/Model/EnumConverter.cs b/src/progaudi.tarantool/Model/EnumConverter.cs new file mode 100644 index 00000000..2221db1a --- /dev/null +++ b/src/progaudi.tarantool/Model/EnumConverter.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Utils; + +namespace ProGaudi.Tarantool.Client.Model +{ + internal class EnumConverter : IMsgPackConverter + where T : struct, IConvertible + { + private IMsgPackConverter _sbyteConverter; + private IMsgPackConverter _byteConverter; + private IMsgPackConverter _shortConverter; + private IMsgPackConverter _ushortConverter; + private IMsgPackConverter _intConverter; + private IMsgPackConverter _uintConverter; + private IMsgPackConverter _longConverter; + private IMsgPackConverter _ulongConverter; + + private readonly Dictionary> _writeMethodsCache = new Dictionary>(); + private readonly Dictionary> _readMethodsCache = new Dictionary>(); + + public void Initialize(MsgPackContext context) + { + _sbyteConverter = context.GetConverter(); + _byteConverter = context.GetConverter(); + _shortConverter = context.GetConverter(); + _ushortConverter = context.GetConverter(); + _intConverter = context.GetConverter(); + _uintConverter = context.GetConverter(); + _longConverter = context.GetConverter(); + _ulongConverter = context.GetConverter(); + + InitializeWriteMethodsChache(); + InitializeReadMethodsCache(); + } + + private void InitializeReadMethodsCache() + { + _readMethodsCache.Add(typeof(sbyte), reader => (T)Enum.ToObject(typeof(T), _sbyteConverter.Read(reader))); + _readMethodsCache.Add(typeof(byte), reader => (T)Enum.ToObject(typeof(T), _byteConverter.Read(reader))); + _readMethodsCache.Add(typeof(short), reader => (T)Enum.ToObject(typeof(T), _shortConverter.Read(reader))); + _readMethodsCache.Add(typeof(ushort), reader => (T)Enum.ToObject(typeof(T), _ushortConverter.Read(reader))); + _readMethodsCache.Add(typeof(int), reader => (T)Enum.ToObject(typeof(T), _intConverter.Read(reader))); + _readMethodsCache.Add(typeof(uint), reader => (T)Enum.ToObject(typeof(T), _uintConverter.Read(reader))); + _readMethodsCache.Add(typeof(long), reader => (T)Enum.ToObject(typeof(T), _longConverter.Read(reader))); + _readMethodsCache.Add(typeof(ulong), reader => (T)Enum.ToObject(typeof(T), _ulongConverter.Read(reader))); + } + + private void InitializeWriteMethodsChache() + { + _writeMethodsCache.Add(typeof(sbyte), (value, writer) => _sbyteConverter.Write(value.ToSByte(CultureInfo.InvariantCulture), writer)); + _writeMethodsCache.Add(typeof(byte), (value, writer) => _byteConverter.Write(value.ToByte(CultureInfo.InvariantCulture), writer)); + _writeMethodsCache.Add(typeof(short), (value, writer) => _shortConverter.Write(value.ToInt16(CultureInfo.InvariantCulture), writer)); + _writeMethodsCache.Add(typeof(ushort), (value, writer) => _ushortConverter.Write(value.ToUInt16(CultureInfo.InvariantCulture), writer)); + _writeMethodsCache.Add(typeof(int), (value, writer) => _intConverter.Write(value.ToInt32(CultureInfo.InvariantCulture), writer)); + _writeMethodsCache.Add(typeof(uint), (value, writer) => _uintConverter.Write(value.ToUInt32(CultureInfo.InvariantCulture), writer)); + _writeMethodsCache.Add(typeof(long), (value, writer) => _longConverter.Write(value.ToInt64(CultureInfo.InvariantCulture), writer)); + _writeMethodsCache.Add(typeof(ulong), (value, writer) => _ulongConverter.Write(value.ToUInt64(CultureInfo.InvariantCulture), writer)); + } + + static EnumConverter() + { + var enumTypeInfo = typeof(T).GetTypeInfo(); + if (!enumTypeInfo.IsEnum) + { + throw ExceptionHelper.EnumExpected(enumTypeInfo); + } + } + + public void Write(T value, IMsgPackWriter writer) + { + var enumUnderlyingType = Enum.GetUnderlyingType(typeof(T)); + + if (_writeMethodsCache.TryGetValue(enumUnderlyingType, out var writeMethod)) + { + writeMethod(value, writer); + } + else + { + throw ExceptionHelper.UnexpectedEnumUnderlyingType(enumUnderlyingType); + } + } + + public T Read(IMsgPackReader reader) + { + var enumUnderlyingType = Enum.GetUnderlyingType(typeof(T)); + if (_readMethodsCache.TryGetValue(enumUnderlyingType, out var readMethod)) + { + return readMethod(reader); + } + + throw ExceptionHelper.UnexpectedEnumUnderlyingType(enumUnderlyingType); + } + } +} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/EnumResolver.cs b/src/progaudi.tarantool/Model/EnumResolver.cs deleted file mode 100644 index 1e18c827..00000000 --- a/src/progaudi.tarantool/Model/EnumResolver.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Reflection; -using MessagePack; -using MessagePack.Formatters; - -namespace ProGaudi.Tarantool.Client.Model -{ - public sealed class EnumResolver : IFormatterResolver - { - public static readonly EnumResolver Instance = new EnumResolver(); - - private EnumResolver() - { - } - - public IMessagePackFormatter GetFormatter() - { - return Cache.Formatter; - } - - private static class Cache - { - public static readonly IMessagePackFormatter Formatter; - - static Cache() - { - var type = typeof(T); - if (!type.IsEnum) - { - return; - } - - var enumAsString = type.GetCustomAttribute(); - if (enumAsString == null) - { - return; - } - - Formatter = (IMessagePackFormatter) Activator.CreateInstance(typeof(EnumAsStringFormatter<>).MakeGenericType(type), new object[] { enumAsString.IgnoreCase }); - } - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/FieldMetadata.cs b/src/progaudi.tarantool/Model/FieldMetadata.cs index 32c76aec..537e9eec 100644 --- a/src/progaudi.tarantool/Model/FieldMetadata.cs +++ b/src/progaudi.tarantool/Model/FieldMetadata.cs @@ -1,11 +1,8 @@ using System; using JetBrains.Annotations; -using MessagePack; -using MessagePack.Formatters; namespace ProGaudi.Tarantool.Client.Model { - [MessagePackFormatter(typeof(Formatter))] public class FieldMetadata : IEquatable { public string Name { get; } @@ -46,47 +43,5 @@ public override int GetHashCode() { return !Equals(left, right); } - - public sealed class Formatter : IMessagePackFormatter - { - public int Serialize(ref byte[] bytes, int offset, FieldMetadata value, IFormatterResolver formatterResolver) - { - throw new NotImplementedException(); - } - - public FieldMetadata Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) - { - if (MessagePackBinary.IsNil(bytes, offset)) - { - readSize = 1; - return null; - } - - var startOffset = offset; - var length = MessagePackBinary.ReadMapHeaderRaw(bytes, offset, out readSize); - offset += readSize; - - var result = default(FieldMetadata); - for (var i = 0; i < length; i++) - { - var key = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); - offset += readSize; - switch (key) - { - case Keys.FieldName: - result = new FieldMetadata(MessagePackBinary.ReadString(bytes, offset, out readSize)); - offset += readSize; - break; - default: - offset += MessagePackBinary.ReadNext(bytes, offset); - break; - } - } - - readSize = offset - startOffset; - - return result; - } - } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/IndexCreationOptions.cs b/src/progaudi.tarantool/Model/IndexCreationOptions.cs index 27a1ecfe..5e934448 100644 --- a/src/progaudi.tarantool/Model/IndexCreationOptions.cs +++ b/src/progaudi.tarantool/Model/IndexCreationOptions.cs @@ -1,8 +1,8 @@ -using MessagePack; +using ProGaudi.MsgPack.Light; namespace ProGaudi.Tarantool.Client.Model { - [MessagePackObject] + [MsgPackMap] public class IndexCreationOptions { public IndexCreationOptions(bool unique) @@ -10,7 +10,11 @@ public IndexCreationOptions(bool unique) Unique = unique; } - [Key("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 df74735c..c37ecafd 100644 --- a/src/progaudi.tarantool/Model/IndexPart.cs +++ b/src/progaudi.tarantool/Model/IndexPart.cs @@ -1,12 +1,9 @@ -using System; -using System.Runtime.Serialization; -using MessagePack; -using MessagePack.Formatters; +using System.Runtime.Serialization; +using ProGaudi.MsgPack.Light; using ProGaudi.Tarantool.Client.Utils; namespace ProGaudi.Tarantool.Client.Model { - [MessagePackFormatter(typeof(Formatter))] public class IndexPart { public IndexPart(uint fieldNo, FieldType type) @@ -24,85 +21,121 @@ public override string ToString() return $"[{FieldNo}, {Type}]"; } - public sealed class Formatter : IMessagePackFormatter + public sealed class Formatter : IMsgPackConverter { - public int Serialize(ref byte[] bytes, int offset, IndexPart value, IFormatterResolver formatterResolver) + private IMsgPackConverter _uintConverter; + private IMsgPackConverter _indexPartTypeConverter; + private IMsgPackConverter _stringConverter; + + public void Initialize(MsgPackContext context) { - throw new NotImplementedException(); + _uintConverter = context.GetConverter(); + _indexPartTypeConverter = context.GetConverter(); + _stringConverter = context.GetConverter(); } - public IndexPart Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + public void Write(IndexPart value, IMsgPackWriter writer) => throw new System.NotImplementedException(); + + public IndexPart Read(IMsgPackReader reader) { - var type = MessagePackBinary.GetMessagePackType(bytes, offset); + var type = reader.ReadDataType(); switch (type) { - case MessagePackType.Nil: - readSize = 1; - return null; - case MessagePackType.Array: - return ReadFromArray(bytes, offset, formatterResolver, out readSize); - case MessagePackType.Map: - return ReadFromMap(bytes, offset, formatterResolver, out readSize); - default: - throw new ArgumentOutOfRangeException(); + 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 static IndexPart ReadFromMap(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + private IndexPart ReadFromArray(IMsgPackReader reader, uint length) { - var startOffset = offset; - var length = MessagePackBinary.ReadArrayHeaderRaw(bytes, offset, out readSize); - offset += readSize; + 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++) { - var key = MessagePackBinary.ReadString(bytes, offset, out readSize); - offset += readSize; - switch (key) + switch (_stringConverter.Read(reader)) { case "field": - fieldNo = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); - offset += readSize; + fieldNo = _uintConverter.Read(reader); break; case "type": - indexPartType = formatterResolver.GetFormatter().Deserialize(bytes, offset, formatterResolver, out readSize); - offset += readSize; + indexPartType = _indexPartTypeConverter.Read(reader); break; default: - offset += MessagePackBinary.ReadNext(bytes, offset); + reader.SkipToken(); break; } } - readSize = offset - startOffset; + if (fieldNo.HasValue && indexPartType.HasValue) + { + return new IndexPart(fieldNo.Value, indexPartType.Value); + } - return fieldNo.HasValue && indexPartType.HasValue - ? new IndexPart(fieldNo.Value, indexPartType.Value) - : throw new SerializationException("Can't read fieldNo or indexPart from map of index metadata"); + throw new SerializationException("Can't read fieldNo or indexPart from map of index metadata"); } - private static IndexPart ReadFromArray(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + private static uint? TryGetLengthFromFixArray(DataTypes type) { - var startOffset = offset; - var length = MessagePackBinary.ReadArrayHeaderRaw(bytes, offset, out readSize); - offset += readSize; + var length = type - DataTypes.FixArray; + return type.GetHighBits(4) == DataTypes.FixArray.GetHighBits(4) ? length : (uint?)null; + } - if (length != 2u) - { - throw ExceptionHelper.InvalidArrayLength(2u, length); - } + private static uint? TryGetLengthFromFixMap(DataTypes type) + { + var length = type - DataTypes.FixMap; + return type.GetHighBits(4) == DataTypes.FixMap.GetHighBits(4) ? length : (uint?)null; + } - var fieldNo = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); - offset += readSize; - var indexPartType = formatterResolver.GetFormatter().Deserialize(bytes, offset, formatterResolver, out readSize); - offset += readSize; + internal static ushort ReadUInt16(IMsgPackReader reader) + { + return (ushort)((reader.ReadByte() << 8) + reader.ReadByte()); + } - readSize = offset - startOffset; + 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 new IndexPart(fieldNo, indexPartType); + return temp; } } } diff --git a/src/progaudi.tarantool/Model/InsertReplaceRequest.cs b/src/progaudi.tarantool/Model/InsertReplaceRequest.cs index 37c9b4c0..85057e76 100644 --- a/src/progaudi.tarantool/Model/InsertReplaceRequest.cs +++ b/src/progaudi.tarantool/Model/InsertReplaceRequest.cs @@ -1,6 +1,5 @@ -using MessagePack; -using MessagePack.Formatters; -using static MessagePack.MessagePackBinary; +using System; +using ProGaudi.MsgPack.Light; namespace ProGaudi.Tarantool.Client.Model { @@ -19,24 +18,31 @@ protected InsertReplaceRequest(CommandCodes code, uint spaceId, T tuple) public CommandCodes Code { get; } - public sealed class Formatter : IMessagePackFormatter> + public sealed class Formatter : IMsgPackConverter> { - public int Serialize(ref byte[] bytes, int offset, InsertReplaceRequest value, IFormatterResolver formatterResolver) + private IMsgPackConverter _uintConverter; + private IMsgPackConverter _tupleConverter; + + public void Initialize(MsgPackContext context) + { + _uintConverter = context.GetConverter(); + _tupleConverter = context.GetConverter(); + } + + public void Write(InsertReplaceRequest value, IMsgPackWriter writer) { - var startOffset = offset; + writer.WriteMapHeader(2); - offset += WriteFixedMapHeaderUnsafe(ref bytes, offset, 2); - offset += WriteUInt32(ref bytes, offset, Keys.SpaceId); - offset += WriteUInt32(ref bytes, offset, value.SpaceId); - offset += WriteUInt32(ref bytes, offset, Keys.Tuple); - offset += formatterResolver.GetFormatter().Serialize(ref bytes, offset, value.Tuple, formatterResolver); + _uintConverter.Write(Keys.SpaceId, writer); + _uintConverter.Write(value.SpaceId, writer); - return offset - startOffset; + _uintConverter.Write(Keys.Tuple, writer); + _tupleConverter.Write(value.Tuple, writer); } - public InsertReplaceRequest Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + public InsertReplaceRequest Read(IMsgPackReader reader) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } } } diff --git a/src/progaudi.tarantool/Model/PackerResolver.cs b/src/progaudi.tarantool/Model/PackerResolver.cs deleted file mode 100644 index 110de932..00000000 --- a/src/progaudi.tarantool/Model/PackerResolver.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using MessagePack; -using MessagePack.Formatters; - -namespace ProGaudi.Tarantool.Client.Model -{ - public sealed class PackerResolver : IFormatterResolver - { - public static readonly PackerResolver Instance = new PackerResolver(); - - private PackerResolver() - { - } - - public IMessagePackFormatter GetFormatter() - { - return Cache.Formatter; - } - - private static class Cache - { - public static readonly IMessagePackFormatter Formatter; - - static Cache() - { - var type = typeof(T); - - if (type == typeof(DataResponse)) - { - Formatter = new DataResponse.Formatter() as IMessagePackFormatter; - return; - } - - if (!type.IsGenericType) return; - - var genericTypeDefinition = type.GetGenericTypeDefinition(); - if (genericTypeDefinition == typeof(CallRequest<>)) - { - var result = typeof(CallRequest<>.Formatter).MakeGenericType(type.GenericTypeArguments); - Formatter = (IMessagePackFormatter) Activator.CreateInstance(result); - return; - } - - if ( - genericTypeDefinition == typeof(InsertReplaceRequest<>) || - genericTypeDefinition == typeof(ReplaceRequest<>) || - genericTypeDefinition == typeof(InsertRequest<>) - ) - { - var result = typeof(InsertReplaceRequest<>.Formatter).MakeGenericType(type.GenericTypeArguments); - Formatter = (IMessagePackFormatter) Activator.CreateInstance(result); - return; - } - - if (genericTypeDefinition == typeof(SelectRequest<>)) - { - var result = typeof(SelectRequest<>.Formatter).MakeGenericType(type.GenericTypeArguments); - Formatter = (IMessagePackFormatter) Activator.CreateInstance(result); - return; - } - - if (genericTypeDefinition == typeof(DataResponse<>)) - { - var result = typeof(DataResponse<>.Formatter).MakeGenericType(type.GenericTypeArguments); - Formatter = (IMessagePackFormatter) Activator.CreateInstance(result); - } - } - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/Model/RequestHeader.cs b/src/progaudi.tarantool/Model/RequestHeader.cs index 5e7080f7..ce1cc0e0 100644 --- a/src/progaudi.tarantool/Model/RequestHeader.cs +++ b/src/progaudi.tarantool/Model/RequestHeader.cs @@ -1,10 +1,8 @@ -using MessagePack; -using MessagePack.Formatters; -using static MessagePack.MessagePackBinary; +using System; +using ProGaudi.MsgPack.Light; namespace ProGaudi.Tarantool.Client.Model { - [MessagePackFormatter(typeof(Formatter))] public class RequestHeader : HeaderBase { public RequestHeader(CommandCodes code, RequestId requestId) @@ -12,24 +10,39 @@ public RequestHeader(CommandCodes code, RequestId requestId) { } - public sealed class Formatter : IMessagePackFormatter + public sealed class Formatter : IMsgPackConverter { - public int Serialize(ref byte[] bytes, int offset, RequestHeader value, IFormatterResolver formatterResolver) + private IMsgPackConverter _uintConverter; + private IMsgPackConverter _ulongConverter; + private IMsgPackConverter _nullConverter; + + public void Initialize(MsgPackContext context) { - var startOffset = offset; + _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); - offset += WriteFixedMapHeaderUnsafe(ref bytes, offset, 2); - offset += WriteUInt32(ref bytes, offset, Keys.Code); - offset += WriteUInt32(ref bytes, offset, (uint)value.Code); - offset += WriteUInt32(ref bytes, offset, Keys.Sync); - offset += WriteUInt64(ref bytes, offset, value.RequestId.Value); + _uintConverter.Write(Keys.Code, writer); + _uintConverter.Write((uint)value.Code, writer); - return offset - startOffset; + _uintConverter.Write(Keys.Sync, writer); + _ulongConverter.Write(value.RequestId.Value, writer); } - public RequestHeader Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + public RequestHeader Read(IMsgPackReader reader) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } } } diff --git a/src/progaudi.tarantool/Model/ResponseHeader.cs b/src/progaudi.tarantool/Model/ResponseHeader.cs index 806a70c5..3a34462f 100644 --- a/src/progaudi.tarantool/Model/ResponseHeader.cs +++ b/src/progaudi.tarantool/Model/ResponseHeader.cs @@ -1,10 +1,9 @@ -using MessagePack; -using MessagePack.Formatters; +using System; +using ProGaudi.MsgPack.Light; using ProGaudi.Tarantool.Client.Utils; namespace ProGaudi.Tarantool.Client.Model { - [MessagePackFormatter(typeof(Formatter))] public class ResponseHeader : HeaderBase { public ResponseHeader(CommandCodes code, RequestId requestId, ulong? schemaId) : base(code, requestId) @@ -14,51 +13,58 @@ public ResponseHeader(CommandCodes code, RequestId requestId, ulong? schemaId) : public ulong? SchemaId { get; } - public sealed class Formatter : IMessagePackFormatter + public sealed class Formatter : IMsgPackConverter { - public int Serialize(ref byte[] bytes, int offset, ResponseHeader value, IFormatterResolver formatterResolver) + 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 System.NotImplementedException(); + throw new NotImplementedException(); } - public ResponseHeader Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + public ResponseHeader Read(IMsgPackReader reader) { - var startOffset = offset; - var length = MessagePackBinary.ReadMapHeaderRaw(bytes, offset, out readSize); - offset += readSize; + var length = reader.ReadMapLength(); + + if (!length.HasValue) + { + throw ExceptionHelper.InvalidMapLength(length, 2u, 3u); + } CommandCodes? code = null; RequestId? sync = null; ulong? schemaId = null; - for (var i = 0; i < length; i++) + for (int i = 0; i < length.Value; i++) { - var key = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); - offset += readSize; + var key = _keyConverter.Read(reader); switch (key) { case Keys.Code: - code = (CommandCodes) MessagePackBinary.ReadUInt32(bytes, offset, out readSize); - offset += readSize; + code = _codeConverter.Read(reader); break; case Keys.Sync: - sync = new RequestId(MessagePackBinary.ReadUInt64(bytes, offset, out readSize)); - offset += readSize; + sync = new RequestId(_ulongConverter.Read(reader)); break; case Keys.SchemaId: - schemaId = MessagePackBinary.ReadUInt64(bytes, offset, out readSize); - offset += readSize; + schemaId = _ulongConverter.Read(reader); break; default: - readSize = MessagePackBinary.ReadNextBlock(bytes, offset); - offset += readSize; + reader.SkipToken(); break; } } - readSize = offset - startOffset; - if (!code.HasValue) { throw ExceptionHelper.PropertyUnspecified("Code"); diff --git a/src/progaudi.tarantool/Model/SelectRequest.cs b/src/progaudi.tarantool/Model/SelectRequest.cs index 339570ce..ba7cd167 100644 --- a/src/progaudi.tarantool/Model/SelectRequest.cs +++ b/src/progaudi.tarantool/Model/SelectRequest.cs @@ -1,6 +1,5 @@ -using MessagePack; -using MessagePack.Formatters; -using static MessagePack.MessagePackBinary; +using System; +using ProGaudi.MsgPack.Light; namespace ProGaudi.Tarantool.Client.Model { @@ -30,34 +29,45 @@ public SelectRequest(uint spaceId, uint indexId, uint limit, uint offset, Iterat public CommandCodes Code => CommandCodes.Select; - public sealed class Formatter : IMessagePackFormatter> + public sealed class Formatter : IMsgPackConverter> { - public int Serialize(ref byte[] bytes, int offset, SelectRequest value, IFormatterResolver formatterResolver) + private IMsgPackConverter _selectKeyConverter; + private IMsgPackConverter _uintConverter; + private IMsgPackConverter _iteratorConverter; + + public void Initialize(MsgPackContext context) { - var startOffset = offset; - - offset += WriteFixedMapHeaderUnsafe(ref bytes, offset, 6); - offset += WriteUInt32(ref bytes, offset, Keys.SpaceId); - offset += WriteUInt32(ref bytes, offset, value.SpaceId); - offset += WriteUInt32(ref bytes, offset, Keys.IndexId); - offset += WriteUInt32(ref bytes, offset, value.IndexId); - offset += WriteUInt32(ref bytes, offset, Keys.Limit); - offset += WriteUInt32(ref bytes, offset, value.Limit); - offset += WriteUInt32(ref bytes, offset, Keys.Offset); - offset += WriteUInt32(ref bytes, offset, value.Offset); - offset += WriteUInt32(ref bytes, offset, Keys.Iterator); - offset += WriteUInt32(ref bytes, offset, (uint)value.Iterator); - offset += WriteUInt32(ref bytes, offset, Keys.Key); - offset += formatterResolver - .GetFormatter() - .Serialize(ref bytes, offset, value.SelectKey, formatterResolver); - - return offset - startOffset; + _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 Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) + public SelectRequest Read(IMsgPackReader reader) { - throw new System.NotImplementedException(); + throw new NotSupportedException(); } } } diff --git a/src/progaudi.tarantool/Model/SpaceField.cs b/src/progaudi.tarantool/Model/SpaceField.cs index 0b3f9fc0..e05f567d 100644 --- a/src/progaudi.tarantool/Model/SpaceField.cs +++ b/src/progaudi.tarantool/Model/SpaceField.cs @@ -1,8 +1,8 @@ -using MessagePack; +using ProGaudi.MsgPack.Light; namespace ProGaudi.Tarantool.Client.Model { - [MessagePackObject] + [MsgPackMap] public class SpaceField { public SpaceField(string name, FieldType type) @@ -11,11 +11,15 @@ public SpaceField(string name, FieldType type) Type = type; } - [Key("name")] - public string Name { get; } + public SpaceField() + { + } + + [MsgPackMapElement("name")] + public string Name { get; set; } - [Key("type")] - public FieldType Type { get; } + [MsgPackMapElement("type")] + public FieldType Type { get; set; } public override string ToString() { diff --git a/src/progaudi.tarantool/Model/SpaceOptions.cs b/src/progaudi.tarantool/Model/SpaceOptions.cs index 3d13aec1..1b0f208e 100644 --- a/src/progaudi.tarantool/Model/SpaceOptions.cs +++ b/src/progaudi.tarantool/Model/SpaceOptions.cs @@ -1,11 +1,15 @@ -using MessagePack; +using ProGaudi.MsgPack.Light; namespace ProGaudi.Tarantool.Client.Model { - [MessagePackObject] + [MsgPackMap] public class SpaceOptions { - [Key("temporary")] + public SpaceOptions() + { + } + + [MsgPackMapElement("temporary")] public bool Temporary { get; set; } public override string ToString() diff --git a/src/progaudi.tarantool/Model/SqlInfo.cs b/src/progaudi.tarantool/Model/SqlInfo.cs index 016f6d1d..63d79ae2 100644 --- a/src/progaudi.tarantool/Model/SqlInfo.cs +++ b/src/progaudi.tarantool/Model/SqlInfo.cs @@ -1,9 +1,5 @@ -using MessagePack; -using MessagePack.Formatters; - -namespace ProGaudi.Tarantool.Client.Model +namespace ProGaudi.Tarantool.Client.Model { - [MessagePackFormatter(typeof(Formatter))] public class SqlInfo { public SqlInfo(int rowCount) @@ -12,47 +8,5 @@ public SqlInfo(int rowCount) } public int RowCount { get; } - - public sealed class Formatter : IMessagePackFormatter - { - public int Serialize(ref byte[] bytes, int offset, SqlInfo value, IFormatterResolver formatterResolver) - { - throw new System.NotImplementedException(); - } - - public SqlInfo Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) - { - if (MessagePackBinary.IsNil(bytes, offset)) - { - readSize = 1; - return null; - } - - var startOffset = offset; - var length = MessagePackBinary.ReadMapHeaderRaw(bytes, offset, out readSize); - offset += readSize; - - var result = default(SqlInfo); - for (var i = 0; i < length; i++) - { - var key = MessagePackBinary.ReadUInt32(bytes, offset, out readSize); - offset += readSize; - switch (key) - { - case Keys.SqlRowCount: - result = new SqlInfo(MessagePackBinary.ReadInt32(bytes, offset, out readSize)); - offset += readSize; - break; - default: - offset += MessagePackBinary.ReadNext(bytes, offset); - break; - } - } - - readSize = offset - startOffset; - - return result; - } - } } } \ No newline at end of file diff --git a/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs b/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs index aabbe5b2..fd07e50f 100644 --- a/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs +++ b/src/progaudi.tarantool/NetworkStreamPhysicalConnection.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.IO; using System.Linq; using System.Net.Sockets; @@ -47,10 +48,11 @@ public async Task Connect(ClientOptions options) options.LogWriter?.WriteLine("Socket connection established."); } - public void Write(in ArraySegment buffer) + public void Write(in ReadOnlySequence buffer) { CheckConnectionStatus(); - _stream.Write(buffer.Array, buffer.Offset, buffer.Count); + var array = buffer.ToArray(); + _stream.Write(array, 0, array.Length); } public Stream Stream => _stream; diff --git a/src/progaudi.tarantool/RequestLength.cs b/src/progaudi.tarantool/RequestLength.cs deleted file mode 100644 index 9465d8ca..00000000 --- a/src/progaudi.tarantool/RequestLength.cs +++ /dev/null @@ -1,33 +0,0 @@ -using MessagePack; -using MessagePack.Formatters; - -namespace ProGaudi.Tarantool.Client -{ - [MessagePackFormatter(typeof(Formatter))] - public readonly struct RequestLength - { - private readonly uint _value; - - public RequestLength(byte[] header, byte[] body) => _value = (uint) (header.Length + body.Length); - - public RequestLength(uint length) => _value = length; - - public static implicit operator uint(RequestLength length) - { - return length._value; - } - - public sealed class Formatter : IMessagePackFormatter - { - public int Serialize(ref byte[] bytes, int offset, RequestLength value, IFormatterResolver formatterResolver) - { - return MessagePackBinary.WriteUInt32ForceUInt32Block(ref bytes, offset, value); - } - - public RequestLength Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize) - { - return new RequestLength(MessagePackBinary.ReadUInt32(bytes, offset, out readSize)); - } - } - } -} \ No newline at end of file diff --git a/src/progaudi.tarantool/RequestWriter.cs b/src/progaudi.tarantool/RequestWriter.cs index 3cc49e01..203eecde 100644 --- a/src/progaudi.tarantool/RequestWriter.cs +++ b/src/progaudi.tarantool/RequestWriter.cs @@ -10,7 +10,7 @@ internal class RequestWriter : IRequestWriter { private readonly ClientOptions _clientOptions; private readonly IPhysicalConnection _physicalConnection; - private readonly Queue> _buffer; + private readonly Queue> _buffer; private readonly object _lock = new object(); private readonly Thread _thread; private readonly ManualResetEventSlim _exitEvent; @@ -21,7 +21,7 @@ public RequestWriter(ClientOptions clientOptions, IPhysicalConnection physicalCo { _clientOptions = clientOptions; _physicalConnection = physicalConnection; - _buffer = new Queue>(); + _buffer = new Queue>(); _thread = new Thread(WriteFunction) { IsBackground = true, @@ -44,14 +44,14 @@ public void BeginWriting() public bool IsConnected => !_disposed; - public void Write(in ArraySegment body) + public void Write(in ReadOnlySequence body) { if (_disposed) { throw new ObjectDisposedException(nameof(RequestWriter)); } - _clientOptions?.LogWriter?.WriteLine($"Enqueuing request: body {body.Count} bytes."); + _clientOptions?.LogWriter?.WriteLine($"Enqueuing request: body {body.Length} bytes."); bool shouldSignal; lock (_lock) { @@ -101,7 +101,7 @@ private void WriteRequests(int limit) var count = 0; while (true) { - ArraySegment request; + ReadOnlySequence request; lock (_lock) { if (_buffer.Count > 0) @@ -110,12 +110,11 @@ private void WriteRequests(int limit) break; } - _clientOptions?.LogWriter?.WriteLine($"Writing request: {request.Count} bytes."); + _clientOptions.LogWriter?.WriteLine($"Writing request: {request.Length} bytes."); _physicalConnection.Write(request); - ArrayPool.Shared.Return(request.Array); - _clientOptions?.LogWriter?.WriteLine($"Wrote request: {request.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 a2f70ffd..e1d0799f 100644 --- a/src/progaudi.tarantool/ResponseReader.cs +++ b/src/progaudi.tarantool/ResponseReader.cs @@ -1,12 +1,13 @@ 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 MessagePack; +using ProGaudi.MsgPack.Light; using ProGaudi.Tarantool.Client.Model; using ProGaudi.Tarantool.Client.Utils; @@ -74,11 +75,11 @@ public void Dispose() } } - private delegate void ResultSetter(in ArraySegment response); + private delegate void ResultSetter(in MemoryStream response); private delegate void ExceptionSetter(Exception ex); - public Task GetResponseTask(RequestId requestId, Func, TResponse> responseCreator) + public Task GetResponseTask(RequestId requestId, Func responseCreator) { if (_disposed) { @@ -99,7 +100,7 @@ public Task GetResponseTask(RequestId requestId, Func response) => tcs.SetResult(responseCreator(response)); + void ResultSetterImpl(in MemoryStream response) => tcs.SetResult(responseCreator(response)); void ExceptionSetterImpl(Exception ex) => tcs.SetException(ex); } @@ -225,8 +226,8 @@ private int TryParseResponses() private void MatchResult(in ArraySegment result) { - var headerFormatter = MessagePackSerializer.DefaultResolver.GetFormatter(); - var header = headerFormatter.Deserialize(result.Array, result.Offset, MessagePackSerializer.DefaultResolver, out var headerLength); + 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) @@ -236,16 +237,23 @@ private void MatchResult(in ArraySegment result) return; } - _clientOptions.LogWriter?.WriteLine($"Match for request with id {header.RequestId} found."); if ((header.Code & CommandCodes.ErrorMask) == CommandCodes.ErrorMask) { - var errorFormatter = MessagePackSerializer.DefaultResolver.GetFormatter(); - var errorResponse = errorFormatter.Deserialize(result.Array, result.Offset + headerLength, MessagePackSerializer.DefaultResolver, out _); + _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 { - tcs.Item1(new ArraySegment(result.Array, result.Offset + headerLength, result.Count - headerLength)); + _clientOptions.LogWriter?.WriteLine($"Match for request with id {header.RequestId} is ok."); + try + { + tcs.Item1(stream); + } + catch (Exception e) + { + tcs.Item2(e); + } } } @@ -274,16 +282,20 @@ private ArraySegment TryParseResponse() return Empty; } - var packetSize = _readingOffset - _parsingOffset < Constants.PacketSizeBufferSize - ? 0 - : (int)MessagePackBinary.ReadUInt64(_buffer, _parsingOffset, out _); - - if (packetSize == 0) + if (_readingOffset - _parsingOffset < Constants.PacketSizeBufferSize) { _clientOptions.LogWriter?.WriteLine($"Can't read packet length, has less than {Constants.PacketSizeBufferSize} bytes."); return Empty; } + var length = new ReadOnlySpan(_buffer, _parsingOffset, 5); + if (length[0] != (byte) DataTypes.UInt32) + { + throw ExceptionUtils.BadTypeException((DataTypes) length[0], DataTypes.UInt32); + } + + var packetSize = (int)BinaryPrimitives.ReadUInt32BigEndian(length.Slice(1)); + if (PacketCompletelyRead(packetSize)) { var offset = _parsingOffset += Constants.PacketSizeBufferSize; diff --git a/src/progaudi.tarantool/SocketPhysicalConnection.cs b/src/progaudi.tarantool/SocketPhysicalConnection.cs index c58a0f83..271ea92a 100644 --- a/src/progaudi.tarantool/SocketPhysicalConnection.cs +++ b/src/progaudi.tarantool/SocketPhysicalConnection.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.IO; using System.Linq; using System.Net.Sockets; @@ -51,7 +52,7 @@ public Task ReadAsync(byte[] buffer, int offset, int count) throw new NotImplementedException(); } - public void Write(in ArraySegment buffer) + public void Write(in ReadOnlySequence buffer) { throw new NotImplementedException(); } diff --git a/src/progaudi.tarantool/Space.cs b/src/progaudi.tarantool/Space.cs index 3c56f114..e186ece0 100644 --- a/src/progaudi.tarantool/Space.cs +++ b/src/progaudi.tarantool/Space.cs @@ -10,13 +10,13 @@ public class Space : ISpace { private readonly Schema _schema; private readonly SpaceMeta _meta; - private readonly Lazy> _indices; + private readonly Lazy> _indices; public Space(Schema schema, SpaceMeta meta, IEnumerable indexMetas) { _schema = schema; _meta = meta; - _indices = new Lazy>(() => new NameIdLazyWrapper(indexMetas.Select(x => (IndexMeta?)x).ToArray(), x => x.Value.Id, x => x.Value.Name)); + _indices = new Lazy>(() => new NameIdLazyWrapper(indexMetas.ToArray(), x => x.Id, x => x.Name)); } public override string ToString() @@ -56,7 +56,7 @@ public bool TryGetIndex(string name, out IIndex inde return false; } - index = new Index(meta.Value, _schema); + index = new Index(meta, _schema); return true; } @@ -70,7 +70,7 @@ public bool TryGetIndex(uint id, out IIndex index) return false; } - index = new Index(meta.Value, _schema); + index = new Index(meta, _schema); return true; } diff --git a/src/progaudi.tarantool/SpaceMeta.cs b/src/progaudi.tarantool/SpaceMeta.cs index 800dbc40..af0ad801 100644 --- a/src/progaudi.tarantool/SpaceMeta.cs +++ b/src/progaudi.tarantool/SpaceMeta.cs @@ -1,12 +1,16 @@ using System; -using MessagePack; +using ProGaudi.MsgPack.Light; using ProGaudi.Tarantool.Client.Model; namespace ProGaudi.Tarantool.Client { - [MessagePackObject] - public struct SpaceMeta : IEquatable + [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; @@ -18,26 +22,26 @@ public SpaceMeta(uint id, int ownerId, string name, StorageEngine engine, uint f Fields = fields; } - [Key(0)] - public uint Id { get; } + [MsgPackArrayElement(0)] + public uint Id { get; set; } - [Key(1)] - public int OwnerId { get; } + [MsgPackArrayElement(1)] + public int OwnerId { get; set; } - [Key(2)] - public string Name { get; } + [MsgPackArrayElement(2)] + public string Name { get; set; } - [Key(3)] - public StorageEngine Engine { get; } + [MsgPackArrayElement(3)] + public StorageEngine Engine { get; set; } - [Key(4)] - public uint FieldCount { get; } + [MsgPackArrayElement(4)] + public uint FieldCount { get; set; } - [Key(5)] - public SpaceOptions Options { get; } + [MsgPackArrayElement(5)] + public SpaceOptions Options { get; set; } - [Key(6)] - public SpaceField[] Fields { get; } + [MsgPackArrayElement(6)] + public SpaceField[] Fields { get; set; } public override string ToString() { diff --git a/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs b/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs new file mode 100644 index 00000000..95d15297 --- /dev/null +++ b/src/progaudi.tarantool/TarantoolConvertersRegistrator.cs @@ -0,0 +1,71 @@ +using ProGaudi.MsgPack.Light; +using ProGaudi.Tarantool.Client.Converters; +using ProGaudi.Tarantool.Client.Model; + +namespace ProGaudi.Tarantool.Client +{ + public class TarantoolConvertersRegistrator + { + public static void Register(MsgPackContext context) + { + context.DiscoverConverters(); + + context.RegisterConverter(new EnumConverter()); + context.RegisterConverter(new EnumConverter()); + + context.RegisterConverter(new EnumAsStringFormatter()); + context.RegisterConverter(new EnumAsStringFormatter()); + context.RegisterConverter(new EnumAsStringFormatter()); + + //context.RegisterConverter(new StringSliceOperationConverter()); + //context.RegisterGenericConverter(typeof(UpdateOperationConverter<>)); + + context.RegisterConverter(new ResponseHeader.Formatter()); + context.RegisterConverter(new RequestHeader.Formatter()); + + context.RegisterConverter(new AuthenticationRequest.Formatter()); + context.RegisterConverter(new IndexPart.Formatter()); + //context.RegisterConverter(new ErrorResponse); + + //context.RegisterConverter(new TupleConverter()); + //context.RegisterConverter(new BoxInfo.Converter()); + + 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(SystemTupleConverter<>)); + context.RegisterGenericConverter(typeof(SystemTupleConverter<,>)); + context.RegisterGenericConverter(typeof(SystemTupleConverter<,,>)); + context.RegisterGenericConverter(typeof(SystemTupleConverter<,,,>)); + context.RegisterGenericConverter(typeof(SystemTupleConverter<,,,,>)); + context.RegisterGenericConverter(typeof(SystemTupleConverter<,,,,,>)); + context.RegisterGenericConverter(typeof(SystemTupleConverter<,,,,,,>)); + + context.RegisterGenericConverter(typeof(ValueTupleConverter<>)); + context.RegisterGenericConverter(typeof(ValueTupleConverter<,>)); + context.RegisterGenericConverter(typeof(ValueTupleConverter<,,>)); + context.RegisterGenericConverter(typeof(ValueTupleConverter<,,,>)); + context.RegisterGenericConverter(typeof(ValueTupleConverter<,,,,>)); + context.RegisterGenericConverter(typeof(ValueTupleConverter<,,,,,>)); + context.RegisterGenericConverter(typeof(ValueTupleConverter<,,,,,,>)); + } + } +} \ No newline at end of file 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/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/progaudi.tarantool.csproj b/src/progaudi.tarantool/progaudi.tarantool.csproj index 10f13360..2cb610be 100644 --- a/src/progaudi.tarantool/progaudi.tarantool.csproj +++ b/src/progaudi.tarantool/progaudi.tarantool.csproj @@ -29,7 +29,7 @@ - +