From fd239f21c8f07e8271c1555bbd04941232370f5e Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Tue, 4 Jun 2024 20:58:27 +0100 Subject: [PATCH 01/16] (bench) spot intersection with integers --- bench/algorithms_test.cpp | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/bench/algorithms_test.cpp b/bench/algorithms_test.cpp index 94385c51..83de941c 100644 --- a/bench/algorithms_test.cpp +++ b/bench/algorithms_test.cpp @@ -111,3 +111,46 @@ BENCHMARK([](benchmark::State &st) { }) ->Name("bm_spot_intersection") ->Range(8, 8 << 10); + +BENCHMARK([](benchmark::State &st) { + std::vector left; + std::vector right; + + left.reserve(st.range(0)); + right.reserve(st.range(0)); + + for (int n = st.range(0); n > 0; n--) { + left.emplace_back(std::rand()); + right.emplace_back(std::rand()); + } + + std::size_t ops = 0; + std::size_t cost = 0; + for (auto _ : st) { + ops++; + + auto i = left.cbegin(); + auto j = right.cbegin(); + + while (i != left.cend() && j != right.cend()) { + cost++; + + if (*i == *j) { + break; + } + + if (*i < *j) { + i++; + } else { + j++; + } + } + } + + st.counters.insert({ + {"ops", benchmark::Counter(ops, benchmark::Counter::kIsRate)}, + {"comparisons", benchmark::Counter(cost, benchmark::Counter::kIsRate)}, + }); +}) + ->Name("bm_spot_intersection_int") + ->Range(8, 8 << 10); From d29924cc7200a56f1160aefd33f5501b14cf81e7 Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Tue, 4 Jun 2024 21:52:50 +0100 Subject: [PATCH 02/16] (db) tuple entity hashes --- db/schema.sql | 8 ++++++++ src/db/tuples.cpp | 45 ++++++++++++++++++++++++++++++++++++++---- src/db/tuples.h | 16 ++++++++++----- src/db/tuples_test.cpp | 23 ++++++++++++++++++--- 4 files changed, 80 insertions(+), 12 deletions(-) diff --git a/db/schema.sql b/db/schema.sql index 67d26eb9..f28333a5 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -38,6 +38,11 @@ create table if not exists tuples ( _id text not null, _rev integer not null, + -- Hash values of entities + -- + _hash_l bigint, + _hash_r bigint, + -- Self references for computed tuples -- _rid_l text, @@ -71,3 +76,6 @@ create table if not exists tuples ( constraint "tuples.check-r_entity_id" check (r_entity_id <> ''), constraint "tuples.check-attrs" check (jsonb_typeof(attrs) = 'object') ); + +create index "tuples.idx-ltr" on tuples using btree (space_id, _hash_l, relation, _hash_r, _id); +create index "tuples.idx-rtl" on tuples using btree (space_id, _hash_r, relation, strand, _hash_l, _id); diff --git a/src/db/tuples.cpp b/src/db/tuples.cpp index 797f49e5..ee33d6a3 100644 --- a/src/db/tuples.cpp +++ b/src/db/tuples.cpp @@ -8,12 +8,13 @@ #include "common.h" namespace db { -Tuple::Tuple(const Tuple::Data &data) noexcept : _data(data), _id(), _rev(0), _ridL(), _ridR() { +Tuple::Tuple(const Tuple::Data &data) noexcept : + _data(data), _hashL(), _hashR(), _id(), _rev(0), _ridL(), _ridR() { sanitise(); } Tuple::Tuple(Tuple::Data &&data) noexcept : - _data(std::move(data)), _id(), _rev(0), _ridL(), _ridR() { + _data(std::move(data)), _hashL(), _hashR(), _id(), _rev(0), _ridL(), _ridR() { sanitise(); } @@ -30,6 +31,7 @@ Tuple::Tuple(const pg::row_t &r) : .spaceId = r["space_id"].as(), .strand = r["strand"].as(), }), + _hashL(r["_hash_l"].as()), _hashR(r["_hash_r"].as()), _id(r["_id"].as()), _rev(r["_rev"].as()), _ridL(r["_rid_l"].as()), _ridR(r["_rid_r"].as()) {} @@ -44,7 +46,8 @@ Tuple::Tuple(const Tuple &left, const Tuple &right) noexcept : .rPrincipalId = right.rPrincipalId(), .spaceId = left.spaceId(), }), - _id(), _rev(0), _ridL(left.id()), _ridR(right.id()) {} + _hashL(left.hashL()), _hashR(right.hashR()), _id(), _rev(0), _ridL(left.id()), + _ridR(right.id()) {} bool Tuple::discard(std::string_view id) { std::string_view qry = R"( @@ -57,6 +60,11 @@ bool Tuple::discard(std::string_view id) { return (res.affected_rows() == 1); } +void Tuple::hash() noexcept { + _hashL = Entity(_data.lEntityType, _data.lEntityId).hash(); + _hashR = Entity(_data.rEntityType, _data.rEntityId).hash(); +} + std::optional Tuple::lookup( std::string_view spaceId, Entity left, Entity right, std::string_view relation, std::string_view strand) { @@ -80,6 +88,7 @@ Tuple Tuple::retrieve(std::string_view id) { attrs, l_principal_id, r_principal_id, _id, _rev, + _hash_l, _hash_r, _rid_l, _rid_r from tuples where _id = $1::text; @@ -103,6 +112,8 @@ void Tuple::sanitise() noexcept { _data.rEntityId = *_data.rPrincipalId; _data.rEntityType = common::principal_entity_v; } + + hash(); } void Tuple::store() { @@ -120,6 +131,7 @@ void Tuple::store() { attrs, l_principal_id, r_principal_id, _id, _rev, + _hash_l, _hash_r, _rid_l, _rid_r ) values ( $1::text, @@ -130,7 +142,8 @@ void Tuple::store() { $8::jsonb, $9::text, $10::text, $11::text, $12::integer, - $13::text, $14::text + $13::bigint, $14::bigint, + $15::text, $16::text ) on conflict (_id) do update @@ -161,6 +174,8 @@ void Tuple::store() { _data.rPrincipalId, _id, _rev, + _hashL, + _hashR, _ridL, _ridR); } catch (pqxx::check_violation &) { @@ -183,6 +198,26 @@ Tuple::Entity::Entity(std::string_view pid) noexcept : Tuple::Entity::Entity(std::string_view type, std::string_view id) noexcept : _id(id), _type(type) {} +std::int64_t Tuple::Entity::hash() const noexcept { + // Jon Maiga's bit mixer from mx3 + // Ref: https://github.com/jonmaiga/mx3/blob/48924ee743d724aea2cafd2b4249ef8df57fa8b9/mx3.h#L17 + auto mix = [](std::int64_t x) -> std::int64_t { + constexpr std::int64_t m = 0xbea225f9eb34556d; + + x ^= x >> 32; + x *= m; + x ^= x >> 29; + x *= m; + x ^= x >> 32; + x *= m; + x ^= x >> 29; + return x; + }; + + std::int64_t seed = std::hash()(type()); + return mix(seed + 0x517cc1b727220a95 + std::hash()(id())); +} + Tuples ListTuples( std::string_view spaceId, std::optional left, std::optional right, std::optional relation, std::string_view lastId, std::uint16_t count) { @@ -229,6 +264,7 @@ Tuples ListTuples( attrs, l_principal_id, r_principal_id, _id, _rev, + _hash_l, _hash_r, _rid_l, _rid_r from tuples {} @@ -303,6 +339,7 @@ Tuples LookupTuples( attrs, l_principal_id, r_principal_id, _id, _rev, + _hash_l, _hash_r, _rid_l, _rid_r from tuples {} diff --git a/src/db/tuples.h b/src/db/tuples.h index ad9327d3..c5ed206f 100644 --- a/src/db/tuples.h +++ b/src/db/tuples.h @@ -42,6 +42,8 @@ class Tuple { std::string_view id() const noexcept { return _id; } std::string_view type() const noexcept { return _type; } + std::int64_t hash() const noexcept; + private: std::string_view _id; std::string_view _type; @@ -85,6 +87,8 @@ class Tuple { void strand(const std::string &strand) noexcept { _data.strand = strand; } const std::string &id() const noexcept { return _id; } + const std::int64_t hashL() const noexcept { return _hashL; } + const std::int64_t hashR() const noexcept { return _hashR; } const int &rev() const noexcept { return _rev; } const rid_t &ridL() const noexcept { return _ridL; } const rid_t &ridR() const noexcept { return _ridR; } @@ -100,13 +104,15 @@ class Tuple { static Tuple retrieve(std::string_view id); private: + void hash() noexcept; + void sanitise() noexcept; - Data _data; - std::string _id; - int _rev; - rid_t _ridL; - rid_t _ridR; + Data _data; + std::int64_t _hashL, _hashR; + std::string _id; + int _rev; + rid_t _ridL, _ridR; }; using Tuples = std::vector; diff --git a/src/db/tuples_test.cpp b/src/db/tuples_test.cpp index 400f013f..1a1a1228 100644 --- a/src/db/tuples_test.cpp +++ b/src/db/tuples_test.cpp @@ -57,6 +57,8 @@ TEST_F(db_TuplesTest, constructor) { EXPECT_EQ(right.rEntityType(), joined.rEntityType()); EXPECT_EQ(left.spaceId(), joined.spaceId()); EXPECT_TRUE(joined.strand().empty()); + EXPECT_EQ(left.hashL(), joined.hashL()); + EXPECT_EQ(right.hashR(), joined.hashR()); EXPECT_EQ(left.id(), joined.ridL()); EXPECT_EQ(right.id(), joined.ridR()); @@ -434,7 +436,8 @@ TEST_F(db_TuplesTest, retrieve) { relation, r_entity_type, r_entity_id, attrs, - _id, _rev + _id, _rev, + _hash_l, _hash_r ) values ( $1::text, $2::text, @@ -442,7 +445,8 @@ TEST_F(db_TuplesTest, retrieve) { $5::text, $6::text, $7::text, $8::jsonb, - $9::text, $10::integer + $9::text, $10::integer, + $11::bigint, $12::bigint ); )"; @@ -457,12 +461,16 @@ TEST_F(db_TuplesTest, retrieve) { "right", R"({"foo": "bar"})", "_id:db_TuplesTest.retrieve", - 1729)); + 1729, + -3631866150419398620, + 7468059380061813551)); auto tuple = db::Tuple::retrieve("_id:db_TuplesTest.retrieve"); EXPECT_FALSE(tuple.ridL()); EXPECT_FALSE(tuple.ridR()); EXPECT_EQ(1729, tuple.rev()); + EXPECT_EQ(-3631866150419398620, tuple.hashL()); + EXPECT_EQ(7468059380061813551, tuple.hashR()); EXPECT_EQ("db_TuplesTest.retrieve", tuple.lEntityType()); EXPECT_EQ("left", tuple.lEntityId()); @@ -542,6 +550,8 @@ TEST_F(db_TuplesTest, sanitise) { EXPECT_EQ(tuple.lPrincipalId(), tuple.lEntityId()); EXPECT_EQ(db::common::principal_entity_v, tuple.rEntityType()); EXPECT_EQ(tuple.rPrincipalId(), tuple.rEntityId()); + EXPECT_EQ(4978332106395442344, tuple.hashL()); + EXPECT_EQ(459406847117771879, tuple.hashR()); } } @@ -567,6 +577,7 @@ TEST_F(db_TuplesTest, store) { attrs, l_principal_id, r_principal_id, _id, _rev, + _hash_l, _hash_r, _rid_l, _rid_r from tuples where _id = $1::text; @@ -588,6 +599,8 @@ TEST_F(db_TuplesTest, store) { rPrincipalId, _id, _rev, + _hashL, + _hashR, _ridL, _ridR] = res[0] @@ -603,6 +616,8 @@ TEST_F(db_TuplesTest, store) { db::Tuple::Data::pid_t, std::string, int, + std::int64_t, + std::int64_t, db::Tuple::rid_t, db::Tuple::rid_t>(); @@ -618,6 +633,8 @@ TEST_F(db_TuplesTest, store) { EXPECT_EQ(tuple.rPrincipalId(), rPrincipalId); EXPECT_EQ(tuple.id(), _id); EXPECT_EQ(tuple.rev(), _rev); + EXPECT_EQ(tuple.hashL(), _hashL); + EXPECT_EQ(tuple.hashR(), _hashR); EXPECT_EQ(tuple.ridL(), _ridL); EXPECT_EQ(tuple.ridR(), _ridR); } From 5c046d2ab08740a515c547323263f1bd0045d26e Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Wed, 5 Jun 2024 11:35:00 +0100 Subject: [PATCH 03/16] (db) tuplets --- src/db/CMakeLists.txt | 3 + src/db/tuplets.cpp | 74 +++++++++++++++++++++ src/db/tuplets.h | 35 ++++++++++ src/db/tuplets_test.cpp | 140 ++++++++++++++++++++++++++++++++++++++++ src/err/errors.h | 2 + 5 files changed, 254 insertions(+) create mode 100644 src/db/tuplets.cpp create mode 100644 src/db/tuplets.h create mode 100644 src/db/tuplets_test.cpp diff --git a/src/db/CMakeLists.txt b/src/db/CMakeLists.txt index 0758532f..d87fa80d 100644 --- a/src/db/CMakeLists.txt +++ b/src/db/CMakeLists.txt @@ -4,6 +4,7 @@ target_sources(db pg.cpp principals.cpp tuples.cpp + tuplets.cpp PUBLIC FILE_SET headers TYPE HEADERS FILES @@ -11,6 +12,7 @@ target_sources(db pg.h principals.h tuples.h + tuplets.h PRIVATE FILE_SET private_headers TYPE HEADERS FILES @@ -51,6 +53,7 @@ if (SENTIUM_BUILD_TESTING) pg_test.cpp principals_test.cpp tuples_test.cpp + tuplets_test.cpp ) target_link_libraries(db_tests diff --git a/src/db/tuplets.cpp b/src/db/tuplets.cpp new file mode 100644 index 00000000..003e788d --- /dev/null +++ b/src/db/tuplets.cpp @@ -0,0 +1,74 @@ +#include "tuplets.h" + +#include + +#include "err/errors.h" + +namespace db { +Tuplet::Tuplet(const pg::row_t &r) : + _hash(r["_hash"].as()), _id(r["_id"].as()), + _relation(r["relation"].as()), _strand(r["strand"].as()) {} + +Tuplets TupletsList( + std::string_view spaceId, std::optional left, std::optional right, + std::optional relation, std::uint16_t count) { + + if (left && right) { + throw err::DbTupletsInvalidListArgs(); + } + + std::int64_t hv; + std::string hash, strand; + std::string where = "where space_id = $1::text"; + + if (left) { + hv = left->hash(); + hash = "_hash_r"; + strand = "null"; + where += " and _hash_l = $2::bigint"; + } else if (right) { + hv = right->hash(); + hash = "_hash_l"; + strand = "strand"; + where += " and _hash_r = $2::bigint"; + } else { + throw err::DbTupletsInvalidListArgs(); + } + + if (relation) { + where += " and relation = $3::text"; + } + + const std::string qry = fmt::format( + R"( + select + _id, + {} as _hash, + relation, + {} as strand + from tuples + {} + order by _hash desc + limit {:d} + )", + hash, + strand, + where, + count); + + db::pg::result_t res; + if (relation) { + res = pg::exec(qry, spaceId, hv, relation); + } else { + res = pg::exec(qry, spaceId, hv); + } + + Tuplets tuplets; + tuplets.reserve(res.affected_rows()); + for (const auto &r : res) { + tuplets.emplace_back(r); + } + + return tuplets; +} +} // namespace db diff --git a/src/db/tuplets.h b/src/db/tuplets.h new file mode 100644 index 00000000..e565e6e0 --- /dev/null +++ b/src/db/tuplets.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include + +#include "pg.h" +#include "tuples.h" + +namespace db { +class Tuplet { +public: + using strand_t = std::optional; + + Tuplet(const pg::row_t &r); + + const std::int64_t &hash() const noexcept { return _hash; } + const std::string &id() const noexcept { return _id; } + const std::string &relation() const noexcept { return _relation; } + const strand_t &strand() const noexcept { return _strand; } + +private: + std::int64_t _hash; + std::string _id; + std::string _relation; + strand_t _strand; +}; + +using Tuplets = std::vector; + +Tuplets TupletsList( + std::string_view spaceId, std::optional left, std::optional right, + std::optional relation = std::nullopt, std::uint16_t count = 10); +} // namespace db diff --git a/src/db/tuplets_test.cpp b/src/db/tuplets_test.cpp new file mode 100644 index 00000000..5eff7423 --- /dev/null +++ b/src/db/tuplets_test.cpp @@ -0,0 +1,140 @@ +#include + +#include "err/errors.h" + +#include "testing.h" +#include "tuplets.h" + +class db_TupletsTest : public testing::Test { +protected: + static void SetUpTestSuite() { + db::testing::setup(); + + // Clear data + db::pg::exec("truncate table tuples;"); + } + + void SetUp() { + // Clear data from each test + db::pg::exec("delete from tuples;"); + } + + static void TearDownTestSuite() { db::testing::teardown(); } +}; + +TEST_F(db_TupletsTest, list) { + // Seed tuple to check other tests are only returning expected results + db::Tuple tuple({ + .lEntityId = "left", + .lEntityType = "db_TupletsTest.list", + .relation = "relation", + .rEntityId = "right", + .rEntityType = "db_TupletsTest.list", + .strand = "strand", + }); + ASSERT_NO_THROW(tuple.store()); + + // Success: list right + { + db::Tuple tuple({ + .lEntityId = "left", + .lEntityType = "db_TupletsTest.list-right", + .relation = "relation", + .rEntityId = "right", + .rEntityType = "db_TupletsTest.list", + .strand = "strand", + }); + ASSERT_NO_THROW(tuple.store()); + + db::Tuplets results; + ASSERT_NO_THROW( + results = + db::TupletsList(tuple.spaceId(), {{tuple.lEntityType(), tuple.lEntityId()}}, {})); + + ASSERT_EQ(1, results.size()); + + const auto &actual = results.front(); + EXPECT_EQ(tuple.hashR(), actual.hash()); + EXPECT_EQ(tuple.id(), actual.id()); + EXPECT_EQ(tuple.relation(), actual.relation()); + EXPECT_FALSE(actual.strand()); + } + + // Success: list left + { + db::Tuple tuple({ + .lEntityId = "left", + .lEntityType = "db_TupletsTest.list", + .relation = "relation", + .rEntityId = "right", + .rEntityType = "db_TupletsTest.list-left", + .strand = "strand", + }); + ASSERT_NO_THROW(tuple.store()); + + db::Tuplets results; + ASSERT_NO_THROW( + results = + db::TupletsList(tuple.spaceId(), {}, {{tuple.rEntityType(), tuple.rEntityId()}})); + + ASSERT_EQ(1, results.size()); + + const auto &actual = results.front(); + EXPECT_EQ(tuple.hashL(), actual.hash()); + EXPECT_EQ(tuple.id(), actual.id()); + EXPECT_EQ(tuple.relation(), actual.relation()); + EXPECT_EQ(tuple.strand(), actual.strand()); + } + + // Success: list with relation + { + db::Tuples tuples({ + {{ + .lEntityId = "left", + .lEntityType = "db_TupletsTest.list-with_relation", + .relation = "relation[0]", + .rEntityId = "right", + .rEntityType = "db_TupletsTest.list-with_relation", + .strand = "strand", + }}, + {{ + .lEntityId = "left", + .lEntityType = "db_TupletsTest.list-with_relation", + .relation = "relation[1]", + .rEntityId = "right", + .rEntityType = "db_TupletsTest.list-with_relation", + .strand = "strand", + }}, + }); + + for (auto &t : tuples) { + ASSERT_NO_THROW(t.store()); + } + + db::Tuplets results; + ASSERT_NO_THROW( + results = db::TupletsList( + tuple.spaceId(), + {}, + {{tuples[0].rEntityType(), tuples[0].rEntityId()}}, + tuples[0].relation())); + + ASSERT_EQ(1, results.size()); + + const auto &actual = results.front(); + EXPECT_EQ(tuples[0].hashL(), actual.hash()); + EXPECT_EQ(tuples[0].id(), actual.id()); + EXPECT_EQ(tuples[0].relation(), actual.relation()); + EXPECT_EQ(tuples[0].strand(), actual.strand()); + } + + // Error: invalid args + { + EXPECT_THROW( + db::TupletsList({}, db::Tuple::Entity(), db::Tuple::Entity()), + err::DbTupletsInvalidListArgs); + + EXPECT_THROW( + db::TupletsList({}, std::nullopt, std::nullopt), err::DbTupletsInvalidListArgs); + } +} diff --git a/src/err/errors.h b/src/err/errors.h index c37301f3..8b22e9f2 100644 --- a/src/err/errors.h +++ b/src/err/errors.h @@ -22,6 +22,8 @@ using DbTupleNotFound = basic_error<"sentium:1.4.3.404", "Tuple not found"> using DbTuplesInvalidListArgs = basic_error<"sentium:1.4.4.400", "Invalid arguments for listing tuples">; +using DbTupletsInvalidListArgs = basic_error<"", "Invalid arguments for listing tuplets">; + using RpcPrincipalsAlreadyExists = basic_error<"sentium:2.1.1.409", "Principal already exists">; using RpcPrincipalsNotFound = basic_error<"sentium:2.1.2.404", "Principal not found">; From a07cdcac432a86996a2960b73d157549a9d66925 Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Wed, 5 Jun 2024 13:46:11 +0100 Subject: [PATCH 04/16] (db) tidy-up --- db/schema.sql | 8 ++++---- src/db/tuples.cpp | 22 +++++++++++----------- src/db/tuples.h | 12 +++++++----- src/db/tuples_test.cpp | 24 ++++++++++++------------ src/db/tuplets.cpp | 8 ++++---- src/db/tuplets_test.cpp | 6 +++--- 6 files changed, 41 insertions(+), 39 deletions(-) diff --git a/db/schema.sql b/db/schema.sql index f28333a5..8ce67d6b 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -40,8 +40,8 @@ create table if not exists tuples ( -- Hash values of entities -- - _hash_l bigint, - _hash_r bigint, + _l_hash bigint, + _r_hash bigint, -- Self references for computed tuples -- @@ -77,5 +77,5 @@ create table if not exists tuples ( constraint "tuples.check-attrs" check (jsonb_typeof(attrs) = 'object') ); -create index "tuples.idx-ltr" on tuples using btree (space_id, _hash_l, relation, _hash_r, _id); -create index "tuples.idx-rtl" on tuples using btree (space_id, _hash_r, relation, strand, _hash_l, _id); +create index "tuples.idx-ltr" on tuples using btree (space_id, _l_hash, relation, _r_hash, _id); +create index "tuples.idx-rtl" on tuples using btree (space_id, _r_hash, relation, strand, _l_hash, _id); diff --git a/src/db/tuples.cpp b/src/db/tuples.cpp index ee33d6a3..bb39e0c4 100644 --- a/src/db/tuples.cpp +++ b/src/db/tuples.cpp @@ -9,12 +9,12 @@ namespace db { Tuple::Tuple(const Tuple::Data &data) noexcept : - _data(data), _hashL(), _hashR(), _id(), _rev(0), _ridL(), _ridR() { + _data(data), _lHash(), _rHash(), _id(), _rev(0), _ridL(), _ridR() { sanitise(); } Tuple::Tuple(Tuple::Data &&data) noexcept : - _data(std::move(data)), _hashL(), _hashR(), _id(), _rev(0), _ridL(), _ridR() { + _data(std::move(data)), _lHash(), _rHash(), _id(), _rev(0), _ridL(), _ridR() { sanitise(); } @@ -31,7 +31,7 @@ Tuple::Tuple(const pg::row_t &r) : .spaceId = r["space_id"].as(), .strand = r["strand"].as(), }), - _hashL(r["_hash_l"].as()), _hashR(r["_hash_r"].as()), + _lHash(r["_l_hash"].as()), _rHash(r["_r_hash"].as()), _id(r["_id"].as()), _rev(r["_rev"].as()), _ridL(r["_rid_l"].as()), _ridR(r["_rid_r"].as()) {} @@ -46,7 +46,7 @@ Tuple::Tuple(const Tuple &left, const Tuple &right) noexcept : .rPrincipalId = right.rPrincipalId(), .spaceId = left.spaceId(), }), - _hashL(left.hashL()), _hashR(right.hashR()), _id(), _rev(0), _ridL(left.id()), + _lHash(left.lHash()), _rHash(right.rHash()), _id(), _rev(0), _ridL(left.id()), _ridR(right.id()) {} bool Tuple::discard(std::string_view id) { @@ -61,8 +61,8 @@ bool Tuple::discard(std::string_view id) { } void Tuple::hash() noexcept { - _hashL = Entity(_data.lEntityType, _data.lEntityId).hash(); - _hashR = Entity(_data.rEntityType, _data.rEntityId).hash(); + _lHash = Entity(_data.lEntityType, _data.lEntityId).hash(); + _rHash = Entity(_data.rEntityType, _data.rEntityId).hash(); } std::optional Tuple::lookup( @@ -88,7 +88,7 @@ Tuple Tuple::retrieve(std::string_view id) { attrs, l_principal_id, r_principal_id, _id, _rev, - _hash_l, _hash_r, + _l_hash, _r_hash, _rid_l, _rid_r from tuples where _id = $1::text; @@ -131,7 +131,7 @@ void Tuple::store() { attrs, l_principal_id, r_principal_id, _id, _rev, - _hash_l, _hash_r, + _l_hash, _r_hash, _rid_l, _rid_r ) values ( $1::text, @@ -174,8 +174,8 @@ void Tuple::store() { _data.rPrincipalId, _id, _rev, - _hashL, - _hashR, + _lHash, + _rHash, _ridL, _ridR); } catch (pqxx::check_violation &) { @@ -339,7 +339,7 @@ Tuples LookupTuples( attrs, l_principal_id, r_principal_id, _id, _rev, - _hash_l, _hash_r, + _l_hash, _r_hash, _rid_l, _rid_r from tuples {} diff --git a/src/db/tuples.h b/src/db/tuples.h index c5ed206f..1b65015d 100644 --- a/src/db/tuples.h +++ b/src/db/tuples.h @@ -87,11 +87,13 @@ class Tuple { void strand(const std::string &strand) noexcept { _data.strand = strand; } const std::string &id() const noexcept { return _id; } - const std::int64_t hashL() const noexcept { return _hashL; } - const std::int64_t hashR() const noexcept { return _hashR; } const int &rev() const noexcept { return _rev; } - const rid_t &ridL() const noexcept { return _ridL; } - const rid_t &ridR() const noexcept { return _ridR; } + + const std::int64_t lHash() const noexcept { return _lHash; } + const std::int64_t rHash() const noexcept { return _rHash; } + + const rid_t &ridL() const noexcept { return _ridL; } + const rid_t &ridR() const noexcept { return _ridR; } void store(); @@ -109,9 +111,9 @@ class Tuple { void sanitise() noexcept; Data _data; - std::int64_t _hashL, _hashR; std::string _id; int _rev; + std::int64_t _lHash, _rHash; rid_t _ridL, _ridR; }; diff --git a/src/db/tuples_test.cpp b/src/db/tuples_test.cpp index 1a1a1228..1105820b 100644 --- a/src/db/tuples_test.cpp +++ b/src/db/tuples_test.cpp @@ -57,8 +57,8 @@ TEST_F(db_TuplesTest, constructor) { EXPECT_EQ(right.rEntityType(), joined.rEntityType()); EXPECT_EQ(left.spaceId(), joined.spaceId()); EXPECT_TRUE(joined.strand().empty()); - EXPECT_EQ(left.hashL(), joined.hashL()); - EXPECT_EQ(right.hashR(), joined.hashR()); + EXPECT_EQ(left.lHash(), joined.lHash()); + EXPECT_EQ(right.rHash(), joined.rHash()); EXPECT_EQ(left.id(), joined.ridL()); EXPECT_EQ(right.id(), joined.ridR()); @@ -437,7 +437,7 @@ TEST_F(db_TuplesTest, retrieve) { r_entity_type, r_entity_id, attrs, _id, _rev, - _hash_l, _hash_r + _l_hash, _r_hash ) values ( $1::text, $2::text, @@ -469,8 +469,8 @@ TEST_F(db_TuplesTest, retrieve) { EXPECT_FALSE(tuple.ridL()); EXPECT_FALSE(tuple.ridR()); EXPECT_EQ(1729, tuple.rev()); - EXPECT_EQ(-3631866150419398620, tuple.hashL()); - EXPECT_EQ(7468059380061813551, tuple.hashR()); + EXPECT_EQ(-3631866150419398620, tuple.lHash()); + EXPECT_EQ(7468059380061813551, tuple.rHash()); EXPECT_EQ("db_TuplesTest.retrieve", tuple.lEntityType()); EXPECT_EQ("left", tuple.lEntityId()); @@ -550,8 +550,8 @@ TEST_F(db_TuplesTest, sanitise) { EXPECT_EQ(tuple.lPrincipalId(), tuple.lEntityId()); EXPECT_EQ(db::common::principal_entity_v, tuple.rEntityType()); EXPECT_EQ(tuple.rPrincipalId(), tuple.rEntityId()); - EXPECT_EQ(4978332106395442344, tuple.hashL()); - EXPECT_EQ(459406847117771879, tuple.hashR()); + EXPECT_EQ(4978332106395442344, tuple.lHash()); + EXPECT_EQ(459406847117771879, tuple.rHash()); } } @@ -577,7 +577,7 @@ TEST_F(db_TuplesTest, store) { attrs, l_principal_id, r_principal_id, _id, _rev, - _hash_l, _hash_r, + _l_hash, _r_hash, _rid_l, _rid_r from tuples where _id = $1::text; @@ -599,8 +599,8 @@ TEST_F(db_TuplesTest, store) { rPrincipalId, _id, _rev, - _hashL, - _hashR, + _lHash, + _rHash, _ridL, _ridR] = res[0] @@ -633,8 +633,8 @@ TEST_F(db_TuplesTest, store) { EXPECT_EQ(tuple.rPrincipalId(), rPrincipalId); EXPECT_EQ(tuple.id(), _id); EXPECT_EQ(tuple.rev(), _rev); - EXPECT_EQ(tuple.hashL(), _hashL); - EXPECT_EQ(tuple.hashR(), _hashR); + EXPECT_EQ(tuple.lHash(), _lHash); + EXPECT_EQ(tuple.rHash(), _rHash); EXPECT_EQ(tuple.ridL(), _ridL); EXPECT_EQ(tuple.ridR(), _ridR); } diff --git a/src/db/tuplets.cpp b/src/db/tuplets.cpp index 003e788d..91723a30 100644 --- a/src/db/tuplets.cpp +++ b/src/db/tuplets.cpp @@ -23,14 +23,14 @@ Tuplets TupletsList( if (left) { hv = left->hash(); - hash = "_hash_r"; + hash = "_r_hash"; strand = "null"; - where += " and _hash_l = $2::bigint"; + where += " and _l_hash = $2::bigint"; } else if (right) { hv = right->hash(); - hash = "_hash_l"; + hash = "_l_hash"; strand = "strand"; - where += " and _hash_r = $2::bigint"; + where += " and _r_hash = $2::bigint"; } else { throw err::DbTupletsInvalidListArgs(); } diff --git a/src/db/tuplets_test.cpp b/src/db/tuplets_test.cpp index 5eff7423..6ed08bfd 100644 --- a/src/db/tuplets_test.cpp +++ b/src/db/tuplets_test.cpp @@ -54,7 +54,7 @@ TEST_F(db_TupletsTest, list) { ASSERT_EQ(1, results.size()); const auto &actual = results.front(); - EXPECT_EQ(tuple.hashR(), actual.hash()); + EXPECT_EQ(tuple.rHash(), actual.hash()); EXPECT_EQ(tuple.id(), actual.id()); EXPECT_EQ(tuple.relation(), actual.relation()); EXPECT_FALSE(actual.strand()); @@ -80,7 +80,7 @@ TEST_F(db_TupletsTest, list) { ASSERT_EQ(1, results.size()); const auto &actual = results.front(); - EXPECT_EQ(tuple.hashL(), actual.hash()); + EXPECT_EQ(tuple.lHash(), actual.hash()); EXPECT_EQ(tuple.id(), actual.id()); EXPECT_EQ(tuple.relation(), actual.relation()); EXPECT_EQ(tuple.strand(), actual.strand()); @@ -122,7 +122,7 @@ TEST_F(db_TupletsTest, list) { ASSERT_EQ(1, results.size()); const auto &actual = results.front(); - EXPECT_EQ(tuples[0].hashL(), actual.hash()); + EXPECT_EQ(tuples[0].lHash(), actual.hash()); EXPECT_EQ(tuples[0].id(), actual.id()); EXPECT_EQ(tuples[0].relation(), actual.relation()); EXPECT_EQ(tuples[0].strand(), actual.strand()); From e86a3ef5437d16ee447aa08878155f2f9cf912a8 Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Wed, 5 Jun 2024 13:48:52 +0100 Subject: [PATCH 05/16] (db) make tests consistent across compilers --- src/db/tuples_test.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/db/tuples_test.cpp b/src/db/tuples_test.cpp index 1105820b..ec33e7ea 100644 --- a/src/db/tuples_test.cpp +++ b/src/db/tuples_test.cpp @@ -550,8 +550,9 @@ TEST_F(db_TuplesTest, sanitise) { EXPECT_EQ(tuple.lPrincipalId(), tuple.lEntityId()); EXPECT_EQ(db::common::principal_entity_v, tuple.rEntityType()); EXPECT_EQ(tuple.rPrincipalId(), tuple.rEntityId()); - EXPECT_EQ(4978332106395442344, tuple.lHash()); - EXPECT_EQ(459406847117771879, tuple.rHash()); + + EXPECT_EQ(db::Tuple::Entity(tuple.lEntityType(), tuple.lEntityId()).hash(), tuple.lHash()); + EXPECT_EQ(db::Tuple::Entity(tuple.rEntityType(), tuple.rEntityId()).hash(), tuple.rHash()); } } From 10b96d55deb327aaf8d8ba45e88c18c2b3a6142d Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Wed, 5 Jun 2024 13:54:00 +0100 Subject: [PATCH 06/16] (err) fix missing error codes --- src/err/errors.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/err/errors.h b/src/err/errors.h index 8b22e9f2..4ed6275b 100644 --- a/src/err/errors.h +++ b/src/err/errors.h @@ -22,7 +22,7 @@ using DbTupleNotFound = basic_error<"sentium:1.4.3.404", "Tuple not found"> using DbTuplesInvalidListArgs = basic_error<"sentium:1.4.4.400", "Invalid arguments for listing tuples">; -using DbTupletsInvalidListArgs = basic_error<"", "Invalid arguments for listing tuplets">; +using DbTupletsInvalidListArgs = basic_error<"sentium:1.4.5.400", "Invalid arguments for listing tuplets">; using RpcPrincipalsAlreadyExists = basic_error<"sentium:2.1.1.409", "Principal already exists">; using RpcPrincipalsNotFound = basic_error<"sentium:2.1.2.404", "Principal not found">; From 592162fcd9ba9dde2f89d41605e35e43e7671bc1 Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Wed, 5 Jun 2024 13:56:39 +0100 Subject: [PATCH 07/16] (db) tidy-up --- src/db/tuples.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/tuples.cpp b/src/db/tuples.cpp index bb39e0c4..c2c4c9fe 100644 --- a/src/db/tuples.cpp +++ b/src/db/tuples.cpp @@ -264,7 +264,7 @@ Tuples ListTuples( attrs, l_principal_id, r_principal_id, _id, _rev, - _hash_l, _hash_r, + _l_hash, _r_hash, _rid_l, _rid_r from tuples {} From 3697a559d0426fcb075ee7dac9fbef302d070d56 Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Wed, 5 Jun 2024 13:59:57 +0100 Subject: [PATCH 08/16] (err) lint --- src/err/errors.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/err/errors.h b/src/err/errors.h index 4ed6275b..e477e498 100644 --- a/src/err/errors.h +++ b/src/err/errors.h @@ -22,7 +22,8 @@ using DbTupleNotFound = basic_error<"sentium:1.4.3.404", "Tuple not found"> using DbTuplesInvalidListArgs = basic_error<"sentium:1.4.4.400", "Invalid arguments for listing tuples">; -using DbTupletsInvalidListArgs = basic_error<"sentium:1.4.5.400", "Invalid arguments for listing tuplets">; +using DbTupletsInvalidListArgs = + basic_error<"sentium:1.4.5.400", "Invalid arguments for listing tuplets">; using RpcPrincipalsAlreadyExists = basic_error<"sentium:2.1.1.409", "Principal already exists">; using RpcPrincipalsNotFound = basic_error<"sentium:2.1.2.404", "Principal not found">; From 96f7d3100e0482f9718b65c0329975c1c7a580aa Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Wed, 5 Jun 2024 14:02:06 +0100 Subject: [PATCH 09/16] (svc) use hash comparisons in spot search --- src/svc/relations.cpp | 25 ++++++++++++++++++------- src/svc/relations_test.cpp | 2 +- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/svc/relations.cpp b/src/svc/relations.cpp index b8f45a20..047fca4f 100644 --- a/src/svc/relations.cpp +++ b/src/svc/relations.cpp @@ -6,6 +6,7 @@ #include #include +#include "db/tuplets.h" #include "encoding/b32.h" #include "err/errors.h" #include "sentium/detail/pagination.pb.h" @@ -551,24 +552,34 @@ Impl::spot_t Impl::spot( std::int32_t cost = 0; - auto t1 = db::ListTuplesRight(spaceId, left, {}, {}, limit); - auto t2 = db::ListTuplesLeft(spaceId, right, relation, {}, limit); + auto t1 = db::TupletsList(spaceId, left, {}, {}, limit); + auto t2 = db::TupletsList(spaceId, {}, right, relation, limit); auto i = t1.cbegin(); auto j = t2.cbegin(); while (i != t1.cend() && j != t2.cend()) { cost++; - auto r = i->rEntityId().compare(j->lEntityId()); - if (r == 0) { - if (i->relation() == j->strand() && i->rEntityType() == j->lEntityType()) { - return {cost, db::Tuple(*i, *j)}; + if (i->hash() == j->hash()) { + if (i->relation() == j->strand()) { + auto tl = db::Tuple::retrieve(i->id()); + auto tr = db::Tuple::retrieve(j->id()); + + if (tl.rEntityType() == tr.lEntityType() && tl.rEntityId() == tr.lEntityId()) { + return {cost, db::Tuple(tl, tr)}; + } + } + + if (j != t2.cend() - 1) { + j++; } else { i++; } + + continue; } - if (r > 0) { + if (i->hash() > j->hash()) { i++; } else { j++; diff --git a/src/svc/relations_test.cpp b/src/svc/relations_test.cpp index 6c55f589..7cef551f 100644 --- a/src/svc/relations_test.cpp +++ b/src/svc/relations_test.cpp @@ -233,7 +233,7 @@ TEST_F(svc_RelationsTest, Check) { EXPECT_EQ(grpcxx::status::code_t::ok, result.status.code()); ASSERT_TRUE(result.response); EXPECT_EQ(true, result.response->found()); - EXPECT_EQ(2, result.response->cost()); + EXPECT_EQ(3, result.response->cost()); ASSERT_TRUE(result.response->has_tuple()); auto &actual = result.response->tuple(); From ed06283ccafca3827dff750663211e6f9163b2bc Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Wed, 5 Jun 2024 14:04:12 +0100 Subject: [PATCH 10/16] (db) ensure indexes are used when listing tuples --- src/db/tuples.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/db/tuples.cpp b/src/db/tuples.cpp index c2c4c9fe..660eba2b 100644 --- a/src/db/tuples.cpp +++ b/src/db/tuples.cpp @@ -230,26 +230,28 @@ Tuples ListTuples( std::string where = "where space_id = $1::text"; std::string sort; if (left) { - entity = *left; - where += " and l_entity_type = $2::text and l_entity_id = $3::text"; - sort = "r_entity_id"; + entity = *left; + where += + " and _l_hash = $2::bigint and l_entity_type = $3::text and l_entity_id = $4::text"; + sort = "r_entity_id"; } else if (right) { - entity = *right; - where += " and r_entity_type = $2::text and r_entity_id = $3::text"; - sort = "l_entity_id"; + entity = *right; + where += + " and _r_hash = $2::bigint and r_entity_type = $3::text and r_entity_id = $4::text"; + sort = "l_entity_id"; } else { throw err::DbTuplesInvalidListArgs(); } if (relation) { - where += " and relation = $4::text"; + where += " and relation = $5::text"; } if (!lastId.empty()) { if (relation) { - where += fmt::format(" and {} < $5::text", sort); + where += fmt::format(" and {} < $6::text", sort); } else { - where += fmt::format(" and {} < $4::text", sort); + where += fmt::format(" and {} < $5::text", sort); } } @@ -277,13 +279,13 @@ Tuples ListTuples( db::pg::result_t res; if (relation && !lastId.empty()) { - res = pg::exec(qry, spaceId, entity.type(), entity.id(), relation, lastId); + res = pg::exec(qry, spaceId, entity.hash(), entity.type(), entity.id(), relation, lastId); } else if (relation) { - res = pg::exec(qry, spaceId, entity.type(), entity.id(), relation); + res = pg::exec(qry, spaceId, entity.hash(), entity.type(), entity.id(), relation); } else if (!lastId.empty()) { - res = pg::exec(qry, spaceId, entity.type(), entity.id(), lastId); + res = pg::exec(qry, spaceId, entity.hash(), entity.type(), entity.id(), lastId); } else { - res = pg::exec(qry, spaceId, entity.type(), entity.id()); + res = pg::exec(qry, spaceId, entity.hash(), entity.type(), entity.id()); } Tuples tuples; From 80d2486a9ef759ef0efe0bc0df7fee9762c13501 Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Wed, 5 Jun 2024 14:13:08 +0100 Subject: [PATCH 11/16] (bench) update benchmarks --- bench/algorithms_test.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/bench/algorithms_test.cpp b/bench/algorithms_test.cpp index 83de941c..58c6236c 100644 --- a/bench/algorithms_test.cpp +++ b/bench/algorithms_test.cpp @@ -81,16 +81,16 @@ BENCHMARK([](benchmark::State &st) { right.emplace_back(xid::next()); } - std::size_t ops = 0; + std::size_t ops = 0; + std::size_t cost = 0; for (auto _ : st) { - st.PauseTiming(); ops++; - st.ResumeTiming(); auto i = left.cbegin(); auto j = right.cbegin(); while (i != left.cend() && j != right.cend()) { + cost++; auto r = i->compare(*j); if (r == 0) { @@ -107,14 +107,15 @@ BENCHMARK([](benchmark::State &st) { st.counters.insert({ {"ops", benchmark::Counter(ops, benchmark::Counter::kIsRate)}, + {"comparisons", benchmark::Counter(cost, benchmark::Counter::kIsRate)}, }); }) ->Name("bm_spot_intersection") ->Range(8, 8 << 10); BENCHMARK([](benchmark::State &st) { - std::vector left; - std::vector right; + std::vector left; + std::vector right; left.reserve(st.range(0)); right.reserve(st.range(0)); @@ -152,5 +153,5 @@ BENCHMARK([](benchmark::State &st) { {"comparisons", benchmark::Counter(cost, benchmark::Counter::kIsRate)}, }); }) - ->Name("bm_spot_intersection_int") + ->Name("bm_spot_intersection_int64") ->Range(8, 8 << 10); From b6ac479dca30a653c35d932eb77202a02df12416 Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Wed, 5 Jun 2024 16:22:50 +0100 Subject: [PATCH 12/16] (db) tidy-up --- src/db/tuples.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/db/tuples.cpp b/src/db/tuples.cpp index 660eba2b..5281e7b7 100644 --- a/src/db/tuples.cpp +++ b/src/db/tuples.cpp @@ -9,12 +9,12 @@ namespace db { Tuple::Tuple(const Tuple::Data &data) noexcept : - _data(data), _lHash(), _rHash(), _id(), _rev(0), _ridL(), _ridR() { + _data(data), _id(), _rev(0), _lHash(), _rHash(), _ridL(), _ridR() { sanitise(); } Tuple::Tuple(Tuple::Data &&data) noexcept : - _data(std::move(data)), _lHash(), _rHash(), _id(), _rev(0), _ridL(), _ridR() { + _data(std::move(data)), _id(), _rev(0), _lHash(), _rHash(), _ridL(), _ridR() { sanitise(); } @@ -31,9 +31,9 @@ Tuple::Tuple(const pg::row_t &r) : .spaceId = r["space_id"].as(), .strand = r["strand"].as(), }), + _id(r["_id"].as()), _rev(r["_rev"].as()), _lHash(r["_l_hash"].as()), _rHash(r["_r_hash"].as()), - _id(r["_id"].as()), _rev(r["_rev"].as()), _ridL(r["_rid_l"].as()), - _ridR(r["_rid_r"].as()) {} + _ridL(r["_rid_l"].as()), _ridR(r["_rid_r"].as()) {} Tuple::Tuple(const Tuple &left, const Tuple &right) noexcept : _data({ @@ -46,7 +46,7 @@ Tuple::Tuple(const Tuple &left, const Tuple &right) noexcept : .rPrincipalId = right.rPrincipalId(), .spaceId = left.spaceId(), }), - _lHash(left.lHash()), _rHash(right.rHash()), _id(), _rev(0), _ridL(left.id()), + _id(), _rev(0), _lHash(left.lHash()), _rHash(right.rHash()), _ridL(left.id()), _ridR(right.id()) {} bool Tuple::discard(std::string_view id) { From 12a59f2322f146702c7eee9b4a39239ee1808807 Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Wed, 5 Jun 2024 16:32:07 +0100 Subject: [PATCH 13/16] (svc) tidy-up --- src/svc/relations.cpp | 4 ++++ src/svc/relations_test.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/svc/relations.cpp b/src/svc/relations.cpp index 047fca4f..7d6022c6 100644 --- a/src/svc/relations.cpp +++ b/src/svc/relations.cpp @@ -565,6 +565,10 @@ Impl::spot_t Impl::spot( auto tl = db::Tuple::retrieve(i->id()); auto tr = db::Tuple::retrieve(j->id()); + cost += 2; + + // Compare text (unhashed) values to avoid any false positives due to hash + // collisions if (tl.rEntityType() == tr.lEntityType() && tl.rEntityId() == tr.lEntityId()) { return {cost, db::Tuple(tl, tr)}; } diff --git a/src/svc/relations_test.cpp b/src/svc/relations_test.cpp index 7cef551f..28c7b8d2 100644 --- a/src/svc/relations_test.cpp +++ b/src/svc/relations_test.cpp @@ -233,7 +233,7 @@ TEST_F(svc_RelationsTest, Check) { EXPECT_EQ(grpcxx::status::code_t::ok, result.status.code()); ASSERT_TRUE(result.response); EXPECT_EQ(true, result.response->found()); - EXPECT_EQ(3, result.response->cost()); + EXPECT_EQ(5, result.response->cost()); ASSERT_TRUE(result.response->has_tuple()); auto &actual = result.response->tuple(); From 3135eab015c45ecc037402c8324f8fe7f290a002 Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Wed, 5 Jun 2024 17:48:24 +0100 Subject: [PATCH 14/16] (db) drop redundant index from tuples --- db/schema.sql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/db/schema.sql b/db/schema.sql index 8ce67d6b..d3284a84 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -77,5 +77,4 @@ create table if not exists tuples ( constraint "tuples.check-attrs" check (jsonb_typeof(attrs) = 'object') ); -create index "tuples.idx-ltr" on tuples using btree (space_id, _l_hash, relation, _r_hash, _id); -create index "tuples.idx-rtl" on tuples using btree (space_id, _r_hash, relation, strand, _l_hash, _id); +create index "tuples.idx-rtl" on tuples using btree (space_id, _r_hash, relation, _l_hash, strand, _id); From 6a783def099d8795682d4e89cf63f34d547e71c1 Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Wed, 5 Jun 2024 21:51:19 +0100 Subject: [PATCH 15/16] (db) avoid unnecessary query conditions when listing tuples --- src/db/tuples.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/db/tuples.cpp b/src/db/tuples.cpp index 5281e7b7..29e94c18 100644 --- a/src/db/tuples.cpp +++ b/src/db/tuples.cpp @@ -227,18 +227,19 @@ Tuples ListTuples( } Tuple::Entity entity; + std::int64_t hash = 0; std::string where = "where space_id = $1::text"; std::string sort; if (left) { - entity = *left; - where += - " and _l_hash = $2::bigint and l_entity_type = $3::text and l_entity_id = $4::text"; - sort = "r_entity_id"; + entity = *left; + sort = "r_entity_id"; + where += " and 0 = $2::bigint and l_entity_type = $3::text and l_entity_id = $4::text"; } else if (right) { entity = *right; + hash = entity.hash(); + sort = "l_entity_id"; where += " and _r_hash = $2::bigint and r_entity_type = $3::text and r_entity_id = $4::text"; - sort = "l_entity_id"; } else { throw err::DbTuplesInvalidListArgs(); } @@ -279,13 +280,13 @@ Tuples ListTuples( db::pg::result_t res; if (relation && !lastId.empty()) { - res = pg::exec(qry, spaceId, entity.hash(), entity.type(), entity.id(), relation, lastId); + res = pg::exec(qry, spaceId, hash, entity.type(), entity.id(), relation, lastId); } else if (relation) { - res = pg::exec(qry, spaceId, entity.hash(), entity.type(), entity.id(), relation); + res = pg::exec(qry, spaceId, hash, entity.type(), entity.id(), relation); } else if (!lastId.empty()) { - res = pg::exec(qry, spaceId, entity.hash(), entity.type(), entity.id(), lastId); + res = pg::exec(qry, spaceId, hash, entity.type(), entity.id(), lastId); } else { - res = pg::exec(qry, spaceId, entity.hash(), entity.type(), entity.id()); + res = pg::exec(qry, spaceId, hash, entity.type(), entity.id()); } Tuples tuples; From e1c4f58402c13898dd5cc79ccd40aa10aadf1512 Mon Sep 17 00:00:00 2001 From: Uditha Atukorala Date: Thu, 6 Jun 2024 09:51:16 +0100 Subject: [PATCH 16/16] =?UTF-8?q?(bench)=20=F0=9F=A4=A6=20sort=20datasets?= =?UTF-8?q?=20before=20doing=20spot=20intersection=20with=20int64s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bench/algorithms_test.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bench/algorithms_test.cpp b/bench/algorithms_test.cpp index 58c6236c..afe7e048 100644 --- a/bench/algorithms_test.cpp +++ b/bench/algorithms_test.cpp @@ -125,6 +125,9 @@ BENCHMARK([](benchmark::State &st) { right.emplace_back(std::rand()); } + std::sort(left.begin(), left.end()); + std::sort(right.begin(), right.end()); + std::size_t ops = 0; std::size_t cost = 0; for (auto _ : st) {