Basic implementation of /commit, /version, and /status
No precondition checking, persistence, or log scanning yet.
This commit is contained in:
@@ -1,15 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <llhttp.h>
|
||||
|
||||
#include "arena_allocator.hpp"
|
||||
#include "connection.hpp"
|
||||
#include "connection_handler.hpp"
|
||||
#include "loop_iterations.hpp"
|
||||
#include "perfetto_categories.hpp"
|
||||
#include "pipeline_entry.hpp"
|
||||
#include "server.hpp"
|
||||
#include "thread_pipeline.hpp"
|
||||
|
||||
@@ -52,13 +56,18 @@ struct HttpConnectionState {
|
||||
bool connection_close = false; // Client requested connection close
|
||||
HttpRoute route = HttpRoute::NotFound;
|
||||
|
||||
// Status request data
|
||||
std::string_view
|
||||
status_request_id; // Request ID extracted from /v1/status/{id} URL
|
||||
|
||||
// Header accumulation buffers (arena-allocated)
|
||||
using ArenaString =
|
||||
std::basic_string<char, std::char_traits<char>, ArenaStlAllocator<char>>;
|
||||
ArenaString current_header_field_buf;
|
||||
ArenaString current_header_value_buf;
|
||||
bool header_field_complete = false;
|
||||
int64_t request_id = 0; // X-Request-Id header value
|
||||
int64_t http_request_id =
|
||||
0; // X-Request-Id header value (for tracing/logging)
|
||||
|
||||
// Streaming parser for POST requests
|
||||
std::unique_ptr<JsonCommitRequestParser> commit_parser;
|
||||
@@ -75,34 +84,47 @@ struct HttpConnectionState {
|
||||
* Supports the WeaselDB REST API endpoints with enum-based routing.
|
||||
*/
|
||||
struct HttpHandler : ConnectionHandler {
|
||||
HttpHandler() {
|
||||
// Stage 0: Version assignment and precondition validation thread
|
||||
validationThread = std::thread{[this]() {
|
||||
pthread_setname_np(pthread_self(), "txn-validate");
|
||||
HttpHandler()
|
||||
: banned_request_ids(
|
||||
ArenaStlAllocator<std::string_view>(&banned_request_arena)) {
|
||||
// Stage 0: Sequence assignment thread
|
||||
sequenceThread = std::thread{[this]() {
|
||||
pthread_setname_np(pthread_self(), "txn-sequence");
|
||||
for (;;) {
|
||||
auto guard = commitPipeline.acquire<0, 0>();
|
||||
if (process_validation_batch(guard.batch)) {
|
||||
if (process_sequence_batch(guard.batch)) {
|
||||
return; // Shutdown signal received
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
// Stage 1: Transaction persistence and subscriber streaming thread
|
||||
persistenceThread = std::thread{[this]() {
|
||||
pthread_setname_np(pthread_self(), "txn-persist");
|
||||
// Stage 1: Precondition resolution thread
|
||||
resolveThread = std::thread{[this]() {
|
||||
pthread_setname_np(pthread_self(), "txn-resolve");
|
||||
for (;;) {
|
||||
auto guard = commitPipeline.acquire<1, 0>();
|
||||
if (process_persistence_batch(guard.batch)) {
|
||||
if (process_resolve_batch(guard.batch)) {
|
||||
return; // Shutdown signal received
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
// Stage 2: Connection return to server thread
|
||||
// Stage 2: Transaction persistence thread
|
||||
persistThread = std::thread{[this]() {
|
||||
pthread_setname_np(pthread_self(), "txn-persist");
|
||||
for (;;) {
|
||||
auto guard = commitPipeline.acquire<2, 0>();
|
||||
if (process_persist_batch(guard.batch)) {
|
||||
return; // Shutdown signal received
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
// Stage 3: Connection return to server thread
|
||||
releaseThread = std::thread{[this]() {
|
||||
pthread_setname_np(pthread_self(), "txn-release");
|
||||
for (;;) {
|
||||
auto guard = commitPipeline.acquire<2, 0>();
|
||||
auto guard = commitPipeline.acquire<3, 0>();
|
||||
if (process_release_batch(guard.batch)) {
|
||||
return; // Shutdown signal received
|
||||
}
|
||||
@@ -110,17 +132,17 @@ struct HttpHandler : ConnectionHandler {
|
||||
}};
|
||||
}
|
||||
~HttpHandler() {
|
||||
// Send shutdown signals to all pipeline stages
|
||||
// Send single shutdown signal that flows through all pipeline stages
|
||||
{
|
||||
auto guard = commitPipeline.push(3, true);
|
||||
for (auto &c : guard.batch) {
|
||||
c = {}; // null connection signals shutdown
|
||||
}
|
||||
auto guard = commitPipeline.push(1, true);
|
||||
guard.batch[0] =
|
||||
ShutdownEntry{}; // Single ShutdownEntry flows through all stages
|
||||
}
|
||||
|
||||
// Join all pipeline threads
|
||||
validationThread.join();
|
||||
persistenceThread.join();
|
||||
sequenceThread.join();
|
||||
resolveThread.join();
|
||||
persistThread.join();
|
||||
releaseThread.join();
|
||||
}
|
||||
|
||||
@@ -148,29 +170,48 @@ struct HttpHandler : ConnectionHandler {
|
||||
private:
|
||||
static constexpr int lg_size = 16;
|
||||
|
||||
// Main commit processing pipeline: validation -> persistence -> release
|
||||
StaticThreadPipeline<std::unique_ptr<Connection>,
|
||||
WaitStrategy::WaitIfUpstreamIdle, 1, 1, 1>
|
||||
// Pipeline state (sequence thread only)
|
||||
int64_t next_version = 1; // Next version to assign (sequence thread only)
|
||||
|
||||
// Pipeline state (persist thread writes, I/O threads read)
|
||||
std::atomic<int64_t> committed_version{
|
||||
0}; // Highest committed version (persist thread writes, I/O threads read)
|
||||
|
||||
// Arena for banned request IDs and related data structures (sequence thread
|
||||
// only)
|
||||
ArenaAllocator banned_request_arena;
|
||||
using BannedRequestIdSet =
|
||||
std::unordered_set<std::string_view, std::hash<std::string_view>,
|
||||
std::equal_to<std::string_view>,
|
||||
ArenaStlAllocator<std::string_view>>;
|
||||
BannedRequestIdSet banned_request_ids; // Request IDs that should not commit
|
||||
// (string_views into arena)
|
||||
|
||||
// Main commit processing pipeline: sequence -> resolve -> persist -> release
|
||||
StaticThreadPipeline<PipelineEntry, WaitStrategy::WaitIfUpstreamIdle, 1, 1, 1,
|
||||
1>
|
||||
commitPipeline{lg_size};
|
||||
|
||||
// Pipeline stage threads
|
||||
std::thread validationThread;
|
||||
std::thread persistenceThread;
|
||||
std::thread sequenceThread;
|
||||
std::thread resolveThread;
|
||||
std::thread persistThread;
|
||||
std::thread releaseThread;
|
||||
|
||||
// Pipeline stage processing methods (batch-based)
|
||||
using BatchType =
|
||||
StaticThreadPipeline<std::unique_ptr<Connection>,
|
||||
WaitStrategy::WaitIfUpstreamIdle, 1, 1, 1>::Batch;
|
||||
bool process_validation_batch(BatchType &batch);
|
||||
bool process_persistence_batch(BatchType &batch);
|
||||
StaticThreadPipeline<PipelineEntry, WaitStrategy::WaitIfUpstreamIdle, 1,
|
||||
1, 1, 1>::Batch;
|
||||
bool process_sequence_batch(BatchType &batch);
|
||||
bool process_resolve_batch(BatchType &batch);
|
||||
bool process_persist_batch(BatchType &batch);
|
||||
bool process_release_batch(BatchType &batch);
|
||||
|
||||
// Route handlers
|
||||
void handleGetVersion(Connection &conn, const HttpConnectionState &state);
|
||||
void handlePostCommit(Connection &conn, const HttpConnectionState &state);
|
||||
void handleGetSubscribe(Connection &conn, const HttpConnectionState &state);
|
||||
void handleGetStatus(Connection &conn, const HttpConnectionState &state);
|
||||
void handleGetStatus(Connection &conn, HttpConnectionState &state);
|
||||
void handlePutRetention(Connection &conn, const HttpConnectionState &state);
|
||||
void handleGetRetention(Connection &conn, const HttpConnectionState &state);
|
||||
void handleDeleteRetention(Connection &conn,
|
||||
|
||||
Reference in New Issue
Block a user