Skip to content

Commit 7e33763

Browse files
authored
Merge pull request #4926 from eisenhauer/get-query-routing
Various HTTP/XROOTD changes
2 parents 4673f16 + a5cbbbb commit 7e33763

5 files changed

Lines changed: 408 additions & 187 deletions

File tree

source/adios2/engine/campaign/CampaignReader.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ void CampaignReader::PerformGets()
121121
return true;
122122
};
123123

124-
size_t nThreads = std::min(nEngines, (size_t)16);
124+
size_t nThreads = std::min(nEngines, (size_t)64);
125125
std::vector<std::future<bool>> futures(nThreads - 1);
126126

127127
// launch Threads-1 threads to process subsets of handles,

source/adios2/toolkit/remote/XrootdHttpRemote.cpp

Lines changed: 167 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,22 @@
77
#include "XrootdHttpRemote.h"
88
#include "adios2/helper/adiosLog.h"
99

10+
#include <chrono>
11+
#include <cstdlib>
1012
#include <cstring>
1113
#include <sstream>
1214
#include <stdexcept>
15+
#include <thread>
16+
17+
#ifdef _WIN32
18+
#include <direct.h>
19+
#include <stdlib.h>
20+
#define getcwd _getcwd
21+
#define PATH_MAX _MAX_PATH
22+
#else
23+
#include <climits>
24+
#include <unistd.h>
25+
#endif
1326

1427
#ifdef ADIOS2_HAVE_CURL
1528
#include <curl/curl.h>
@@ -219,9 +232,18 @@ void CurlMultiPool::WorkerLoop()
219232
continue;
220233
}
221234

222-
// Wait for socket activity (up to 100ms)
235+
// Skip waiting if new requests are queued -- submit them immediately
236+
{
237+
std::lock_guard<std::mutex> lock(m_QueueMutex);
238+
if (!m_PendingQueue.empty())
239+
{
240+
continue;
241+
}
242+
}
243+
244+
// Wait for socket activity (up to 10ms)
223245
int numfds;
224-
CURLMcode mc = curl_multi_wait(m_MultiHandle, nullptr, 0, 100, &numfds);
246+
CURLMcode mc = curl_multi_wait(m_MultiHandle, nullptr, 0, 10, &numfds);
225247
if (mc != CURLM_OK)
226248
{
227249
helper::Log("Remote", "CurlMultiPool", "WorkerLoop",
@@ -269,7 +291,23 @@ void XrootdHttpRemote::Open(const std::string hostname, const int32_t port,
269291
const std::string filename, const Mode mode, bool RowMajorOrdering,
270292
const Params &params)
271293
{
272-
m_Filename = filename;
294+
// Ensure filename is absolute so the URL path is clean (no double-slash ambiguity)
295+
if (!filename.empty() && filename[0] != '/')
296+
{
297+
char cwd[PATH_MAX];
298+
if (getcwd(cwd, sizeof(cwd)))
299+
{
300+
m_Filename = std::string(cwd) + "/" + filename;
301+
}
302+
else
303+
{
304+
m_Filename = filename;
305+
}
306+
}
307+
else
308+
{
309+
m_Filename = filename;
310+
}
273311
m_Mode = mode;
274312
m_RowMajorOrdering = RowMajorOrdering;
275313

@@ -310,7 +348,8 @@ void XrootdHttpRemote::Open(const std::string hostname, const int32_t port,
310348
}
311349

312350
std::ostringstream urlStream;
313-
urlStream << (m_UseHttps ? "https" : "http") << "://" << hostname << ":" << port << "/ssi";
351+
urlStream << (m_UseHttps ? "https" : "http") << "://" << hostname << ":" << port << "/adios/"
352+
<< UrlEncode(m_Filename);
314353
m_BaseUrl = urlStream.str();
315354

316355
m_OpenSuccess = true;
@@ -323,26 +362,47 @@ std::string XrootdHttpRemote::BuildRequestString(const char *VarName, size_t Ste
323362
const Dims &Start, const Accuracy &accuracy)
324363
{
325364
std::ostringstream reqStream;
326-
std::string encodedFilename = UrlEncode(m_Filename);
327365
std::string encodedVarName = UrlEncode(std::string(VarName));
328366

329-
reqStream << "get Filename=" << encodedFilename;
330-
reqStream << "&RMOrder=" << (m_RowMajorOrdering ? 1 : 0);
331-
reqStream << "&Varname=" << encodedVarName;
332-
reqStream << "&StepStart=" << Step;
333-
reqStream << "&StepCount=" << StepCount;
334-
reqStream << "&Block=" << BlockID;
335-
reqStream << "&Dims=" << Count.size();
336-
337-
for (const auto &c : Count)
338-
reqStream << "&Count=" << c;
339-
for (const auto &s : Start)
340-
reqStream << "&Start=" << s;
341-
342-
reqStream << "&AccuracyError=" << accuracy.error;
343-
reqStream << "&AccuracyNorm=" << accuracy.norm;
344-
reqStream << "&AccuracyRelative=" << (accuracy.relative ? 1 : 0);
345-
367+
reqStream << "get&Varname=" << encodedVarName;
368+
if (m_RowMajorOrdering)
369+
{
370+
reqStream << "&RMOrder=1";
371+
}
372+
if (Step != 0)
373+
{
374+
reqStream << "&StepStart=" << Step;
375+
}
376+
if (StepCount != 1)
377+
{
378+
reqStream << "&StepCount=" << StepCount;
379+
}
380+
if (BlockID != static_cast<size_t>(-1))
381+
{
382+
reqStream << "&Block=" << BlockID;
383+
}
384+
if (!Count.empty())
385+
{
386+
reqStream << "&Count=" << Count[0];
387+
for (size_t i = 1; i < Count.size(); i++)
388+
{
389+
reqStream << "," << Count[i];
390+
}
391+
}
392+
if (!Start.empty())
393+
{
394+
reqStream << "&Start=" << Start[0];
395+
for (size_t i = 1; i < Start.size(); i++)
396+
{
397+
reqStream << "," << Start[i];
398+
}
399+
}
400+
if (accuracy.error != 0.0 || accuracy.norm != 0.0 || accuracy.relative)
401+
{
402+
reqStream << "&AccuracyError=" << accuracy.error;
403+
reqStream << "&AccuracyNorm=" << accuracy.norm;
404+
reqStream << "&AccuracyRelative=" << (accuracy.relative ? 1 : 0);
405+
}
346406
if (!m_EngineParams.empty())
347407
{
348408
reqStream << "&EngineParams=" << UrlEncode(m_EngineParams);
@@ -352,7 +412,7 @@ std::string XrootdHttpRemote::BuildRequestString(const char *VarName, size_t Ste
352412
}
353413

354414
CURL *XrootdHttpRemote::CreateEasyHandle(AsyncGet *asyncOp, const std::string &url,
355-
const std::string &postData)
415+
const std::string &queryData)
356416
{
357417
#ifdef ADIOS2_HAVE_CURL
358418
CURL *easy = curl_easy_init();
@@ -362,8 +422,9 @@ CURL *XrootdHttpRemote::CreateEasyHandle(AsyncGet *asyncOp, const std::string &u
362422
curl_easy_setopt(easy, CURLOPT_PRIVATE, asyncOp);
363423
asyncOp->easyHandle = easy;
364424

365-
curl_easy_setopt(easy, CURLOPT_URL, url.c_str());
366-
curl_easy_setopt(easy, CURLOPT_COPYPOSTFIELDS, postData.c_str());
425+
std::string fullUrl = url + "?" + queryData;
426+
curl_easy_setopt(easy, CURLOPT_URL, fullUrl.c_str());
427+
curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);
367428
curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, WriteCallback);
368429
curl_easy_setopt(easy, CURLOPT_WRITEDATA, &asyncOp->responseData);
369430
curl_easy_setopt(easy, CURLOPT_CONNECTTIMEOUT, m_ConnectTimeout);
@@ -385,9 +446,6 @@ CURL *XrootdHttpRemote::CreateEasyHandle(AsyncGet *asyncOp, const std::string &u
385446
}
386447
}
387448

388-
asyncOp->headers =
389-
curl_slist_append(nullptr, "Content-Type: application/x-www-form-urlencoded");
390-
curl_easy_setopt(easy, CURLOPT_HTTPHEADER, asyncOp->headers);
391449
curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
392450

393451
return easy;
@@ -399,11 +457,12 @@ CURL *XrootdHttpRemote::CreateEasyHandle(AsyncGet *asyncOp, const std::string &u
399457
std::string XrootdHttpRemote::BuildBatchRequestString(const std::vector<BatchGetRequest> &requests)
400458
{
401459
std::ostringstream reqStream;
402-
std::string encodedFilename = UrlEncode(m_Filename);
403460

404-
reqStream << "batchget Filename=" << encodedFilename;
405-
reqStream << "&RMOrder=" << (m_RowMajorOrdering ? 1 : 0);
406-
reqStream << "&NVars=" << requests.size();
461+
reqStream << "batchget&NVars=" << requests.size();
462+
if (m_RowMajorOrdering)
463+
{
464+
reqStream << "&RMOrder=1";
465+
}
407466
if (!m_EngineParams.empty())
408467
{
409468
reqStream << "&EngineParams=" << UrlEncode(m_EngineParams);
@@ -412,18 +471,41 @@ std::string XrootdHttpRemote::BuildBatchRequestString(const std::vector<BatchGet
412471
for (const auto &req : requests)
413472
{
414473
std::string encodedVarName = UrlEncode(std::string(req.VarName));
415-
reqStream << "|Varname=" << encodedVarName;
416-
reqStream << "&StepStart=" << req.Step;
417-
reqStream << "&StepCount=" << req.StepCount;
418-
reqStream << "&Block=" << req.BlockID;
419-
reqStream << "&Dims=" << req.Count.size();
420-
for (const auto &c : req.Count)
421-
reqStream << "&Count=" << c;
422-
for (const auto &s : req.Start)
423-
reqStream << "&Start=" << s;
424-
reqStream << "&AccuracyError=" << req.accuracy.error;
425-
reqStream << "&AccuracyNorm=" << req.accuracy.norm;
426-
reqStream << "&AccuracyRelative=" << (req.accuracy.relative ? 1 : 0);
474+
reqStream << "&Varname=" << encodedVarName;
475+
if (req.Step != 0)
476+
{
477+
reqStream << "&StepStart=" << req.Step;
478+
}
479+
if (req.StepCount != 1)
480+
{
481+
reqStream << "&StepCount=" << req.StepCount;
482+
}
483+
if (req.BlockID != static_cast<size_t>(-1))
484+
{
485+
reqStream << "&Block=" << req.BlockID;
486+
}
487+
if (!req.Count.empty())
488+
{
489+
reqStream << "&Count=" << req.Count[0];
490+
for (size_t i = 1; i < req.Count.size(); i++)
491+
{
492+
reqStream << "," << req.Count[i];
493+
}
494+
}
495+
if (!req.Start.empty())
496+
{
497+
reqStream << "&Start=" << req.Start[0];
498+
for (size_t i = 1; i < req.Start.size(); i++)
499+
{
500+
reqStream << "," << req.Start[i];
501+
}
502+
}
503+
if (req.accuracy.error != 0.0 || req.accuracy.norm != 0.0 || req.accuracy.relative)
504+
{
505+
reqStream << "&AccuracyError=" << req.accuracy.error;
506+
reqStream << "&AccuracyNorm=" << req.accuracy.norm;
507+
reqStream << "&AccuracyRelative=" << (req.accuracy.relative ? 1 : 0);
508+
}
427509
}
428510

429511
return reqStream.str();
@@ -489,30 +571,58 @@ bool XrootdHttpRemote::BatchGet(const std::vector<BatchGetRequest> &requests)
489571
subBatches.push_back({startIdx, count, asyncOp});
490572
}
491573

492-
// Wait for all sub-batches and parse responses
574+
// Wait for all sub-batches, retry transient failures
575+
const int maxRetries = 3;
493576
bool allOk = true;
494577
for (auto &sb : subBatches)
495578
{
496579
bool success = sb.asyncOp->promise.get_future().get();
580+
std::string lastErr;
497581

498-
if (!success || !allOk)
582+
if (!success)
499583
{
500-
if (!success && allOk)
584+
lastErr = sb.asyncOp->errorMsg;
585+
delete sb.asyncOp;
586+
sb.asyncOp = nullptr;
587+
588+
// Retry this sub-batch
589+
for (int retry = 1; retry <= maxRetries; retry++)
501590
{
502-
std::string errMsg = sb.asyncOp->errorMsg;
503-
// Clean up remaining sub-batches before throwing
504-
delete sb.asyncOp;
505-
for (size_t j = (&sb - &subBatches[0]) + 1; j < subBatches.size(); j++)
591+
helper::Log("Remote", "XrootdHttpRemote", "BatchGet",
592+
"Sub-batch failed (" + lastErr + "), retry " + std::to_string(retry) +
593+
"/" + std::to_string(maxRetries),
594+
helper::LogMode::WARNING);
595+
596+
std::this_thread::sleep_for(std::chrono::milliseconds(100 * retry));
597+
598+
std::vector<BatchGetRequest> subRequests(requests.begin() + sb.startIdx,
599+
requests.begin() + sb.startIdx + sb.count);
600+
std::string postData = BuildBatchRequestString(subRequests);
601+
AsyncGet *retryOp = new AsyncGet();
602+
CURL *easy = CreateEasyHandle(retryOp, m_BaseUrl, postData);
603+
if (!easy)
506604
{
507-
subBatches[j].asyncOp->promise.get_future().get();
508-
delete subBatches[j].asyncOp;
605+
delete retryOp;
606+
continue;
509607
}
608+
pool.Submit(easy, retryOp);
609+
success = retryOp->promise.get_future().get();
610+
if (success)
611+
{
612+
sb.asyncOp = retryOp;
613+
break;
614+
}
615+
lastErr = retryOp->errorMsg;
616+
delete retryOp;
617+
}
618+
619+
if (!success)
620+
{
510621
helper::Throw<std::runtime_error>("Remote", "XrootdHttpRemote", "BatchGet",
511-
"Sub-batch failed: " + errMsg);
622+
"Sub-batch failed after " +
623+
std::to_string(maxRetries) +
624+
" retries: " + lastErr);
512625
}
513-
allOk = false;
514-
delete sb.asyncOp;
515-
continue;
516626
}
517627

518628
// Parse binary response:

0 commit comments

Comments
 (0)