diff options
| -rw-r--r-- | CHANGELOG.md | 12 | ||||
| -rw-r--r-- | VERSION.txt | 2 | ||||
| -rw-r--r-- | src/zenhttp/clients/asynchttpclient.cpp | 36 | ||||
| -rw-r--r-- | src/zenhttp/include/zenhttp/asynchttpclient.h | 6 | ||||
| -rw-r--r-- | src/zenserver/storage/objectstore/objectstore.cpp | 35 | ||||
| -rw-r--r-- | src/zenserver/storage/storageconfig.cpp | 2 |
6 files changed, 54 insertions, 39 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a88753bc..39972aa8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## - Feature: Hub bulk deprovision endpoint (`POST /hub/deprovision`) tears down all provisioned and hibernated modules in a single request +- Feature: Hub instance forwarding options for memory allocator and tracing + - `--hub-instance-malloc` selects the memory allocator for child instances + - `--hub-instance-trace` sets trace channels for child instances + - `--hub-instance-tracehost` sets the trace streaming host for child instances + - `--hub-instance-tracefile` sets the trace output file for child instances; supports `{moduleid}` and `{port}` placeholders - Improvement: Replaced archived `http_parser` library with `llhttp` for HTTP request parsing - Improvement: HTTP range responses (RFC 7233) are now fully compliant across the object store and build store - 206 Partial Content responses now include a `Content-Range` header; previously absent for single-range requests, which broke `HttpClient::GetRanges()` @@ -17,13 +22,8 @@ - Improvement: `--consul-register-hub` option to disable hub parent service Consul registration while keeping instance registration active - Bugfix: Added logic to shared memory instance state management to ensure unclean shutdown followed by restart with identical pid doesn't lead to errors. Particularly likely to happen when running on k8s - Improvement: Dashboard service stats panes are now always visible on service pages (projects, cache, builds, workspaces) rather than hidden until data arrives -- Feature: Hub instance forwarding options for memory allocator and tracing - - `--hub-instance-malloc` selects the memory allocator for child instances - - `--hub-instance-trace` sets trace channels for child instances - - `--hub-instance-tracehost` sets the trace streaming host for child instances - - `--hub-instance-tracefile` sets the trace output file for child instances; supports `{moduleid}` and `{port}` placeholders -- Bugfix: Dashboard stats tiles no longer flicker on page load when WebSocket updates arrive before all stats are available - Improvement: Updated vendored MinIO test server from RELEASE.2025-07-23 to RELEASE.2025-09-07 (HTTP listener bugfixes, ListObject fix) +- Bugfix: Dashboard stats tiles no longer flicker on page load when WebSocket updates arrive before all stats are available ## 5.8.3 - Feature: Incremental CAS-based hydration/dehydration replacing the previous full-copy approach diff --git a/VERSION.txt b/VERSION.txt index f05e61d96..b61055bc9 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.8.3
\ No newline at end of file +5.8.4-pre0
\ No newline at end of file diff --git a/src/zenhttp/clients/asynchttpclient.cpp b/src/zenhttp/clients/asynchttpclient.cpp index bdf7f160c..4b189af36 100644 --- a/src/zenhttp/clients/asynchttpclient.cpp +++ b/src/zenhttp/clients/asynchttpclient.cpp @@ -100,7 +100,7 @@ struct AsyncHttpClient::Impl { // Clean up curl state on the strand where all curl_multi operations // are serialized. Use a promise to block until the cleanup handler - // has actually executed — essential for the external io_context case + // has actually executed - essential for the external io_context case // where we don't own the run loop. std::promise<void> Done; std::future<void> DoneFuture = Done.get_future(); @@ -109,7 +109,7 @@ struct AsyncHttpClient::Impl m_ShuttingDown = true; m_Timer.cancel(); - // Release all tracked sockets (don't close — curl owns the fds). + // Release all tracked sockets (don't close - curl owns the fds). for (auto& [Fd, Info] : m_Sockets) { if (Info->Socket.is_open()) @@ -178,7 +178,7 @@ struct AsyncHttpClient::Impl } } - // ── Handle pool ───────────────────────────────────────────────────── + // -- Handle pool ----------------------------------------------------- CURL* AllocHandle() { @@ -199,7 +199,7 @@ struct AsyncHttpClient::Impl void ReleaseHandle(CURL* Handle) { m_HandlePool.push_back(Handle); } - // ── Configure a handle with common settings ───────────────────────── + // -- Configure a handle with common settings ------------------------- // Called only from DoAsync* lambdas running on the strand. void ConfigureHandle(CURL* Handle, std::string_view ResourcePath, const HttpClient::KeyValueMap& Parameters) @@ -258,7 +258,7 @@ struct AsyncHttpClient::Impl } } - // ── Access token ──────────────────────────────────────────────────── + // -- Access token ---------------------------------------------------- std::optional<std::string> GetAccessToken() { @@ -293,7 +293,7 @@ struct AsyncHttpClient::Impl return {}; } - // ── Submit a transfer ─────────────────────────────────────────────── + // -- Submit a transfer ----------------------------------------------- void SubmitTransfer(CURL* Handle, std::unique_ptr<TransferContext> Ctx) { @@ -323,7 +323,7 @@ struct AsyncHttpClient::Impl } } - // ── Socket-action integration ─────────────────────────────────────── + // -- Socket-action integration --------------------------------------- // // curl_multi drives I/O via two callbacks: // - SocketCallback: curl tells us which sockets to watch for read/write @@ -341,7 +341,7 @@ struct AsyncHttpClient::Impl explicit SocketInfo(asio::io_context& IoContext) : Socket(IoContext) {} }; - // Static thunks registered with curl_multi ──────────────────────────── + // Static thunks registered with curl_multi ---------------------------- static int CurlSocketCallback(CURL* Easy, curl_socket_t Fd, int Action, void* UserPtr, void* SocketPtr) { @@ -367,7 +367,7 @@ struct AsyncHttpClient::Impl curl_multi_setopt(m_Multi, CURLMOPT_TIMERDATA, this); } - // Called by curl when socket watch state changes ────────────────────── + // Called by curl when socket watch state changes --------------------- void OnCurlSocket(curl_socket_t Fd, int Action, SocketInfo* Info) { @@ -389,7 +389,7 @@ struct AsyncHttpClient::Impl if (!Info) { - // New socket — wrap the native fd in an ASIO socket. + // New socket - wrap the native fd in an ASIO socket. auto [It, Inserted] = m_Sockets.emplace(Fd, std::make_unique<SocketInfo>(m_IoContext)); Info = It->second.get(); @@ -458,7 +458,7 @@ struct AsyncHttpClient::Impl } } - // Called by curl when it wants a timeout ────────────────────────────── + // Called by curl when it wants a timeout ------------------------------ void OnCurlTimer(long TimeoutMs) { @@ -472,7 +472,7 @@ struct AsyncHttpClient::Impl if (TimeoutMs == 0) { - // curl wants immediate action — run it directly on the strand. + // curl wants immediate action - run it directly on the strand. asio::post(m_Strand, [this]() { if (m_ShuttingDown) { @@ -498,7 +498,7 @@ struct AsyncHttpClient::Impl })); } - // Drain completed transfers from curl_multi ────────────────────────── + // Drain completed transfers from curl_multi -------------------------- void CheckCompleted() { @@ -605,7 +605,7 @@ struct AsyncHttpClient::Impl }); } - // ── Async verb implementations ────────────────────────────────────── + // -- Async verb implementations -------------------------------------- void DoAsyncGet(std::string Url, AsyncHttpCallback Callback, @@ -816,7 +816,7 @@ struct AsyncHttpClient::Impl }); } - // ── Members ───────────────────────────────────────────────────────── + // -- Members --------------------------------------------------------- std::string m_BaseUri; HttpClientSettings m_Settings; @@ -824,7 +824,7 @@ struct AsyncHttpClient::Impl std::string m_SessionId; std::string m_UnixSocketPathUtf8; - // io_context and strand — all curl_multi operations are serialized on the + // io_context and strand - all curl_multi operations are serialized on the // strand, making this safe even when the io_context has multiple threads. std::unique_ptr<asio::io_context> m_OwnedIoContext; asio::io_context& m_IoContext; @@ -861,7 +861,7 @@ AsyncHttpClient::AsyncHttpClient(std::string_view BaseUri, asio::io_context& IoC AsyncHttpClient::~AsyncHttpClient() = default; -// ── Callback-based API ────────────────────────────────────────────────── +// -- Callback-based API -------------------------------------------------- void AsyncHttpClient::AsyncGet(std::string_view Url, @@ -925,7 +925,7 @@ AsyncHttpClient::AsyncPut(std::string_view Url, AsyncHttpCallback Callback, cons m_Impl->DoAsyncPutNoPayload(std::string(Url), std::move(Callback), Parameters); } -// ── Future-based API ──────────────────────────────────────────────────── +// -- Future-based API ---------------------------------------------------- std::future<HttpClient::Response> AsyncHttpClient::Get(std::string_view Url, const KeyValueMap& AdditionalHeader, const KeyValueMap& Parameters) diff --git a/src/zenhttp/include/zenhttp/asynchttpclient.h b/src/zenhttp/include/zenhttp/asynchttpclient.h index 58429349d..cb41626b9 100644 --- a/src/zenhttp/include/zenhttp/asynchttpclient.h +++ b/src/zenhttp/include/zenhttp/asynchttpclient.h @@ -21,7 +21,7 @@ using AsyncHttpCallback = std::function<void(HttpClient::Response)>; /** Asynchronous HTTP client backed by curl_multi and ASIO. * - * Uses curl_multi_perform() driven by an ASIO steady_timer to process + * Uses curl_multi_socket_action() driven by ASIO socket async_wait to process * transfers without blocking the caller. All curl_multi operations are * serialized on an internal strand; callers may issue requests from any * thread, and the io_context may have multiple threads. @@ -53,7 +53,7 @@ public: AsyncHttpClient(const AsyncHttpClient&) = delete; AsyncHttpClient& operator=(const AsyncHttpClient&) = delete; - // ── Callback-based API ────────────────────────────────────────────── + // -- Callback-based API ---------------------------------------------- void AsyncGet(std::string_view Url, AsyncHttpCallback Callback, @@ -85,7 +85,7 @@ public: void AsyncPut(std::string_view Url, AsyncHttpCallback Callback, const KeyValueMap& Parameters = {}); - // ── Future-based API ──────────────────────────────────────────────── + // -- Future-based API ------------------------------------------------ [[nodiscard]] std::future<Response> Get(std::string_view Url, const KeyValueMap& AdditionalHeader = {}, diff --git a/src/zenserver/storage/objectstore/objectstore.cpp b/src/zenserver/storage/objectstore/objectstore.cpp index bab9df06d..1115c1cd6 100644 --- a/src/zenserver/storage/objectstore/objectstore.cpp +++ b/src/zenserver/storage/objectstore/objectstore.cpp @@ -669,24 +669,39 @@ HttpObjectStoreService::GetObject(HttpRouterRequest& Request, const std::string_ NiceBytes(FileBuf.GetSize()), NiceBytes(TotalServed)); } - else if (Ranges.size() == 1) + else { - const uint64_t TotalSize = FileBuf.GetSize(); - const uint64_t RangeEnd = (Ranges[0].End != ~uint64_t(0)) ? Ranges[0].End : TotalSize - 1; - if (RangeEnd < TotalSize && Ranges[0].Start <= RangeEnd) + const uint64_t TotalSize = FileBuf.GetSize(); + uint64_t ServedBytes = 0; + for (const HttpRange& Range : Ranges) { - const uint64_t RangeSize = 1 + (RangeEnd - Ranges[0].Start); - const uint64_t TotalServed = m_TotalBytesServed.fetch_add(RangeSize) + RangeSize; + const uint64_t RangeEnd = (Range.End != ~uint64_t(0)) ? Range.End : TotalSize - 1; + if (RangeEnd < TotalSize && Range.Start <= RangeEnd) + { + ServedBytes += 1 + (RangeEnd - Range.Start); + } + } + if (ServedBytes > 0) + { + const uint64_t TotalServed = m_TotalBytesServed.fetch_add(ServedBytes) + ServedBytes; ZEN_LOG_DEBUG(LogObj, - "GET - '{}/{}' (Range: {}-{}) ({}/{}) [OK] (Served: {})", + "GET - '{}/{}' (Ranges: {}) ({}/{}) [OK] (Served: {})", BucketName, RelativeBucketPath, - Ranges[0].Start, - RangeEnd, - NiceBytes(RangeSize), + Ranges.size(), + NiceBytes(ServedBytes), NiceBytes(TotalSize), NiceBytes(TotalServed)); } + else + { + ZEN_LOG_DEBUG(LogObj, + "GET - '{}/{}' (Ranges: {}) [416] ({})", + BucketName, + RelativeBucketPath, + Ranges.size(), + NiceBytes(TotalSize)); + } } Request.ServerRequest().WriteResponse(HttpContentType::kBinary, FileBuf, Ranges); } diff --git a/src/zenserver/storage/storageconfig.cpp b/src/zenserver/storage/storageconfig.cpp index 0dbb45164..b615af280 100644 --- a/src/zenserver/storage/storageconfig.cpp +++ b/src/zenserver/storage/storageconfig.cpp @@ -477,7 +477,7 @@ ZenStorageServerConfigurator::AddConfigOptions(LuaConfig::Options& LuaOptions) ServerOptions.GcConfig.CompactBlockUsageThresholdPercent, "gc-compactblock-threshold"sv); LuaOptions.AddOption("gc.verbose"sv, ServerOptions.GcConfig.Verbose, "gc-verbose"sv); - LuaOptions.AddOption("gc.single-threaded"sv, ServerOptions.GcConfig.SingleThreaded, "gc-single-threaded"sv); + LuaOptions.AddOption("gc.singlethreaded"sv, ServerOptions.GcConfig.SingleThreaded, "gc-single-threaded"sv); LuaOptions.AddOption("gc.cache.attachment.store"sv, ServerOptions.GcConfig.StoreCacheAttachmentMetaData, "gc-cache-attachment-store"); LuaOptions.AddOption("gc.projectstore.attachment.store"sv, ServerOptions.GcConfig.StoreProjectAttachmentMetaData, |