You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by is...@apache.org on 2022/10/04 08:12:30 UTC

[ignite-3] branch main updated: IGNITE-17426 C++ 3.0: Implement table API (#1154)

This is an automated email from the ASF dual-hosted git repository.

isapego pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 96139067d6 IGNITE-17426 C++ 3.0: Implement table API (#1154)
96139067d6 is described below

commit 96139067d6215ba6508b10c4fbdaa9b473752a4b
Author: Igor Sapego <is...@apache.org>
AuthorDate: Tue Oct 4 11:12:25 2022 +0300

    IGNITE-17426 C++ 3.0: Implement table API (#1154)
---
 modules/platforms/cpp/client-test/CMakeLists.txt   |   1 +
 .../platforms/cpp/client-test/src/gtest_logger.h   |  20 +-
 .../cpp/client-test/src/ignite_client_test.cpp     | 123 +----------
 .../src/ignite_runner_suite.h}                     |  27 +--
 modules/platforms/cpp/client-test/src/main.cpp     |  12 +-
 .../{ignite_client_test.cpp => tables_test.cpp}    |  83 ++++---
 .../cpp/client/include/ignite/table/tables.h       |  15 +-
 .../platforms/cpp/client/src/client_operation.h    |   3 +
 .../platforms/cpp/client/src/cluster_connection.h  |  17 +-
 modules/platforms/cpp/client/src/ignite_client.cpp |   2 +-
 .../platforms/cpp/client/src/node_connection.cpp   |   2 +-
 modules/platforms/cpp/client/src/node_connection.h |   2 +-
 .../platforms/cpp/client/src/response_handler.h    |   6 +-
 .../platforms/cpp/client/src/table/table_impl.h    |   6 +-
 modules/platforms/cpp/client/src/table/tables.cpp  |   4 +
 .../platforms/cpp/client/src/table/tables_impl.cpp |  28 ++-
 .../platforms/cpp/client/src/table/tables_impl.h   |  12 +-
 modules/platforms/cpp/common/factory.h             |  22 ++
 modules/platforms/cpp/common/guid.h                | 239 ---------------------
 modules/platforms/cpp/common/ignite_result.h       | 192 +++++++----------
 modules/platforms/cpp/common/uuid.h                |  28 +--
 .../include/ignite/network/length_prefix_codec.h   |  17 +-
 .../include/ignite/protocol/extension_types.h      |   2 +-
 .../cpp/protocol/include/ignite/protocol/reader.h  |  46 +++-
 .../cpp/protocol/include/ignite/protocol/utils.h   |  48 ++++-
 modules/platforms/cpp/protocol/src/reader.cpp      |  39 +---
 modules/platforms/cpp/protocol/src/utils.cpp       |  39 +++-
 27 files changed, 401 insertions(+), 634 deletions(-)

diff --git a/modules/platforms/cpp/client-test/CMakeLists.txt b/modules/platforms/cpp/client-test/CMakeLists.txt
index 84cc3d57a8..6a1b98f44d 100644
--- a/modules/platforms/cpp/client-test/CMakeLists.txt
+++ b/modules/platforms/cpp/client-test/CMakeLists.txt
@@ -28,6 +28,7 @@ enable_testing()
 set(SOURCES
     src/ignite_client_test.cpp
     src/main.cpp
+    src/tables_test.cpp
 )
 
 add_executable(${TARGET} ${SOURCES})
diff --git a/modules/platforms/cpp/client-test/src/gtest_logger.h b/modules/platforms/cpp/client-test/src/gtest_logger.h
index 5c501c5fdf..ebf9d1fba1 100644
--- a/modules/platforms/cpp/client-test/src/gtest_logger.h
+++ b/modules/platforms/cpp/client-test/src/gtest_logger.h
@@ -17,18 +17,20 @@
 
 #pragma once
 
+#include "ignite/ignite_logger.h"
+
+#include <gtest/gtest.h>
+
 #include <memory>
 #include <sstream>
 #include <string>
 
-#include <gtest/gtest.h>
-
 namespace ignite {
 
 /**
  * Test logger.
  */
-class GtestLogger : public IgniteLogger {
+class gtest_logger : public IgniteLogger {
 public:
     /**
      * Construct.
@@ -36,25 +38,25 @@ public:
      * @param includeTs Include timestamps.
      * @param debug Enable debug.
      */
-    GtestLogger(bool includeTs, bool debug)
+    gtest_logger(bool includeTs, bool debug)
         : m_includeTs(includeTs)
         , m_debug(debug) { }
 
     void logError(std::string_view message) override {
-        std::cout << "[          ] [ ERROR ]   " + std::string(message) + '\n' << std::flush;
+        std::cout << "[          ] [ ERROR ]   " + get_timestamp() + std::string(message) + '\n' << std::flush;
     }
 
     void logWarning(std::string_view message) override {
-        std::cout << "[          ] [ WARNING ] " + std::string(message) + '\n' << std::flush;
+        std::cout << "[          ] [ WARNING ] " + get_timestamp() + std::string(message) + '\n' << std::flush;
     }
 
     void logInfo(std::string_view message) override {
-        std::cout << "[          ] [ INFO ]    " + std::string(message) + '\n' << std::flush;
+        std::cout << "[          ] [ INFO ]    " + get_timestamp() + std::string(message) + '\n' << std::flush;
     }
 
     void logDebug(std::string_view message) override {
         if (m_debug)
-            std::cout << "[          ] [ DEBUG ]   " + std::string(message) + '\n' << std::flush;
+            std::cout << "[          ] [ DEBUG ]   " + get_timestamp() + std::string(message) + '\n' << std::flush;
     }
 
 private:
@@ -63,7 +65,7 @@ private:
      *
      * @return Timestamp string.
      */
-    [[nodiscard]] std::string getTimestamp() const {
+    [[nodiscard]] std::string get_timestamp() const {
         if (!m_includeTs)
             return {};
 
diff --git a/modules/platforms/cpp/client-test/src/ignite_client_test.cpp b/modules/platforms/cpp/client-test/src/ignite_client_test.cpp
index 7abb00898c..ef6f05cde7 100644
--- a/modules/platforms/cpp/client-test/src/ignite_client_test.cpp
+++ b/modules/platforms/cpp/client-test/src/ignite_client_test.cpp
@@ -15,40 +15,25 @@
  * limitations under the License.
  */
 
-#include <chrono>
-#include <string_view>
-#include <thread>
-
-#include <gtest/gtest.h>
+#include "ignite_runner_suite.h"
 
 #include "ignite/ignite_client.h"
 #include "ignite/ignite_client_configuration.h"
 
-#include "gtest_logger.h"
+#include <gtest/gtest.h>
 
-using namespace ignite;
+#include <chrono>
 
-static const std::initializer_list<std::string_view> NODE_ADDRS = {"127.0.0.1:10942", "127.0.0.1:10943"};
+using namespace ignite;
 
 /**
  * Test suite.
  */
-class ClientTest : public ::testing::Test {
-protected:
-    ClientTest() = default;
-    ~ClientTest() override = default;
+class client_test : public ignite_runner_suite {};
 
-    /**
-     * Get logger.
-     *
-     * @return Logger for tests.
-     */
-    static std::shared_ptr<GtestLogger> getLogger() { return std::make_shared<GtestLogger>(true, true); }
-};
-
-TEST_F(ClientTest, GetConfiguration) {
+TEST_F(client_test, get_configuration) {
     IgniteClientConfiguration cfg{NODE_ADDRS};
-    cfg.setLogger(getLogger());
+    cfg.setLogger(get_logger());
     cfg.setConnectionLimit(42);
 
     auto client = IgniteClient::start(cfg, std::chrono::seconds(5));
@@ -58,97 +43,3 @@ TEST_F(ClientTest, GetConfiguration) {
     EXPECT_EQ(cfg.getEndpoints(), cfg2.getEndpoints());
     EXPECT_EQ(cfg.getConnectionLimit(), cfg2.getConnectionLimit());
 }
-
-TEST_F(ClientTest, TablesGetTablePromises) {
-    IgniteClientConfiguration cfg{NODE_ADDRS};
-    cfg.setLogger(getLogger());
-
-    auto clientPromise = std::make_shared<std::promise<IgniteClient>>();
-    IgniteClient::startAsync(cfg, std::chrono::seconds(5), ignite_result<IgniteClient>::promise_setter(clientPromise));
-
-    auto client = clientPromise->get_future().get();
-
-    auto tables = client.getTables();
-
-    auto tablePromise = std::make_shared<std::promise<std::optional<Table>>>();
-    tables.getTableAsync("PUB.some_unknown", ignite_result<std::optional<Table>>::promise_setter(tablePromise));
-
-    auto tableUnknown = tablePromise->get_future().get();
-    EXPECT_FALSE(tableUnknown.has_value());
-
-    tablePromise = std::make_shared<std::promise<std::optional<Table>>>();
-    tables.getTableAsync("PUB.tbl1", ignite_result<std::optional<Table>>::promise_setter(tablePromise));
-
-    auto table = tablePromise->get_future().get();
-    ASSERT_TRUE(table.has_value());
-    EXPECT_EQ(table->getName(), "PUB.tbl1");
-}
-
-template <typename T>
-bool checkAndSetOperationError(std::promise<void> &operation, const ignite_result<T> &res) {
-    if (res.has_error()) {
-        operation.set_exception(std::make_exception_ptr(res.error()));
-        return false;
-    }
-    if (!res.has_value()) {
-        operation.set_exception(std::make_exception_ptr(ignite_error("There is no value in client result")));
-        return false;
-    }
-    return true;
-}
-
-TEST_F(ClientTest, TablesGetTableCallbacks) {
-    auto operation0 = std::make_shared<std::promise<void>>();
-    auto operation1 = std::make_shared<std::promise<void>>();
-    auto operation2 = std::make_shared<std::promise<void>>();
-
-    IgniteClientConfiguration cfg{NODE_ADDRS};
-    cfg.setLogger(getLogger());
-
-    IgniteClient client;
-
-    IgniteClient::startAsync(cfg, std::chrono::seconds(5), [&](ignite_result<IgniteClient> clientRes) {
-        if (!checkAndSetOperationError(*operation0, clientRes))
-            return;
-
-        client = std::move(clientRes).value();
-        auto tables = client.getTables();
-
-        operation0->set_value();
-        tables.getTableAsync("PUB.some_unknown", [&](auto tableRes) {
-            if (!checkAndSetOperationError(*operation1, tableRes))
-                return;
-
-            auto tableUnknown = std::move(tableRes).value();
-            if (tableUnknown.has_value()) {
-                operation1->set_exception(std::make_exception_ptr(ignite_error("Table should be null")));
-                return;
-            }
-
-            operation1->set_value();
-        });
-
-        tables.getTableAsync("PUB.tbl1", [&](auto tableRes) {
-            if (!checkAndSetOperationError(*operation2, tableRes))
-                return;
-
-            auto table = std::move(tableRes).value();
-            if (!table.has_value()) {
-                operation2->set_exception(std::make_exception_ptr(ignite_error("Table should not be null")));
-                return;
-            }
-            if (table->getName() != "PUB.tbl1") {
-                operation2->set_exception(
-                    std::make_exception_ptr(ignite_error("Table has unexpected name: " + table->getName())));
-                return;
-            }
-
-            operation2->set_value();
-        });
-    });
-
-    // Waiting for all operations to complete
-    operation0->get_future().get();
-    operation1->get_future().get();
-    operation2->get_future().get();
-}
diff --git a/modules/platforms/cpp/common/factory.h b/modules/platforms/cpp/client-test/src/ignite_runner_suite.h
similarity index 64%
copy from modules/platforms/cpp/common/factory.h
copy to modules/platforms/cpp/client-test/src/ignite_runner_suite.h
index dd58c592d6..4904dc43b4 100644
--- a/modules/platforms/cpp/common/factory.h
+++ b/modules/platforms/cpp/client-test/src/ignite_runner_suite.h
@@ -17,29 +17,30 @@
 
 #pragma once
 
+#include "gtest_logger.h"
+
+#include <gtest/gtest.h>
+
 #include <memory>
 
 namespace ignite {
 
 /**
- * Factory class.
- *
- * @tparam T Instances of this type factory builds.
+ * Test suite.
  */
-template <typename T>
-class factory {
-public:
-    /**
-     * Destructor.
-     */
-    virtual ~factory() = default;
+class ignite_runner_suite : public ::testing::Test {
+protected:
+    static constexpr std::initializer_list<std::string_view> NODE_ADDRS = {"127.0.0.1:10942", "127.0.0.1:10943"};
+
+    ignite_runner_suite() = default;
+    ~ignite_runner_suite() override = default;
 
     /**
-     * Build instance.
+     * Get logger.
      *
-     * @return New instance of type @c T.
+     * @return Logger for tests.
      */
-    virtual std::unique_ptr<T> build() = 0;
+    static std::shared_ptr<gtest_logger> get_logger() { return std::make_shared<gtest_logger>(false, true); }
 };
 
 } // namespace ignite
diff --git a/modules/platforms/cpp/client-test/src/main.cpp b/modules/platforms/cpp/client-test/src/main.cpp
index a61126d273..cbe91c3325 100644
--- a/modules/platforms/cpp/client-test/src/main.cpp
+++ b/modules/platforms/cpp/client-test/src/main.cpp
@@ -15,18 +15,18 @@
  * limitations under the License.
  */
 
-#include <chrono>
-#include <thread>
+#include "ignite_runner.h"
+#include "common/ignite_error.h"
 
 #include <gtest/gtest.h>
 
-#include "common/ignite_error.h"
-#include "ignite_runner.h"
+#include <chrono>
+#include <thread>
 
 /**
  * Run prior to any other tests.
  */
-void BeforeAll() {
+void before_all() {
     ignite::IgniteRunner runner;
 
     // Ignite dry run to make sure everything is built, all artifacts downloaded
@@ -40,7 +40,7 @@ void BeforeAll() {
 
 int main(int argc, char **argv) {
     int res = 0;
-    BeforeAll();
+    before_all();
     ignite::IgniteRunner runner;
     try {
         runner.start(false);
diff --git a/modules/platforms/cpp/client-test/src/ignite_client_test.cpp b/modules/platforms/cpp/client-test/src/tables_test.cpp
similarity index 71%
copy from modules/platforms/cpp/client-test/src/ignite_client_test.cpp
copy to modules/platforms/cpp/client-test/src/tables_test.cpp
index 7abb00898c..fc82788517 100644
--- a/modules/platforms/cpp/client-test/src/ignite_client_test.cpp
+++ b/modules/platforms/cpp/client-test/src/tables_test.cpp
@@ -15,69 +15,43 @@
  * limitations under the License.
  */
 
-#include <chrono>
-#include <string_view>
-#include <thread>
-
-#include <gtest/gtest.h>
+#include "ignite_runner_suite.h"
 
 #include "ignite/ignite_client.h"
 #include "ignite/ignite_client_configuration.h"
 
-#include "gtest_logger.h"
+#include <gtest/gtest.h>
 
-using namespace ignite;
+#include <algorithm>
+#include <chrono>
+#include <thread>
 
-static const std::initializer_list<std::string_view> NODE_ADDRS = {"127.0.0.1:10942", "127.0.0.1:10943"};
+using namespace ignite;
 
 /**
  * Test suite.
  */
-class ClientTest : public ::testing::Test {
-protected:
-    ClientTest() = default;
-    ~ClientTest() override = default;
-
-    /**
-     * Get logger.
-     *
-     * @return Logger for tests.
-     */
-    static std::shared_ptr<GtestLogger> getLogger() { return std::make_shared<GtestLogger>(true, true); }
-};
-
-TEST_F(ClientTest, GetConfiguration) {
-    IgniteClientConfiguration cfg{NODE_ADDRS};
-    cfg.setLogger(getLogger());
-    cfg.setConnectionLimit(42);
-
-    auto client = IgniteClient::start(cfg, std::chrono::seconds(5));
-
-    const auto &cfg2 = client.getConfiguration();
+class tables_test : public ignite_runner_suite {};
 
-    EXPECT_EQ(cfg.getEndpoints(), cfg2.getEndpoints());
-    EXPECT_EQ(cfg.getConnectionLimit(), cfg2.getConnectionLimit());
-}
-
-TEST_F(ClientTest, TablesGetTablePromises) {
+TEST_F(tables_test, tables_get_table_async_promises) {
     IgniteClientConfiguration cfg{NODE_ADDRS};
-    cfg.setLogger(getLogger());
+    cfg.setLogger(get_logger());
 
     auto clientPromise = std::make_shared<std::promise<IgniteClient>>();
-    IgniteClient::startAsync(cfg, std::chrono::seconds(5), ignite_result<IgniteClient>::promise_setter(clientPromise));
+    IgniteClient::startAsync(cfg, std::chrono::seconds(5), result_promise_setter(clientPromise));
 
     auto client = clientPromise->get_future().get();
 
     auto tables = client.getTables();
 
     auto tablePromise = std::make_shared<std::promise<std::optional<Table>>>();
-    tables.getTableAsync("PUB.some_unknown", ignite_result<std::optional<Table>>::promise_setter(tablePromise));
+    tables.getTableAsync("PUB.some_unknown", result_promise_setter(tablePromise));
 
     auto tableUnknown = tablePromise->get_future().get();
     EXPECT_FALSE(tableUnknown.has_value());
 
     tablePromise = std::make_shared<std::promise<std::optional<Table>>>();
-    tables.getTableAsync("PUB.tbl1", ignite_result<std::optional<Table>>::promise_setter(tablePromise));
+    tables.getTableAsync("PUB.tbl1", result_promise_setter(tablePromise));
 
     auto table = tablePromise->get_future().get();
     ASSERT_TRUE(table.has_value());
@@ -85,7 +59,7 @@ TEST_F(ClientTest, TablesGetTablePromises) {
 }
 
 template <typename T>
-bool checkAndSetOperationError(std::promise<void> &operation, const ignite_result<T> &res) {
+bool check_and_set_operation_error(std::promise<void> &operation, const ignite_result<T> &res) {
     if (res.has_error()) {
         operation.set_exception(std::make_exception_ptr(res.error()));
         return false;
@@ -97,18 +71,18 @@ bool checkAndSetOperationError(std::promise<void> &operation, const ignite_resul
     return true;
 }
 
-TEST_F(ClientTest, TablesGetTableCallbacks) {
+TEST_F(tables_test, tables_get_table_async_callbacks) {
     auto operation0 = std::make_shared<std::promise<void>>();
     auto operation1 = std::make_shared<std::promise<void>>();
     auto operation2 = std::make_shared<std::promise<void>>();
 
     IgniteClientConfiguration cfg{NODE_ADDRS};
-    cfg.setLogger(getLogger());
+    cfg.setLogger(get_logger());
 
     IgniteClient client;
 
     IgniteClient::startAsync(cfg, std::chrono::seconds(5), [&](ignite_result<IgniteClient> clientRes) {
-        if (!checkAndSetOperationError(*operation0, clientRes))
+        if (!check_and_set_operation_error(*operation0, clientRes))
             return;
 
         client = std::move(clientRes).value();
@@ -116,7 +90,7 @@ TEST_F(ClientTest, TablesGetTableCallbacks) {
 
         operation0->set_value();
         tables.getTableAsync("PUB.some_unknown", [&](auto tableRes) {
-            if (!checkAndSetOperationError(*operation1, tableRes))
+            if (!check_and_set_operation_error(*operation1, tableRes))
                 return;
 
             auto tableUnknown = std::move(tableRes).value();
@@ -129,7 +103,7 @@ TEST_F(ClientTest, TablesGetTableCallbacks) {
         });
 
         tables.getTableAsync("PUB.tbl1", [&](auto tableRes) {
-            if (!checkAndSetOperationError(*operation2, tableRes))
+            if (!check_and_set_operation_error(*operation2, tableRes))
                 return;
 
             auto table = std::move(tableRes).value();
@@ -152,3 +126,24 @@ TEST_F(ClientTest, TablesGetTableCallbacks) {
     operation1->get_future().get();
     operation2->get_future().get();
 }
+
+TEST_F(tables_test, tables_get_tables_async_promises) {
+    IgniteClientConfiguration cfg{NODE_ADDRS};
+    cfg.setLogger(get_logger());
+
+    auto client = IgniteClient::start(cfg, std::chrono::seconds(5));
+
+    auto tablesApi = client.getTables();
+
+    auto tablesPromise = std::make_shared<std::promise<std::vector<Table>>>();
+    tablesApi.getTablesAsync(result_promise_setter(tablesPromise));
+
+    auto tables = tablesPromise->get_future().get();
+    ASSERT_GT(tables.size(), 0);
+
+    auto it = std::find_if(tables.begin(), tables.end(), [] (auto& table) {
+        return table.getName() == "PUB.TBL1";
+    });
+
+    ASSERT_NE(it, tables.end());
+}
diff --git a/modules/platforms/cpp/client/include/ignite/table/tables.h b/modules/platforms/cpp/client/include/ignite/table/tables.h
index 358a4245de..fad3cadd90 100644
--- a/modules/platforms/cpp/client/include/ignite/table/tables.h
+++ b/modules/platforms/cpp/client/include/ignite/table/tables.h
@@ -57,12 +57,21 @@ public:
      *   "public.tbl0" - the table "PUBLIC.TBL0" will be looked up,
      *   "PUBLIC.\"Tbl0\"" - "PUBLIC.Tbl0",
      *   "\"MySchema\".\"Tbl0\"" - "MySchema.Tbl0", etc.
-     * @param callback Callback to be called once operation is complete.
-     * @return Table with corresponding name or @c std::nullopt if the table does not exist.
-     * @throw ignite_error In case of error.
+     * @param callback Callback to be called once operation is complete. On success, the callback is invoked with
+     *    an instance of the table with corresponding name or @c std::nullopt if the table does not exist.
+     * @throw ignite_error In case of error while trying to send a request.
      */
     IGNITE_API void getTableAsync(const std::string &name, ignite_callback<std::optional<Table>> callback);
 
+    /**
+     * Gets all tables.
+     *
+     * @param callback Callback to be called once operation is complete. On success, the callback is invoked with
+     *    a vector of all tables.
+     * @throw ignite_error In case of error while trying to send a request.
+     */
+    IGNITE_API void getTablesAsync(ignite_callback<std::vector<Table>> callback);
+
 private:
     /**
      * Constructor
diff --git a/modules/platforms/cpp/client/src/client_operation.h b/modules/platforms/cpp/client/src/client_operation.h
index d186c79b35..298dbf4f69 100644
--- a/modules/platforms/cpp/client/src/client_operation.h
+++ b/modules/platforms/cpp/client/src/client_operation.h
@@ -23,6 +23,9 @@ namespace ignite::detail {
  * Client operation code.
  */
 enum class ClientOperation {
+    /** Get all tables. */
+    TABLES_GET = 3,
+
     /** Get table. */
     TABLE_GET = 4,
 
diff --git a/modules/platforms/cpp/client/src/cluster_connection.h b/modules/platforms/cpp/client/src/cluster_connection.h
index 467942b52a..4a232bb09d 100644
--- a/modules/platforms/cpp/client/src/cluster_connection.h
+++ b/modules/platforms/cpp/client/src/cluster_connection.h
@@ -86,8 +86,6 @@ public:
 
     /**
      * Stop connection.
-     *
-     * @return Future representing finishing of the connection process.
      */
     void stop();
 
@@ -97,8 +95,7 @@ public:
      * @tparam T Result type.
      * @param op Operation code.
      * @param wr Writer function.
-     * @param rd Reader function.
-     * @return Future result.
+     * @param handler Response handler.
      */
     template <typename T>
     void performRequest(ClientOperation op, const std::function<void(protocol::Writer &)> &wr,
@@ -114,6 +111,18 @@ public:
         }
     }
 
+    /**
+     * Perform request.
+     *
+     * @tparam T Result type.
+     * @param op Operation code.
+     * @param handler Response handler.
+     */
+    template <typename T>
+    void performRequest(ClientOperation op, std::shared_ptr<ResponseHandlerImpl<T>> handler) {
+        performRequest(op, [](protocol::Writer&) {}, std::move(handler));
+    }
+
 private:
     /**
      * Get random node connection.
diff --git a/modules/platforms/cpp/client/src/ignite_client.cpp b/modules/platforms/cpp/client/src/ignite_client.cpp
index d7b09770e7..7b5a0febd7 100644
--- a/modules/platforms/cpp/client/src/ignite_client.cpp
+++ b/modules/platforms/cpp/client/src/ignite_client.cpp
@@ -27,7 +27,7 @@ void IgniteClient::startAsync(IgniteClientConfiguration configuration, std::chro
     ignite_callback<IgniteClient> callback) {
     // TODO: IGNITE-17762 Async start should not require starting thread internally. Replace with async timer.
     (void)std::async([cfg = std::move(configuration), timeout, callback = std::move(callback)]() mutable {
-        auto res = ignite_result<IgniteClient>::of_operation(
+        auto res = result_of_operation<IgniteClient>(
             [cfg = std::move(cfg), timeout]() { return start(cfg, timeout); });
         callback(std::move(res));
     });
diff --git a/modules/platforms/cpp/client/src/node_connection.cpp b/modules/platforms/cpp/client/src/node_connection.cpp
index b39bc75e67..5903eabcdb 100644
--- a/modules/platforms/cpp/client/src/node_connection.cpp
+++ b/modules/platforms/cpp/client/src/node_connection.cpp
@@ -30,7 +30,7 @@ NodeConnection::NodeConnection(
 
 NodeConnection::~NodeConnection() {
     for (auto &handler : m_requestHandlers) {
-        auto handlingRes = ignite_result<void>::of_operation([&]() {
+        auto handlingRes = result_of_operation<void>([&]() {
             auto res = handler.second->setError(ignite_error("Connection closed before response was received"));
             if (res.has_error())
                 m_logger->logError(
diff --git a/modules/platforms/cpp/client/src/node_connection.h b/modules/platforms/cpp/client/src/node_connection.h
index d506ca5031..81451b2e29 100644
--- a/modules/platforms/cpp/client/src/node_connection.h
+++ b/modules/platforms/cpp/client/src/node_connection.h
@@ -88,7 +88,7 @@ public:
      * @tparam T Result type.
      * @param op Operation code.
      * @param wr Writer function.
-     * @param rd Reader function.
+     * @param handler Response handler.
      * @return @c true on success and @c false otherwise.
      */
     template <typename T>
diff --git a/modules/platforms/cpp/client/src/response_handler.h b/modules/platforms/cpp/client/src/response_handler.h
index 168c41e59a..127f7998f6 100644
--- a/modules/platforms/cpp/client/src/response_handler.h
+++ b/modules/platforms/cpp/client/src/response_handler.h
@@ -90,8 +90,8 @@ public:
         if (!callback)
             return {};
 
-        auto res = ignite_result<T>::of_operation([&]() { return m_readFunc(reader); });
-        return ignite_result<void>::of_operation([&]() { callback(std::move(res)); });
+        auto res = result_of_operation<T>([&]() { return m_readFunc(reader); });
+        return result_of_operation<void>([&]() { callback(std::move(res)); });
     }
 
     /**
@@ -104,7 +104,7 @@ public:
         if (!callback)
             return {};
 
-        return ignite_result<void>::of_operation([&]() { callback({std::move(err)}); });
+        return result_of_operation<void>([&]() { callback({std::move(err)}); });
     }
 
 private:
diff --git a/modules/platforms/cpp/client/src/table/table_impl.h b/modules/platforms/cpp/client/src/table/table_impl.h
index 05225bdce2..9f130e25f9 100644
--- a/modules/platforms/cpp/client/src/table/table_impl.h
+++ b/modules/platforms/cpp/client/src/table/table_impl.h
@@ -21,7 +21,7 @@
 #include <memory>
 #include <utility>
 
-#include "common/guid.h"
+#include "common/uuid.h"
 
 namespace ignite::detail {
 
@@ -46,7 +46,7 @@ public:
      * @param name Name.
      * @param id ID.
      */
-    TableImpl(std::string name, Guid id)
+    TableImpl(std::string name, uuid id)
         : m_name(std::move(name))
         , m_id(id) { }
 
@@ -62,7 +62,7 @@ private:
     std::string m_name;
 
     /** Table ID. */
-    Guid m_id;
+    uuid m_id;
 };
 
 } // namespace ignite::detail
diff --git a/modules/platforms/cpp/client/src/table/tables.cpp b/modules/platforms/cpp/client/src/table/tables.cpp
index 40475f706e..4f5d6512dd 100644
--- a/modules/platforms/cpp/client/src/table/tables.cpp
+++ b/modules/platforms/cpp/client/src/table/tables.cpp
@@ -28,6 +28,10 @@ void Tables::getTableAsync(const std::string &name, ignite_callback<std::optiona
     getImpl().getTableAsync(name, std::move(callback));
 }
 
+void Tables::getTablesAsync(ignite_callback<std::vector<Table>> callback) {
+    getImpl().getTablesAsync(std::move(callback));
+}
+
 Tables::Tables(std::shared_ptr<void> impl)
     : m_impl(std::move(impl)) {
 }
diff --git a/modules/platforms/cpp/client/src/table/tables_impl.cpp b/modules/platforms/cpp/client/src/table/tables_impl.cpp
index af74fc3a14..a08eb024dd 100644
--- a/modules/platforms/cpp/client/src/table/tables_impl.cpp
+++ b/modules/platforms/cpp/client/src/table/tables_impl.cpp
@@ -15,8 +15,6 @@
  * limitations under the License.
  */
 
-#include "common/utils.h"
-
 #include "ignite/protocol/reader.h"
 #include "ignite/protocol/writer.h"
 #include "table/tables_impl.h"
@@ -28,8 +26,8 @@ void TablesImpl::getTableAsync(const std::string &name, ignite_callback<std::opt
         if (reader.tryReadNil())
             return std::nullopt;
 
-        auto guid = reader.readGuid();
-        auto tableImpl = std::make_shared<TableImpl>(name, guid);
+        auto id = reader.readUuid();
+        auto tableImpl = std::make_shared<TableImpl>(name, id);
 
         return std::make_optional(Table(tableImpl));
     };
@@ -41,4 +39,26 @@ void TablesImpl::getTableAsync(const std::string &name, ignite_callback<std::opt
         ClientOperation::TABLE_GET, [&name](protocol::Writer &writer) { writer.write(name); }, std::move(handler));
 }
 
+void TablesImpl::getTablesAsync(ignite_callback<std::vector<Table>> callback) {
+    auto readerFunc = [](protocol::Reader &reader) -> std::vector<Table> {
+        if (reader.tryReadNil())
+            return {};
+
+        std::vector<Table> tables;
+        tables.reserve(reader.readMapSize());
+
+        reader.readMap<uuid, std::string>([&tables] (auto&& id, auto&& name) {
+            auto tableImpl = std::make_shared<TableImpl>(std::forward<std::string>(name), std::forward<uuid>(id));
+            tables.push_back(Table{tableImpl});
+        });
+
+        return std::move(tables);
+    };
+
+    auto handler =
+        std::make_shared<ResponseHandlerImpl<std::vector<Table>>>(std::move(readerFunc), std::move(callback));
+
+    m_connection->performRequest(ClientOperation::TABLES_GET, std::move(handler));
+}
+
 } // namespace ignite::detail
diff --git a/modules/platforms/cpp/client/src/table/tables_impl.h b/modules/platforms/cpp/client/src/table/tables_impl.h
index 2b1f94da3b..b40eec4054 100644
--- a/modules/platforms/cpp/client/src/table/tables_impl.h
+++ b/modules/platforms/cpp/client/src/table/tables_impl.h
@@ -46,11 +46,19 @@ public:
      *
      * @param name Table name.
      * @param callback Callback.
-     * @return TableImpl with corresponding name.
-     * @throw ignite_error In case of error.
+     * @throw ignite_error In case of error while trying to send a request.
      */
     void getTableAsync(const std::string &name, ignite_callback<std::optional<Table>> callback);
 
+    /**
+     * Gets all tables.
+     *
+     * @param callback Callback to be called once operation is complete. On success, the callback is invoked with
+     *    a vector of all tables.
+     * @throw ignite_error In case of error while trying to send a request.
+     */
+    void getTablesAsync(ignite_callback<std::vector<Table>> callback);
+
 private:
     /** Cluster connection. */
     std::shared_ptr<ClusterConnection> m_connection;
diff --git a/modules/platforms/cpp/common/factory.h b/modules/platforms/cpp/common/factory.h
index dd58c592d6..1da364532f 100644
--- a/modules/platforms/cpp/common/factory.h
+++ b/modules/platforms/cpp/common/factory.h
@@ -42,4 +42,26 @@ public:
     virtual std::unique_ptr<T> build() = 0;
 };
 
+/**
+ * Basic factory class.
+ *
+ * @tparam TB Base type.
+ * @tparam TC Concrete type.
+ */
+template <typename TB, typename TC>
+class basic_factory : public factory<TB> {
+public:
+    /**
+     * Destructor.
+     */
+    virtual ~basic_factory() = default;
+
+    /**
+     * Build instance.
+     *
+     * @return New instance of type @c T.
+     */
+    [[nodiscard]] std::unique_ptr<TB> build() override { return std::make_unique<TC>(); }
+};
+
 } // namespace ignite
diff --git a/modules/platforms/cpp/common/guid.h b/modules/platforms/cpp/common/guid.h
deleted file mode 100644
index 37ed6b49bb..0000000000
--- a/modules/platforms/cpp/common/guid.h
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file
- * Declares ignite::Guid class.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <iomanip>
-
-namespace ignite {
-
-/**
- * Global universally unique identifier (GUID).
- */
-class Guid {
-public:
-    // Default
-    Guid() = default;
-
-    /**
-     * Constructor.
-     *
-     * @param most Most significant bits.
-     * @param least Least significant bits.
-     */
-    Guid(int64_t most, int64_t least)
-        : m_most(most)
-        , m_least(least) { }
-
-    /**
-     * Returns the most significant 64 bits of this instance.
-     *
-     * @return The most significant 64 bits of this instance.
-     */
-    [[nodiscard]] std::int64_t getMostSignificantBits() const { return m_most; }
-
-    /**
-     * Returns the least significant 64 bits of this instance.
-     *
-     * @return The least significant 64 bits of this instance.
-     */
-    [[nodiscard]] std::int64_t getLeastSignificantBits() const { return m_least; }
-
-    /**
-     * The version number associated with this instance.  The version
-     * number describes how this Guid was generated.
-     *
-     * The version number has the following meaning:
-     * 1    Time-based UUID;
-     * 2    DCE security UUID;
-     * 3    Name-based UUID;
-     * 4    Randomly generated UUID.
-     *
-     * @return The version number of this instance.
-     */
-    [[nodiscard]] std::int32_t getVersion() const { return static_cast<int32_t>((m_most >> 12) & 0x0F); }
-
-    /**
-     * The variant number associated with this instance. The variant
-     * number describes the layout of the Guid.
-     *
-     * The variant number has the following meaning:
-     * 0    Reserved for NCS backward compatibility;
-     * 2    IETF RFC 4122 (Leach-Salz), used by this class;
-     * 6    Reserved, Microsoft Corporation backward compatibility;
-     * 7    Reserved for future definition.
-     *
-     * @return The variant number of this instance.
-     */
-    [[nodiscard]] std::int32_t getVariant() const {
-        auto least = static_cast<uint64_t>(m_least);
-        return static_cast<std::int32_t>((least >> (64 - (least >> 62))) & (least >> 63));
-    }
-
-    /**
-     * Compare to another value.
-     *
-     * @param other Instance to compare to.
-     * @return Zero if equals, negative number if less and positive if more.
-     */
-    [[nodiscard]] std::int64_t compare(const Guid &other) const {
-        if (m_most < other.m_most)
-            return -1;
-
-        if (m_most > other.m_most)
-            return 1;
-
-        if (m_least < other.m_least)
-            return -1;
-
-        if (m_least > other.m_least)
-            return 1;
-
-        return 0;
-    }
-
-private:
-    /** Most significant bits. */
-    std::int64_t m_most{0};
-
-    /** Least significant bits. */
-    std::int64_t m_least{0};
-};
-
-/**
- * Comparison operator.
- *
- * @param val1 First value.
- * @param val2 Second value.
- * @return True if equal.
- */
-inline bool operator==(const Guid &val1, const Guid &val2) {
-    return val1.compare(val2) == 0;
-}
-
-/**
- * Comparison operator.
- *
- * @param val1 First value.
- * @param val2 Second value.
- * @return True if not equal.
- */
-inline bool operator!=(const Guid &val1, const Guid &val2) {
-    return val1.compare(val2) != 0;
-}
-
-/**
- * Comparison operator.
- *
- * @param val1 First value.
- * @param val2 Second value.
- * @return True if less.
- */
-inline bool operator<(const Guid &val1, const Guid &val2) {
-    return val1.compare(val2) < 0;
-}
-
-/**
- * Comparison operator.
- *
- * @param val1 First value.
- * @param val2 Second value.
- * @return True if less or equal.
- */
-inline bool operator<=(const Guid &val1, const Guid &val2) {
-    return val1.compare(val2) <= 0;
-}
-
-/**
- * Comparison operator.
- *
- * @param val1 First value.
- * @param val2 Second value.
- * @return True if greater.
- */
-inline bool operator>(const Guid &val1, const Guid &val2) {
-    return val1.compare(val2) > 0;
-}
-
-/**
- * Comparison operator.
- *
- * @param val1 First value.
- * @param val2 Second value.
- * @return True if greater or equal.
- */
-inline bool operator>=(const Guid &val1, const Guid &val2) {
-    return val1.compare(val2) >= 0;
-}
-
-/**
- * Output operator.
- *
- * @param os Output stream.
- * @param guid Guid to output.
- * @return Reference to the first param.
- */
-template <typename C>
-::std::basic_ostream<C> &operator<<(std::basic_ostream<C> &os, const Guid &guid) {
-    auto part1 = uint32_t(guid.getMostSignificantBits() >> 32);
-    auto part2 = uint16_t(guid.getMostSignificantBits() >> 16);
-    auto part3 = uint16_t(guid.getMostSignificantBits());
-    auto part4 = uint16_t(guid.getLeastSignificantBits() >> 48);
-    uint64_t part5 = guid.getLeastSignificantBits() & 0x0000FFFFFFFFFFFFU;
-
-    // clang-format off
-    os  << std::hex
-        << std::setfill<C>('0') << std::setw(8)  << part1 << '-'
-        << std::setfill<C>('0') << std::setw(4)  << part2 << '-'
-        << std::setfill<C>('0') << std::setw(4)  << part3 << '-'
-        << std::setfill<C>('0') << std::setw(4)  << part4 << '-'
-        << std::setfill<C>('0') << std::setw(12) << part5 << std::dec;
-    // clang-format on
-
-    return os;
-}
-
-/**
- * Input operator.
- *
- * @param is Input stream.
- * @param guid Guid to input.
- * @return Reference to the first param.
- */
-template <typename C>
-::std::basic_istream<C> &operator>>(std::basic_istream<C> &is, Guid &guid) {
-    uint64_t parts[5];
-
-    C delim;
-    for (int i = 0; i < 4; ++i) {
-        is >> std::hex >> parts[i] >> delim;
-        if (delim != static_cast<C>('-'))
-            return is;
-    }
-
-    is >> std::hex >> parts[4];
-    guid = Guid(int64_t((parts[0] << 32) | (parts[1] << 16) | parts[2]), int64_t((parts[3] << 48) | parts[4]));
-    return is;
-}
-
-} // namespace ignite
diff --git a/modules/platforms/cpp/common/ignite_result.h b/modules/platforms/cpp/common/ignite_result.h
index 2bbaf3af43..67b3957759 100644
--- a/modules/platforms/cpp/common/ignite_result.h
+++ b/modules/platforms/cpp/common/ignite_result.h
@@ -17,14 +17,15 @@
 
 #pragma once
 
+#include "common/ignite_error.h"
+
 #include <cstdint>
 #include <functional>
 #include <future>
 #include <optional>
+#include <variant>
 #include <string>
 
-#include "common/ignite_error.h"
-
 namespace ignite {
 
 /**
@@ -42,8 +43,7 @@ public:
      * @param value Value.
      */
     ignite_result(T &&value) // NOLINT(google-explicit-constructor)
-        : m_value(std::move(value))
-        , m_error(std::nullopt) { }
+        : m_value(std::move(value)) { }
 
     /**
      * Constructor.
@@ -51,22 +51,21 @@ public:
      * @param message Message.
      */
     ignite_result(ignite_error &&error) // NOLINT(google-explicit-constructor)
-        : m_value(std::nullopt)
-        , m_error(std::move(error)) { }
+        : m_value(std::move(error)) { }
 
     /**
      * Has value.
      *
      * @return @c true if the result has value.
      */
-    [[nodiscard]] bool has_value() const noexcept { return m_value.has_value(); }
+    [[nodiscard]] bool has_value() const noexcept { return !has_error(); }
 
     /**
      * Has error.
      *
      * @return @c true if the result has error.
      */
-    [[nodiscard]] bool has_error() const noexcept { return m_error.has_value(); }
+    [[nodiscard]] bool has_error() const noexcept { return std::holds_alternative<ignite_error>(m_value); }
 
     /**
      * Get value.
@@ -77,7 +76,7 @@ public:
         if (!has_value())
             throw ignite_error("No value is present in result");
 
-        return std::move(m_value.value());
+        return std::get<T>(std::move(m_value));
     }
 
     /**
@@ -89,7 +88,7 @@ public:
         if (!has_value())
             throw ignite_error("No value is present in result");
 
-        return m_value.value();
+        return std::get<T>(m_value);
     }
 
     /**
@@ -101,7 +100,7 @@ public:
         if (!has_value())
             throw ignite_error("No value is present in result");
 
-        return m_value.value();
+        return std::get<T>(m_value);
     }
 
     /**
@@ -113,7 +112,7 @@ public:
         if (!has_error())
             throw ignite_error("No error is present in result");
 
-        return std::move(m_error.value());
+        return std::move(std::get<ignite_error>(m_value));
     }
 
     /**
@@ -125,7 +124,7 @@ public:
         if (!has_error())
             throw ignite_error("No error is present in result");
 
-        return m_error.value();
+        return std::get<ignite_error>(m_value);
     }
 
     /**
@@ -137,68 +136,20 @@ public:
         if (!has_error())
             throw ignite_error("No error is present in result");
 
-        return m_error.value();
+        return std::get<ignite_error>(m_value);
     }
 
     /**
-     * Bool operator. Can be used to check result for an error.
+     * Bool operator.
+     * Can be used to check result for an error.
      *
      * @return @c true if result does not contain error.
      */
     explicit operator bool() const noexcept { return !has_error(); }
 
-    /**
-     * Wrap operation result in ignite_result.
-     * @param operation Operation to wrap.
-     * @return ignite_result
-     */
-    static ignite_result of_operation(const std::function<T()> &operation) noexcept {
-        // TODO: IGNITE-17760 Move to common once it's re-factored
-        try {
-            return {operation()};
-        } catch (const ignite_error &err) {
-            return {ignite_error(err)};
-        } catch (const std::exception &err) {
-            std::string msg("Standard library exception is thrown: ");
-            msg += err.what();
-            return {ignite_error(status_code::GENERIC, msg, std::current_exception())};
-        } catch (...) {
-            return {ignite_error(status_code::UNKNOWN, "Unknown error is encountered when processing network event",
-                std::current_exception())};
-        }
-    }
-
-    /**
-     * Return promise setter.
-     *
-     * @param pr Promise.
-     * @return Promise setter.
-     */
-    static std::function<void(ignite_result<T>)> promise_setter(std::shared_ptr<std::promise<T>> pr) {
-        // TODO: IGNITE-17760 Move to common once it's re-factored
-        return [pr = std::move(pr)](
-                   ignite_result<T> &&res) mutable { ignite_result<T>::set_promise(*pr, std::move(res)); };
-    }
-
 private:
-    /**
-     * Set promise from result.
-     *
-     * @param pr Promise to set.
-     */
-    static void set_promise(std::promise<T> &pr, ignite_result res) {
-        if (res.has_error()) {
-            pr.set_exception(std::make_exception_ptr(std::move(res.m_error.value())));
-        } else {
-            pr.set_value(std::move(res.m_value.value()));
-        }
-    }
-
     /** Value. */
-    std::optional<T> m_value;
-
-    /** Error. */
-    std::optional<ignite_error> m_error;
+    std::variant<ignite_error, T> m_value;
 };
 
 /**
@@ -218,9 +169,8 @@ public:
      *
      * @param message Message.
      */
-    ignite_result(ignite_error &&error)
-        : // NOLINT(google-explicit-constructor)
-        m_error(std::move(error)) { }
+    ignite_result(ignite_error &&error) // NOLINT(google-explicit-constructor)
+        : m_error(std::move(error)) { }
 
     /**
      * Has error.
@@ -254,65 +204,81 @@ public:
     }
 
     /**
-     * Bool operator. Can be used to check result for an error.
+     * Bool operator.
+     * Can be used to check result for an error.
      *
      * @return @c true if result does not contain error.
      */
     explicit operator bool() const noexcept { return !has_error(); }
 
-    /**
-     * Wrap operation result in ignite_result.
-     * @param operation Operation to wrap.
-     * @return ignite_result
-     */
-    static ignite_result of_operation(const std::function<void()> &operation) noexcept {
-        // TODO: IGNITE-17760 Move to common once it's re-factored
-        try {
+
+private:
+    /** Error. */
+    std::optional<ignite_error> m_error;
+};
+
+template <typename T>
+using ignite_callback = std::function<void(ignite_result<T> &&)>;
+
+/**
+ * Wrap operation result in ignite_result.
+ *
+ * @param operation Operation to wrap.
+ * @return ignite_result
+ */
+template <typename T>
+ignite_result<T> result_of_operation(const std::function<T()> &operation) noexcept {
+    // TODO: IGNITE-17760 Move to common once it's re-factored
+    try {
+        if constexpr (std::is_same<decltype(operation()), void>::value) {
             operation();
             return {};
-        } catch (const ignite_error &err) {
-            return {ignite_error(err)};
-        } catch (const std::exception &err) {
-            std::string msg("Standard library exception is thrown: ");
-            msg += err.what();
-            return {ignite_error(status_code::GENERIC, msg, std::current_exception())};
-        } catch (...) {
-            return {ignite_error(status_code::UNKNOWN, "Unknown error is encountered when processing network event",
-                std::current_exception())};
+        } else {
+            return {operation()};
         }
+    } catch (const ignite_error &err) {
+        return {ignite_error(err)};
+    } catch (const std::exception &err) {
+        std::string msg("Standard library exception is thrown: ");
+        msg += err.what();
+        return {ignite_error(status_code::GENERIC, msg, std::current_exception())};
+    } catch (...) {
+        return {ignite_error(status_code::UNKNOWN, "Unknown error is encountered when processing network event",
+                             std::current_exception())};
     }
+}
 
-    /**
-     * Return promise setter.
-     *
-     * @param pr Promise.
-     * @return Promise setter.
-     */
-    static std::function<void(ignite_result<void>)> promise_setter(std::shared_ptr<std::promise<void>> pr) {
-        // TODO: IGNITE-17760 Move to common once it's re-factored
-        return [pr = std::move(pr)](
-                   ignite_result<void> &&res) mutable { ignite_result<void>::set_promise(*pr, std::move(res)); };
-    }
-
-private:
-    /**
-     * Set promise from result.
-     *
-     * @param pr Promise to set.
-     */
-    static void set_promise(std::promise<void> &pr, ignite_result res) {
-        if (res.has_error()) {
-            pr.set_exception(std::make_exception_ptr(std::move(res.m_error.value())));
-        } else {
+/**
+ * Set promise from result.
+ *
+ * @param pr Promise to set.
+ * @param res Result to use.
+ */
+template <typename T>
+void result_set_promise(std::promise<T> &pr, ignite_result<T> &&res) {
+    // TODO: IGNITE-17760 Move to common once it's re-factored
+    if (!res) {
+        pr.set_exception(std::make_exception_ptr(std::move(res).error()));
+    } else {
+        if constexpr (std::is_same<T, void>::value) {
             pr.set_value();
+        } else {
+            pr.set_value(std::move(res).value());
         }
     }
+}
 
-    /** Error. */
-    std::optional<ignite_error> m_error;
-};
-
+/**
+ * Get promise setter for a promise to be used with ignite result.
+ *
+ * @param pr Promise.
+ * @return Promise setter.
+ */
 template <typename T>
-using ignite_callback = std::function<void(ignite_result<T> &&)>;
+std::function<void(ignite_result<T>)> result_promise_setter(std::shared_ptr<std::promise<T>> pr) {
+    // TODO: IGNITE-17760 Move to common once it's re-factored
+    return [pr = std::move(pr)](
+            ignite_result<T> &&res) mutable { result_set_promise<T>(*pr, std::move(res)); };
+}
 
 } // namespace ignite
diff --git a/modules/platforms/cpp/common/uuid.h b/modules/platforms/cpp/common/uuid.h
index f78d4fddbf..848b313e06 100644
--- a/modules/platforms/cpp/common/uuid.h
+++ b/modules/platforms/cpp/common/uuid.h
@@ -51,14 +51,14 @@ public:
      *
      * @return The most significant 64 bits of this instance.
      */
-    constexpr std::int64_t getMostSignificantBits() const noexcept { return most; }
+    [[nodiscard]] constexpr std::int64_t getMostSignificantBits() const noexcept { return most; }
 
     /**
      * Returns the least significant 64 bits of this instance.
      *
      * @return The least significant 64 bits of this instance.
      */
-    constexpr std::int64_t getLeastSignificantBits() const noexcept { return least; }
+    [[nodiscard]] constexpr std::int64_t getLeastSignificantBits() const noexcept { return least; }
 
     /**
      * The version number associated with this instance.  The version
@@ -72,7 +72,7 @@ public:
      *
      * @return The version number of this instance.
      */
-    constexpr std::int32_t version() const noexcept { return static_cast<std::int32_t>((most >> 12) & 0x0f); }
+    [[nodiscard]] constexpr std::int32_t version() const noexcept { return static_cast<std::int32_t>((most >> 12) & 0x0f); }
 
     /**
      * The variant number associated with this instance. The variant
@@ -86,8 +86,8 @@ public:
      *
      * @return The variant number of this instance.
      */
-    constexpr std::int32_t variant() const noexcept {
-        std::uint64_t least0 = static_cast<uint64_t>(least);
+    [[nodiscard]] constexpr std::int32_t variant() const noexcept {
+        auto least0 = static_cast<uint64_t>(least);
         return static_cast<std::int32_t>((least0 >> (64 - (least0 >> 62))) & (least >> 63));
     }
 
@@ -97,7 +97,7 @@ public:
      * @param other Instance to compare to.
      * @return Zero if equals, negative number if less, and positive if greater.
      */
-    constexpr int compare(const uuid &other) const noexcept {
+    [[nodiscard]] constexpr int compare(const uuid &other) const noexcept {
         if (most != other.most) {
             return most < other.most ? -1 : 1;
         }
@@ -190,11 +190,14 @@ constexpr bool operator>=(const uuid &lhs, const uuid &rhs) noexcept {
  */
 template <typename C, typename T>
 ::std::basic_ostream<C, T> &operator<<(std::basic_ostream<C, T> &os, const uuid &uuid) {
-    uint32_t part1 = static_cast<uint32_t>(uuid.getMostSignificantBits() >> 32);
-    uint16_t part2 = static_cast<uint16_t>(uuid.getMostSignificantBits() >> 16);
-    uint16_t part3 = static_cast<uint16_t>(uuid.getMostSignificantBits());
-    uint16_t part4 = static_cast<uint16_t>(uuid.getLeastSignificantBits() >> 48);
-    uint64_t part5 = uuid.getLeastSignificantBits() & 0x0000FFFFFFFFFFFFU;
+    auto msb = uuid.getMostSignificantBits();
+    auto lsb = uuid.getLeastSignificantBits();
+
+    auto part1 = static_cast<std::uint32_t>(msb >> 32);
+    auto part2 = static_cast<std::uint16_t>(msb >> 16);
+    auto part3 = static_cast<std::uint16_t>(msb);
+    auto part4 = static_cast<std::uint16_t>(lsb >> 48);
+    uint64_t part5 = lsb & 0x0000FFFFFFFFFFFFU;
 
     std::ios_base::fmtflags savedFlags = os.flags();
 
@@ -240,7 +243,8 @@ template <typename C, typename T>
 
     is.flags(savedFlags);
 
-    result = uuid((parts[0] << 32) | (parts[1] << 16) | parts[2], (parts[3] << 48) | parts[4]);
+    result = uuid(std::int64_t((parts[0] << 32) | (parts[1] << 16) | parts[2]),
+                  std::int64_t((parts[3] << 48) | parts[4]));
 
     return is;
 }
diff --git a/modules/platforms/cpp/network/include/ignite/network/length_prefix_codec.h b/modules/platforms/cpp/network/include/ignite/network/length_prefix_codec.h
index 07e240604c..2a9b662185 100644
--- a/modules/platforms/cpp/network/include/ignite/network/length_prefix_codec.h
+++ b/modules/platforms/cpp/network/include/ignite/network/length_prefix_codec.h
@@ -86,20 +86,7 @@ private:
     bool m_magicReceived;
 };
 
-/**
- * Factory for LengthPrefixCodec.
- */
-class LengthPrefixCodecFactory : public factory<Codec> {
-public:
-    // Default
-    LengthPrefixCodecFactory() = default;
-
-    /**
-     * Build instance.
-     *
-     * @return New instance of type @c T.
-     */
-    std::unique_ptr<Codec> build() override { return std::make_unique<LengthPrefixCodec>(); }
-};
+/** Factory for LengthPrefixCodec. */
+typedef basic_factory<Codec, LengthPrefixCodec> LengthPrefixCodecFactory;
 
 } // namespace ignite::network
diff --git a/modules/platforms/cpp/protocol/include/ignite/protocol/extension_types.h b/modules/platforms/cpp/protocol/include/ignite/protocol/extension_types.h
index 48102193f6..d6b564b5fb 100644
--- a/modules/platforms/cpp/protocol/include/ignite/protocol/extension_types.h
+++ b/modules/platforms/cpp/protocol/include/ignite/protocol/extension_types.h
@@ -29,7 +29,7 @@ enum class ExtensionTypes : std::int8_t {
 
     DECIMAL = 2,
 
-    GUID = 3,
+    UUID = 3,
 
     DATE = 4,
 
diff --git a/modules/platforms/cpp/protocol/include/ignite/protocol/reader.h b/modules/platforms/cpp/protocol/include/ignite/protocol/reader.h
index 0686d78b48..135a51bedf 100644
--- a/modules/platforms/cpp/protocol/include/ignite/protocol/reader.h
+++ b/modules/platforms/cpp/protocol/include/ignite/protocol/reader.h
@@ -22,9 +22,11 @@
 
 #include <msgpack.h>
 
-#include "common/guid.h"
+#include "common/uuid.h"
 #include "common/types.h"
 
+#include "ignite/protocol/utils.h"
+
 namespace ignite::protocol {
 
 /**
@@ -87,11 +89,42 @@ public:
     [[nodiscard]] std::optional<std::string> readStringNullable();
 
     /**
-     * Read GUID.
+     * Read UUID.
+     *
+     * @return UUID value.
+     */
+    [[nodiscard]] uuid readUuid();
+
+    /**
+     * Read Map size.
+     *
+     * @return Map size.
+     */
+    [[nodiscard]] uint32_t readMapSize() const {
+        checkDataInStream();
+
+        if (m_currentVal.data.type != MSGPACK_OBJECT_MAP)
+            throw ignite_error("The value in stream is not a Map");
+
+        return m_currentVal.data.via.map.size;
+    }
+
+    /**
+     * Read Map.
      *
-     * @return GUID value.
+     * @tparam K Key type.
+     * @tparam V Value type.
+     * @param handler Pair handler.
      */
-    [[nodiscard]] Guid readGuid();
+     template<typename K, typename V>
+     void readMap(const std::function<void(K&&, V&&)>& handler) {
+        auto size = readMapSize();
+        for (std::uint32_t i = 0; i < size; ++i) {
+            auto key = unpack_object<K>(m_currentVal.data.via.map.ptr[i].key);
+            auto val = unpack_object<V>(m_currentVal.data.via.map.ptr[i].val);
+            handler(std::move(key), std::move(val));
+        }
+    }
 
     /**
      * If the next value is Nil, read it and move reader to the next position.
@@ -114,7 +147,10 @@ private:
     /**
      * Check whether there is a data in stream and throw ignite_error if there is none.
      */
-    void checkDataInStream();
+    void checkDataInStream() const {
+        if (m_moveRes < 0)
+            throw ignite_error("No more data in stream");
+    }
 
     /** Buffer. */
     bytes_view m_buffer;
diff --git a/modules/platforms/cpp/protocol/include/ignite/protocol/utils.h b/modules/platforms/cpp/protocol/include/ignite/protocol/utils.h
index 9cc0d9934e..4d97d9df06 100644
--- a/modules/platforms/cpp/protocol/include/ignite/protocol/utils.h
+++ b/modules/platforms/cpp/protocol/include/ignite/protocol/utils.h
@@ -24,11 +24,18 @@
 #include <optional>
 
 #include "common/ignite_error.h"
+#include "common/bytes.h"
 #include "common/types.h"
+#include "common/uuid.h"
 
-#include "ignite/protocol/reader.h"
+#include "ignite/protocol/extension_types.h"
+
+struct msgpack_object;
 
 namespace ignite::protocol {
+
+class Reader;
+
 /** Magic bytes. */
 static constexpr std::array<std::byte, 4> MAGIC_BYTES = {
     std::byte('I'), std::byte('G'), std::byte('N'), std::byte('I')};
@@ -187,12 +194,45 @@ inline void writeInt16(int16_t value, std::byte *buffer, size_t offset = 0) {
     return writeUint16(std::uint16_t(value), buffer, offset);
 }
 
+template<typename T>
+T unpack_object(const msgpack_object&);
+
+/**
+ * Unpack number.
+ *
+ * @param object MsgPack object.
+ * @return Number.
+ * @throw ignite_error if the object is not a number.
+ */
+template<>
+int64_t unpack_object(const msgpack_object& object);
+
+/**
+ * Unpack string.
+ *
+ * @param object MsgPack object.
+ * @return String.
+ * @throw ignite_error if the object is not a string.
+ */
+template<>
+std::string unpack_object(const msgpack_object& object);
+
+/**
+ * Unpack UUID.
+ *
+ * @param object MsgPack object.
+ * @return UUID.
+ * @throw ignite_error if the object is not a UUID.
+ */
+template<>
+uuid unpack_object(const msgpack_object& object);
+
 /**
- * Make random GUID.
+ * Make random UUID.
  *
- * @return Random GUID instance.
+ * @return Random UUID instance.
  */
-ignite::Guid makeRandomGuid();
+ignite::uuid makeRandomUuid();
 
 /**
  * Read error.
diff --git a/modules/platforms/cpp/protocol/src/reader.cpp b/modules/platforms/cpp/protocol/src/reader.cpp
index ef552f4200..42431fadfa 100644
--- a/modules/platforms/cpp/protocol/src/reader.cpp
+++ b/modules/platforms/cpp/protocol/src/reader.cpp
@@ -16,8 +16,8 @@
  */
 
 #include "common/ignite_error.h"
+#include "common/bytes.h"
 
-#include "ignite/protocol/extension_types.h"
 #include "ignite/protocol/reader.h"
 #include "ignite/protocol/utils.h"
 
@@ -42,12 +42,7 @@ Reader::Reader(bytes_view buffer)
 std::int64_t Reader::readInt64() {
     checkDataInStream();
 
-    msgpack_object object = m_currentVal.data;
-    if (object.type != MSGPACK_OBJECT_NEGATIVE_INTEGER && object.type != MSGPACK_OBJECT_POSITIVE_INTEGER)
-        throw ignite_error("The value in stream is not an integer number");
-
-    auto res = object.via.i64;
-
+    auto res = unpack_object<int64_t>(m_currentVal.data);
     next();
 
     return res;
@@ -56,12 +51,7 @@ std::int64_t Reader::readInt64() {
 std::string Reader::readString() {
     checkDataInStream();
 
-    msgpack_object object = m_currentVal.data;
-    if (object.type != MSGPACK_OBJECT_STR)
-        throw ignite_error("The value in stream is not a string");
-
-    std::string res{object.via.str.ptr, object.via.str.size};
-
+    std::string res = unpack_object<std::string>(m_currentVal.data);
     next();
 
     return res;
@@ -74,24 +64,12 @@ std::optional<std::string> Reader::readStringNullable() {
     return readString();
 }
 
-Guid Reader::readGuid() {
+uuid Reader::readUuid() {
     checkDataInStream();
 
-    if (m_currentVal.data.type != MSGPACK_OBJECT_EXT
-        && m_currentVal.data.via.ext.type != std::int8_t(ExtensionTypes::GUID))
-        throw ignite_error("The value in stream is not a GUID");
-
-    if (m_currentVal.data.via.ext.size != 16)
-        throw ignite_error("Unexpected value size");
-
-    auto data = reinterpret_cast<const std::byte *>(m_currentVal.data.via.ext.ptr);
-
-    int64_t most = protocol::readInt64(data);
-    int64_t least = protocol::readInt64(data, 8);
-
-    Guid res(most, least);
-
+    uuid res = unpack_object<uuid>(m_currentVal.data);
     next();
+
     return res;
 }
 
@@ -109,9 +87,4 @@ void Reader::next() {
     m_moveRes = msgpack_unpacker_next(&m_unpacker, &m_currentVal);
 }
 
-void Reader::checkDataInStream() {
-    if (m_moveRes < 0)
-        throw ignite_error("No more data in stream");
-}
-
 } // namespace ignite::protocol
diff --git a/modules/platforms/cpp/protocol/src/utils.cpp b/modules/platforms/cpp/protocol/src/utils.cpp
index 85e02df6e2..7d9eccadfe 100644
--- a/modules/platforms/cpp/protocol/src/utils.cpp
+++ b/modules/platforms/cpp/protocol/src/utils.cpp
@@ -19,11 +19,46 @@
 #include <random>
 #include <sstream>
 
+#include <msgpack.h>
+
+#include "ignite/protocol/reader.h"
 #include "ignite/protocol/utils.h"
 
 namespace ignite::protocol {
 
-Guid makeRandomGuid() {
+template<>
+int64_t unpack_object(const msgpack_object &object) {
+    if (object.type != MSGPACK_OBJECT_NEGATIVE_INTEGER && object.type != MSGPACK_OBJECT_POSITIVE_INTEGER)
+        throw ignite_error("The value in stream is not an integer number");
+
+    return object.via.i64;
+}
+
+template<>
+uuid unpack_object(const msgpack_object &object) {
+    if (object.type != MSGPACK_OBJECT_EXT && object.via.ext.type != std::int8_t(ExtensionTypes::UUID))
+        throw ignite_error("The value in stream is not a UUID");
+
+    if (object.via.ext.size != 16)
+        throw ignite_error("Unexpected UUID size: " + std::to_string(object.via.ext.size));
+
+    auto data = reinterpret_cast<const std::byte *>(object.via.ext.ptr);
+
+    auto msb = bytes::load<Endian::LITTLE, int64_t>(data);
+    auto lsb = bytes::load<Endian::LITTLE, int64_t>(data + 8);
+
+    return {msb, lsb};
+}
+
+template<>
+std::string unpack_object(const msgpack_object &object) {
+    if (object.type != MSGPACK_OBJECT_STR)
+        throw ignite_error("The value in stream is not a string");
+
+    return {object.via.str.ptr, object.via.str.size};
+}
+
+uuid makeRandomUuid() {
     static std::mutex randomMutex;
     static std::random_device rd;
     static std::mt19937 gen(rd());
@@ -39,7 +74,7 @@ std::optional<ignite_error> readError(Reader &reader) {
     if (reader.tryReadNil())
         return std::nullopt;
 
-    Guid traceId = reader.tryReadNil() ? makeRandomGuid() : reader.readGuid();
+    uuid traceId = reader.tryReadNil() ? makeRandomUuid() : reader.readUuid();
     int32_t code = reader.tryReadNil() ? 65537 : reader.readInt32();
     std::string className = reader.readString();
     std::string message = reader.readString();