switched things up
This commit is contained in:
parent
cb3c7df552
commit
e24f034fd9
11 changed files with 183 additions and 43 deletions
|
@ -13,7 +13,7 @@ AlwaysBreakAfterReturnType: None
|
||||||
BreakBeforeBinaryOperators: All
|
BreakBeforeBinaryOperators: All
|
||||||
BreakBeforeBraces: Attach
|
BreakBeforeBraces: Attach
|
||||||
BreakBeforeConceptDeclarations: Always
|
BreakBeforeConceptDeclarations: Always
|
||||||
ColumnLimit: 80
|
ColumnLimit: 120
|
||||||
ContinuationIndentWidth: 2
|
ContinuationIndentWidth: 2
|
||||||
Cpp11BracedListStyle: true
|
Cpp11BracedListStyle: true
|
||||||
FixNamespaceComments: true
|
FixNamespaceComments: true
|
||||||
|
|
4
.envrc
4
.envrc
|
@ -3,7 +3,3 @@ if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then
|
||||||
fi
|
fi
|
||||||
watch_file devShell.nix shell.nix flake.nix
|
watch_file devShell.nix shell.nix flake.nix
|
||||||
use flake || use nix
|
use flake || use nix
|
||||||
|
|
||||||
CMAKE_GENERATOR=Ninja
|
|
||||||
CMAKE_MAKE_PROGRAM=Ninja
|
|
||||||
|
|
||||||
|
|
1
.gdbinit
Normal file
1
.gdbinit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
handle SIG35 nostop noprint
|
1
.gitmodules
vendored
1
.gitmodules
vendored
|
@ -1,3 +1,4 @@
|
||||||
[submodule "seastar"]
|
[submodule "seastar"]
|
||||||
path = seastar
|
path = seastar
|
||||||
url = https://github.com/scylladb/seastar.git
|
url = https://github.com/scylladb/seastar.git
|
||||||
|
branch = 2b43417d210edbd7a3c3065bcfe3c0a9aea27f75
|
||||||
|
|
|
@ -40,7 +40,10 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") # Make our cmak
|
||||||
####################
|
####################
|
||||||
## Dependencies ##
|
## Dependencies ##
|
||||||
####################
|
####################
|
||||||
|
find_package(Boost REQUIRED COMPONENTS system)
|
||||||
|
if(Boost_FOUND)
|
||||||
|
include_directories(${Boost_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
###############
|
###############
|
||||||
## Options ##
|
## Options ##
|
||||||
|
|
|
@ -5,20 +5,87 @@ Formerly named "HanoiDB" but the C++ version needed a new name, so ^H^H and
|
||||||
voila, "NoiDB".
|
voila, "NoiDB".
|
||||||
|
|
||||||
### History
|
### History
|
||||||
See [HanoiDB](https://github.com/krestenkrab/hanoidb) and the [lasp-lang](https://github.com/lasp-lang/hanoidb) fork.
|
See [HanoiDB](https://github.com/krestenkrab/hanoidb) and the
|
||||||
|
[lasp-lang](https://github.com/lasp-lang/hanoidb) fork.
|
||||||
|
|
||||||
|
### Network API
|
||||||
|
|
||||||
|
HTTP REST CRUD API
|
||||||
|
|
||||||
|
#### Create/Update
|
||||||
|
|
||||||
|
| *Method* | *Path* | *Consumes* | HTTP Code |
|
||||||
|
| -------- | --------------- | ------------------------ | --------- |
|
||||||
|
| `PUT` | `/kv/:key?value`| `value` query parameter | 2xx, etc. |
|
||||||
|
| `POST` | `/kv/:key` | `application/text` body | 2xx, etc |
|
||||||
|
|
||||||
|
*Path Parameters*
|
||||||
|
* `key` (string: "") - Specifies the path of the key to read.
|
||||||
|
|
||||||
|
*Query Parameters*
|
||||||
|
* `value` (string: "") - Specifies the value to store for the key.
|
||||||
|
|
||||||
|
#### Read Key
|
||||||
|
|
||||||
|
| *Method* | *Path* | *Produces* | HTTP Code |
|
||||||
|
| -------- | --------------- | ------------------------ | --------- |
|
||||||
|
| `GET` | `/kv/:key` | `application/text` value | 2xx, etc. |
|
||||||
|
|
||||||
|
*Path Parameters*
|
||||||
|
* `key` (string: "") - Specifies the path of the key to read.
|
||||||
|
|
||||||
|
#### Delete Key
|
||||||
|
|
||||||
|
| *Method* | *Path* | *Produces* | HTTP Code |
|
||||||
|
| -------- | --------------- | ------------------------ | --------- |
|
||||||
|
| `DELETE` | `/kv/:key` | | 2xx, etc. |
|
||||||
|
|
||||||
|
*Path Parameters*
|
||||||
|
* `key` (string: "") - Specifies the path of the key to read.
|
||||||
|
|
||||||
|
### Seastar Specifics
|
||||||
|
|
||||||
|
* All REST requests serviable by any shard.
|
||||||
|
* Queries map/reduce nurseries, then contact owning shard (possibly triggering
|
||||||
|
incremental merge work).
|
||||||
|
* Owner of level 2<sup>n</sup> governed by random slicing.
|
||||||
|
* Every node runs a nursery.
|
||||||
|
* Each nursery is at more 2^8 KVPs
|
||||||
|
* Nurseries are:
|
||||||
|
* B-trees in memory,
|
||||||
|
* and logged to disk according to format below.
|
||||||
|
* Searching across nurseries is a map/reduce operation over the shards.
|
||||||
|
* Combining, merging Nurseries owned by shard closing the nursery.
|
||||||
|
|
||||||
|
|
||||||
### Basics
|
### Basics
|
||||||
If there are N records, there are in log<sub>2</sub>(N) levels (each being a plain B-tree in a file named "A-*level*.data"). The file `A-0.data` has 1 record, `A-1.data` has 2 records, `A-2.data` has 4 records, and so on: `A-n.data` has 2<sup>n</sup> records.
|
If there are N records, there are in log<sub>2</sub>(N) levels (each being a
|
||||||
|
plain B-tree in a file named "A-*level*.data"). The file `A-0.data` has 1
|
||||||
|
record, `A-1.data` has 2 records, `A-2.data` has 4 records, and so on:
|
||||||
|
`A-n.data` has 2<sup>n</sup> records.
|
||||||
|
|
||||||
In "stable state", each level file is either full (there) or empty (not there); so if there are e.g. 20 records stored, then there are only data in filed `A-2.data` (4 records) and `A-4.data` (16 records).
|
In "stable state", each level file is either full (there) or empty (not there);
|
||||||
|
so if there are e.g. 20 records stored, then there are only 2 data files
|
||||||
|
`A-2.data` (4 records) and `A-4.data` (16 records) required.
|
||||||
|
|
||||||
OK, I've told you a lie. In practice, it is not practical to create a new file for each insert (injection at level #0), so we allows you to define the "top level" to be a number higher that #0; currently defaulting to #5 (32 records). That means that you take the amortization "hit" for ever 32 inserts.
|
In practice, it is not practical to create a new file for each insert (injection
|
||||||
|
at level #0), so we maintain a "top level" to be a number higher that #0;
|
||||||
|
currently defaulting to #5 (32 records). That means that you take the
|
||||||
|
amortization "hit" for ever 32 inserts. This first combined level is the
|
||||||
|
"Nursery".
|
||||||
|
|
||||||
### Lookup
|
### Lookup
|
||||||
Lookup is quite simple: starting at `A-0.data`, the sought for Key is searched in the B-tree there. If nothing is found, search continues to the next data file. So if there are *N* levels, then *N* disk-based B-tree lookups are performed. Each lookup is "guarded" by a bloom filter to improve the likelihood that disk-based searches are only done when likely to succeed.
|
Lookup is quite simple: starting at `A-0.data`, the sought for key is searched
|
||||||
|
in the B-tree there. Finding nothing, the search continues to the next data
|
||||||
|
file. So if there are *N* levels, then *N* disk-based B-tree lookups are
|
||||||
|
performed. Each lookup is "guarded" by a bloom filter to improve the likelihood
|
||||||
|
that disk-based searches are only done when likely to succeed.
|
||||||
|
|
||||||
### Insertion
|
### Insertion
|
||||||
Insertion works by a mechanism known as B-tree injection. Insertion always starts by constructing a fresh B-tree with 1 element in it, and "injecting" that B-tree into level #0. So you always inject a B-tree of the same size as the size of the level you're injecting it into.
|
Insertion works by a mechanism known as B-tree injection. Insertion always
|
||||||
|
starts by constructing a fresh B-tree with 1 element in it, and "injecting" that
|
||||||
|
B-tree into level #0. So you always inject a B-tree of the same size as the
|
||||||
|
size of the level you're injecting it into.
|
||||||
|
|
||||||
- If the level being injected into empty (there is no A-*level*.data file), then the injected B-tree becomes the contents for that level (we just rename the file).
|
- If the level being injected into empty (there is no A-*level*.data file), then the injected B-tree becomes the contents for that level (we just rename the file).
|
||||||
- Otherwise,
|
- Otherwise,
|
||||||
|
|
26
flake.lock
26
flake.lock
|
@ -22,13 +22,31 @@
|
||||||
"utils": "utils"
|
"utils": "utils"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"utils": {
|
"systems": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1623875721,
|
"lastModified": 1681028828,
|
||||||
"narHash": "sha256-A8BU7bjS5GirpAUv4QA+QnJ4CceLHkcXdRp4xITDB0s=",
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710146030,
|
||||||
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "f7e004a55b120c02ecb6219596820fcd32ca8772",
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
|
|
||||||
# Build time and Run time dependencies
|
# Build time and Run time dependencies
|
||||||
boost
|
boost
|
||||||
|
cryptopp
|
||||||
c-ares
|
c-ares
|
||||||
fmt
|
fmt
|
||||||
gnutls
|
gnutls
|
||||||
|
@ -57,6 +58,10 @@
|
||||||
icon = "f121";
|
icon = "f121";
|
||||||
in ''
|
in ''
|
||||||
export PS1="$(echo -e '\u${icon}') {\[$(tput sgr0)\]\[\033[38;5;228m\]\w\[$(tput sgr0)\]\[\033[38;5;15m\]} (${name}) \\$ \[$(tput sgr0)\]"
|
export PS1="$(echo -e '\u${icon}') {\[$(tput sgr0)\]\[\033[38;5;228m\]\w\[$(tput sgr0)\]\[\033[38;5;15m\]} (${name}) \\$ \[$(tput sgr0)\]"
|
||||||
|
export CMAKE_GENERATOR=Ninja
|
||||||
|
export CMAKE_MAKE_PROGRAM=Ninja
|
||||||
|
export CC=clang
|
||||||
|
export CXX=clang++
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
2
seastar
2
seastar
|
@ -1 +1 @@
|
||||||
Subproject commit a965080ec0bb895e5c6196d3b082fa8d8f49b512
|
Subproject commit 2b43417d210edbd7a3c3065bcfe3c0a9aea27f75
|
|
@ -1,3 +1,3 @@
|
||||||
add_executable(noidb)
|
add_executable(noidb)
|
||||||
target_sources(noidb PRIVATE noidb.cc)
|
target_sources(noidb PRIVATE noidb.cc Database.cc)
|
||||||
target_link_libraries(noidb PRIVATE Seastar::seastar)
|
target_link_libraries(noidb PRIVATE Seastar::seastar)
|
||||||
|
|
99
src/noidb.cc
99
src/noidb.cc
|
@ -1,36 +1,85 @@
|
||||||
|
|
||||||
|
#include "Database.hh"
|
||||||
|
|
||||||
|
#include <seastar/apps/lib/stop_signal.hh>
|
||||||
#include <seastar/core/app-template.hh>
|
#include <seastar/core/app-template.hh>
|
||||||
#include <seastar/core/coroutine.hh>
|
#include <seastar/core/reactor.hh>
|
||||||
#include <seastar/core/memory.hh>
|
#include <seastar/core/sleep.hh>
|
||||||
|
#include <seastar/core/thread.hh>
|
||||||
|
#include <seastar/http/file_handler.hh>
|
||||||
|
#include <seastar/http/function_handlers.hh>
|
||||||
|
#include <seastar/http/httpd.hh>
|
||||||
|
#include <seastar/http/request.hh>
|
||||||
|
#include <seastar/http/routes.hh>
|
||||||
#include <seastar/util/log.hh>
|
#include <seastar/util/log.hh>
|
||||||
|
|
||||||
// using namespace seastar;
|
#include <stdexcept>
|
||||||
|
|
||||||
seastar::logger lg("hanoidb");
|
using namespace seastar;
|
||||||
|
using namespace httpd;
|
||||||
|
|
||||||
static seastar::future<> hello_from_all_cores_serial() {
|
logger lg("noidb");
|
||||||
for (unsigned i = 0; i < seastar::smp::count; ++i) {
|
|
||||||
co_await seastar::smp::submit_to(
|
|
||||||
i, [] { lg.info("serial - Hello from every core"); });
|
|
||||||
};
|
|
||||||
co_return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static seastar::future<> hello_from_all_cores_parallel() {
|
namespace bpo = boost::program_options;
|
||||||
co_await seastar::smp::invoke_on_all([]() -> seastar::future<> {
|
|
||||||
auto memory = seastar::memory::get_memory_layout();
|
|
||||||
lg.info(
|
|
||||||
"parallel - memory layout start={} end={} size={}", memory.start, memory.end, memory.end - memory.start);
|
|
||||||
co_return;
|
|
||||||
});
|
|
||||||
co_return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
seastar::app_template app;
|
seastar::app_template app;
|
||||||
|
|
||||||
return app.run(argc, argv, [&]() -> seastar::future<int> {
|
// Options
|
||||||
co_await hello_from_all_cores_serial();
|
app.add_options()("address", bpo::value<seastar::sstring>()->default_value("0.0.0.0"), "HTTP Server address");
|
||||||
co_await hello_from_all_cores_parallel();
|
app.add_options()("port", bpo::value<uint16_t>()->default_value(8080), "HTTP Server port");
|
||||||
co_return 0;
|
app.add_options()("data", bpo::value<std::string>()->required(), "Data directory");
|
||||||
});
|
|
||||||
|
try {
|
||||||
|
return app.run(argc, argv, [&app] {
|
||||||
|
return seastar::async([&app] {
|
||||||
|
seastar_apps_lib::stop_signal stop_signal;
|
||||||
|
const auto& config = app.configuration();
|
||||||
|
|
||||||
|
// Start Server
|
||||||
|
seastar::net::inet_address addr(config["address"].as<seastar::sstring>());
|
||||||
|
uint16_t port = config["port"].as<uint16_t>();
|
||||||
|
|
||||||
|
seastar::httpd::http_server_control srv;
|
||||||
|
srv.start().get();
|
||||||
|
|
||||||
|
Database db(srv);
|
||||||
|
|
||||||
|
srv
|
||||||
|
.set_routes([](seastar::httpd::routes& r) {
|
||||||
|
r.add(
|
||||||
|
seastar::httpd::operation_type::GET,
|
||||||
|
seastar::httpd::url("/hello"),
|
||||||
|
new seastar::httpd::function_handler(
|
||||||
|
[]([[maybe_unused]] seastar::httpd::const_req req) { return "hi"; }));
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
|
||||||
|
srv
|
||||||
|
.set_routes([](seastar::httpd::routes& r) {
|
||||||
|
r.add(
|
||||||
|
seastar::httpd::operation_type::GET,
|
||||||
|
seastar::httpd::url("").remainder("path"),
|
||||||
|
new seastar::httpd::directory_handler("./public/"));
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
|
||||||
|
srv.listen(seastar::socket_address{addr, port}).get();
|
||||||
|
lg.info("NoiDB HTTP server listening on {}:{}\n", addr, port);
|
||||||
|
|
||||||
|
seastar::engine().at_exit([&srv, &db]() -> seastar::future<> {
|
||||||
|
lg.info("Stopping NoiDB HTTP server");
|
||||||
|
auto status = co_await db.stop();
|
||||||
|
if (status) lg.info("Stopped NoiDB Database");
|
||||||
|
co_await srv.stop();
|
||||||
|
co_return;
|
||||||
|
});
|
||||||
|
|
||||||
|
stop_signal.wait().get(); // block waiting for SIGINT or SIGTERM signal
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (...) {
|
||||||
|
lg.error("Failed to start NoiDB: {}\n", std::current_exception());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue