Call epoll_ctl in release stage
This commit is contained in:
@@ -122,19 +122,21 @@ void HttpHandler::on_batch_complete(std::span<Connection *const> batch) {
|
|||||||
state->parsing_commit && state->basic_validation_passed) {
|
state->parsing_commit && state->basic_validation_passed) {
|
||||||
*out_iter++ =
|
*out_iter++ =
|
||||||
CommitEntry{conn->get_weak_ref(), state->http_request_id,
|
CommitEntry{conn->get_weak_ref(), state->http_request_id,
|
||||||
state->connection_close, state->commit_request.get()};
|
state->connection_close, state->commit_request.get(),
|
||||||
|
std::move(state->arena)};
|
||||||
}
|
}
|
||||||
// Create StatusEntry for status requests
|
// Create StatusEntry for status requests
|
||||||
else if (state->route == HttpRoute::GetStatus) {
|
else if (state->route == HttpRoute::GetStatus) {
|
||||||
*out_iter++ =
|
*out_iter++ =
|
||||||
StatusEntry{conn->get_weak_ref(), state->http_request_id,
|
StatusEntry{conn->get_weak_ref(), state->http_request_id,
|
||||||
state->connection_close, state->status_request_id};
|
state->connection_close, state->status_request_id,
|
||||||
|
std::move(state->arena)};
|
||||||
}
|
}
|
||||||
// Create HealthCheckEntry for health check requests
|
// Create HealthCheckEntry for health check requests
|
||||||
else if (state->route == HttpRoute::GetOk) {
|
else if (state->route == HttpRoute::GetOk) {
|
||||||
*out_iter++ =
|
*out_iter++ =
|
||||||
HealthCheckEntry{conn->get_weak_ref(), state->http_request_id,
|
HealthCheckEntry{conn->get_weak_ref(), state->http_request_id,
|
||||||
state->connection_close};
|
state->connection_close, std::move(state->arena)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -488,6 +490,57 @@ void HttpHandler::send_error_response(MessageSender &conn, int status_code,
|
|||||||
http_request_id, close_connection);
|
http_request_id, close_connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::span<std::string_view>
|
||||||
|
HttpHandler::format_response(int status_code, std::string_view content_type,
|
||||||
|
std::string_view body, Arena &response_arena,
|
||||||
|
int64_t http_request_id, bool close_connection) {
|
||||||
|
// Status text
|
||||||
|
std::string_view status_text;
|
||||||
|
switch (status_code) {
|
||||||
|
case 200:
|
||||||
|
status_text = "OK";
|
||||||
|
break;
|
||||||
|
case 400:
|
||||||
|
status_text = "Bad Request";
|
||||||
|
break;
|
||||||
|
case 404:
|
||||||
|
status_text = "Not Found";
|
||||||
|
break;
|
||||||
|
case 500:
|
||||||
|
status_text = "Internal Server Error";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
status_text = "Unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *connection_header = close_connection ? "close" : "keep-alive";
|
||||||
|
|
||||||
|
auto response = std::span{response_arena.allocate<std::string_view>(1), 1};
|
||||||
|
|
||||||
|
response[0] =
|
||||||
|
format(response_arena,
|
||||||
|
"HTTP/1.1 %d %.*s\r\n"
|
||||||
|
"Content-Type: %.*s\r\n"
|
||||||
|
"Content-Length: %zu\r\n"
|
||||||
|
"X-Response-ID: %ld\r\n"
|
||||||
|
"Connection: %s\r\n"
|
||||||
|
"\r\n%.*s",
|
||||||
|
status_code, static_cast<int>(status_text.size()),
|
||||||
|
status_text.data(), static_cast<int>(content_type.size()),
|
||||||
|
content_type.data(), body.size(), http_request_id,
|
||||||
|
connection_header, static_cast<int>(body.size()), body.data());
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::span<std::string_view> HttpHandler::format_json_response(
|
||||||
|
int status_code, std::string_view json, Arena &response_arena,
|
||||||
|
int64_t http_request_id, bool close_connection) {
|
||||||
|
return format_response(status_code, "application/json", json, response_arena,
|
||||||
|
http_request_id, close_connection);
|
||||||
|
}
|
||||||
|
|
||||||
// llhttp callbacks
|
// llhttp callbacks
|
||||||
int HttpHandler::onUrl(llhttp_t *parser, const char *at, size_t length) {
|
int HttpHandler::onUrl(llhttp_t *parser, const char *at, size_t length) {
|
||||||
auto *state = static_cast<HttpConnectionState *>(parser->data);
|
auto *state = static_cast<HttpConnectionState *>(parser->data);
|
||||||
@@ -856,26 +909,26 @@ bool HttpHandler::process_persist_batch(BatchType &batch) {
|
|||||||
|
|
||||||
const CommitRequest &commit_request = *commit_entry.commit_request;
|
const CommitRequest &commit_request = *commit_entry.commit_request;
|
||||||
|
|
||||||
Arena response_arena;
|
// Generate success response with actual assigned version using
|
||||||
|
// request arena
|
||||||
std::string_view response;
|
std::string_view response;
|
||||||
|
|
||||||
// Generate success response with actual assigned version
|
|
||||||
if (commit_request.request_id().has_value()) {
|
if (commit_request.request_id().has_value()) {
|
||||||
response = format(
|
response = format(
|
||||||
response_arena,
|
commit_entry.request_arena,
|
||||||
R"({"request_id":"%.*s","status":"committed","version":%ld,"leader_id":"leader123"})",
|
R"({"request_id":"%.*s","status":"committed","version":%ld,"leader_id":"leader123"})",
|
||||||
static_cast<int>(commit_request.request_id().value().size()),
|
static_cast<int>(commit_request.request_id().value().size()),
|
||||||
commit_request.request_id().value().data(),
|
commit_request.request_id().value().data(),
|
||||||
commit_entry.assigned_version);
|
commit_entry.assigned_version);
|
||||||
} else {
|
} else {
|
||||||
response = format(
|
response = format(
|
||||||
response_arena,
|
commit_entry.request_arena,
|
||||||
R"({"status":"committed","version":%ld,"leader_id":"leader123"})",
|
R"({"status":"committed","version":%ld,"leader_id":"leader123"})",
|
||||||
commit_entry.assigned_version);
|
commit_entry.assigned_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
send_json_response(
|
// Format response but don't send yet - store for release stage
|
||||||
*conn_ref, 200, response, std::move(response_arena),
|
commit_entry.response_message = format_json_response(
|
||||||
|
200, response, commit_entry.request_arena,
|
||||||
commit_entry.http_request_id, commit_entry.connection_close);
|
commit_entry.http_request_id, commit_entry.connection_close);
|
||||||
|
|
||||||
return false; // Continue processing
|
return false; // Continue processing
|
||||||
@@ -898,8 +951,9 @@ bool HttpHandler::process_persist_batch(BatchType &batch) {
|
|||||||
"http", "persist_health_check",
|
"http", "persist_health_check",
|
||||||
perfetto::Flow::Global(health_check_entry.http_request_id));
|
perfetto::Flow::Global(health_check_entry.http_request_id));
|
||||||
|
|
||||||
// Generate OK response
|
// Format OK response but don't send yet - store for release stage
|
||||||
send_response(*conn_ref, 200, "text/plain", "OK", Arena{},
|
health_check_entry.response_message = format_response(
|
||||||
|
200, "text/plain", "OK", health_check_entry.request_arena,
|
||||||
health_check_entry.http_request_id,
|
health_check_entry.http_request_id,
|
||||||
health_check_entry.connection_close);
|
health_check_entry.connection_close);
|
||||||
|
|
||||||
@@ -944,6 +998,11 @@ bool HttpHandler::process_release_batch(BatchType &batch) {
|
|||||||
TRACE_EVENT("http", "release_commit",
|
TRACE_EVENT("http", "release_commit",
|
||||||
perfetto::Flow::Global(commit_entry.http_request_id));
|
perfetto::Flow::Global(commit_entry.http_request_id));
|
||||||
|
|
||||||
|
// Send the response that was formatted in persist stage
|
||||||
|
conn_ref->append_message(commit_entry.response_message,
|
||||||
|
std::move(commit_entry.request_arena),
|
||||||
|
commit_entry.connection_close);
|
||||||
|
|
||||||
return false; // Continue processing
|
return false; // Continue processing
|
||||||
} else if constexpr (std::is_same_v<T, StatusEntry>) {
|
} else if constexpr (std::is_same_v<T, StatusEntry>) {
|
||||||
// Process status entry: return connection to server
|
// Process status entry: return connection to server
|
||||||
@@ -975,6 +1034,12 @@ bool HttpHandler::process_release_batch(BatchType &batch) {
|
|||||||
"http", "release_health_check",
|
"http", "release_health_check",
|
||||||
perfetto::Flow::Global(health_check_entry.http_request_id));
|
perfetto::Flow::Global(health_check_entry.http_request_id));
|
||||||
|
|
||||||
|
// Send the response that was formatted in persist stage
|
||||||
|
conn_ref->append_message(
|
||||||
|
health_check_entry.response_message,
|
||||||
|
std::move(health_check_entry.request_arena),
|
||||||
|
health_check_entry.connection_close);
|
||||||
|
|
||||||
return false; // Continue processing
|
return false; // Continue processing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ private:
|
|||||||
// (string_views into arena)
|
// (string_views into arena)
|
||||||
|
|
||||||
// Main commit processing pipeline: sequence -> resolve -> persist -> release
|
// Main commit processing pipeline: sequence -> resolve -> persist -> release
|
||||||
StaticThreadPipeline<PipelineEntry, WaitStrategy::WaitIfUpstreamIdle, 1, 1, 1,
|
StaticThreadPipeline<PipelineEntry, WaitStrategy::WaitIfStageEmpty, 1, 1, 1,
|
||||||
1>
|
1>
|
||||||
commitPipeline{lg_size};
|
commitPipeline{lg_size};
|
||||||
|
|
||||||
@@ -181,8 +181,8 @@ private:
|
|||||||
|
|
||||||
// Pipeline stage processing methods (batch-based)
|
// Pipeline stage processing methods (batch-based)
|
||||||
using BatchType =
|
using BatchType =
|
||||||
StaticThreadPipeline<PipelineEntry, WaitStrategy::WaitIfUpstreamIdle, 1,
|
StaticThreadPipeline<PipelineEntry, WaitStrategy::WaitIfStageEmpty, 1, 1,
|
||||||
1, 1, 1>::Batch;
|
1, 1>::Batch;
|
||||||
bool process_sequence_batch(BatchType &batch);
|
bool process_sequence_batch(BatchType &batch);
|
||||||
bool process_resolve_batch(BatchType &batch);
|
bool process_resolve_batch(BatchType &batch);
|
||||||
bool process_persist_batch(BatchType &batch);
|
bool process_persist_batch(BatchType &batch);
|
||||||
@@ -225,4 +225,14 @@ private:
|
|||||||
std::string_view message,
|
std::string_view message,
|
||||||
Arena response_arena, int64_t http_request_id,
|
Arena response_arena, int64_t http_request_id,
|
||||||
bool close_connection);
|
bool close_connection);
|
||||||
|
|
||||||
|
// Helper functions for formatting responses without sending
|
||||||
|
static std::span<std::string_view>
|
||||||
|
format_response(int status_code, std::string_view content_type,
|
||||||
|
std::string_view body, Arena &response_arena,
|
||||||
|
int64_t http_request_id, bool close_connection);
|
||||||
|
static std::span<std::string_view>
|
||||||
|
format_json_response(int status_code, std::string_view json,
|
||||||
|
Arena &response_arena, int64_t http_request_id,
|
||||||
|
bool close_connection);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "arena.hpp"
|
||||||
#include "connection.hpp"
|
#include "connection.hpp"
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
@@ -19,14 +20,21 @@ struct CommitEntry {
|
|||||||
// Copied HTTP state (pipeline threads cannot access connection user_data)
|
// Copied HTTP state (pipeline threads cannot access connection user_data)
|
||||||
int64_t http_request_id = 0;
|
int64_t http_request_id = 0;
|
||||||
bool connection_close = false;
|
bool connection_close = false;
|
||||||
const CommitRequest *commit_request =
|
const CommitRequest *commit_request = nullptr; // Points to request_arena data
|
||||||
nullptr; // Points to connection's arena data
|
|
||||||
|
// Request arena contains parsed request data and formatted response
|
||||||
|
Arena request_arena;
|
||||||
|
|
||||||
|
// Response data (set by persist stage, consumed by release stage)
|
||||||
|
// Points to response formatted in request_arena
|
||||||
|
std::span<std::string_view> response_message;
|
||||||
|
|
||||||
CommitEntry() = default; // Default constructor for variant
|
CommitEntry() = default; // Default constructor for variant
|
||||||
explicit CommitEntry(WeakRef<MessageSender> conn, int64_t req_id,
|
explicit CommitEntry(WeakRef<MessageSender> conn, int64_t req_id,
|
||||||
bool close_conn, const CommitRequest *req)
|
bool close_conn, const CommitRequest *req, Arena arena)
|
||||||
: connection(std::move(conn)), http_request_id(req_id),
|
: connection(std::move(conn)), http_request_id(req_id),
|
||||||
connection_close(close_conn), commit_request(req) {}
|
connection_close(close_conn), commit_request(req),
|
||||||
|
request_arena(std::move(arena)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,13 +48,18 @@ struct StatusEntry {
|
|||||||
// Copied HTTP state
|
// Copied HTTP state
|
||||||
int64_t http_request_id = 0;
|
int64_t http_request_id = 0;
|
||||||
bool connection_close = false;
|
bool connection_close = false;
|
||||||
std::string_view status_request_id; // Points to connection's arena data
|
std::string_view status_request_id; // Points to request_arena data
|
||||||
|
|
||||||
|
// Request arena for HTTP request data
|
||||||
|
Arena request_arena;
|
||||||
|
|
||||||
StatusEntry() = default; // Default constructor for variant
|
StatusEntry() = default; // Default constructor for variant
|
||||||
explicit StatusEntry(WeakRef<MessageSender> conn, int64_t req_id,
|
explicit StatusEntry(WeakRef<MessageSender> conn, int64_t req_id,
|
||||||
bool close_conn, std::string_view request_id)
|
bool close_conn, std::string_view request_id,
|
||||||
|
Arena arena)
|
||||||
: connection(std::move(conn)), http_request_id(req_id),
|
: connection(std::move(conn)), http_request_id(req_id),
|
||||||
connection_close(close_conn), status_request_id(request_id) {}
|
connection_close(close_conn), status_request_id(request_id),
|
||||||
|
request_arena(std::move(arena)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,11 +74,18 @@ struct HealthCheckEntry {
|
|||||||
int64_t http_request_id = 0;
|
int64_t http_request_id = 0;
|
||||||
bool connection_close = false;
|
bool connection_close = false;
|
||||||
|
|
||||||
|
// Request arena for formatting response
|
||||||
|
Arena request_arena;
|
||||||
|
|
||||||
|
// Response data (set by persist stage, consumed by release stage)
|
||||||
|
// Points to response formatted in request_arena
|
||||||
|
std::span<std::string_view> response_message;
|
||||||
|
|
||||||
HealthCheckEntry() = default; // Default constructor for variant
|
HealthCheckEntry() = default; // Default constructor for variant
|
||||||
explicit HealthCheckEntry(WeakRef<MessageSender> conn, int64_t req_id,
|
explicit HealthCheckEntry(WeakRef<MessageSender> conn, int64_t req_id,
|
||||||
bool close_conn)
|
bool close_conn, Arena arena)
|
||||||
: connection(std::move(conn)), http_request_id(req_id),
|
: connection(std::move(conn)), http_request_id(req_id),
|
||||||
connection_close(close_conn) {}
|
connection_close(close_conn), request_arena(std::move(arena)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user