Skip to content

Commit 5bf889d

Browse files
committed
Query and validate http header cache the following control directives: ETag, must-revalidate, and max-age.
The cache control parameters are saved in xattr of the *cinfo file at the time of *cinfo file creation in function XrdPfc::File::Open(). The parameter values are retrieved with XrdCl::File::Fcntl() query. The cache control parameters are tested in Pfc::Cache::Prepare() where the values are compared from the save xattr attributes from *cinfo file and the one queried with XrdCl::FileSystem.::Query() The comparison is made only if must-revalidate is true or expired. The File::Fcntl() and FileSystem::Query() use new query code XrdCl::QueryCode::Head.
1 parent a90e401 commit 5bf889d

File tree

9 files changed

+185
-6
lines changed

9 files changed

+185
-6
lines changed

src/XrdCl/XrdClFileSystem.hh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ namespace XrdCl
6060
Space = kXR_Qspace, //!< Query logical space stats
6161
Stats = kXR_QStats, //!< Query server stats
6262
Visa = kXR_Qvisa, //!< Query file visa attributes
63+
Head = kXR_Qhead, //!< Query http header response
6364
XAttr = kXR_Qxattr //!< Query file extended attributes
6465
};
6566
};

src/XrdOuc/XrdOucCache.hh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
#include "XrdOuc/XrdOucCacheStats.hh"
3939
#include "XrdOuc/XrdOucIOVec.hh"
40+
#include "XrdCl/XrdClBuffer.hh"
4041

4142
struct stat;
4243
class XrdOucEnv;
@@ -165,6 +166,20 @@ long long FSize() = 0;
165166

166167
virtual int Fstat(struct stat &sbuff) {(void)sbuff; return 1;}
167168

169+
170+
//------------------------------------------------------------------------------
171+
//! Perform an fcntl() operation (defaults to passthrough).
172+
//!
173+
//! @param AMT, for the moment XrdCl::Buffer to pass query code value and
174+
//! XrdCl::Buffer to pass the string response. The XrdCL::Buffers is
175+
//! interpreted as std::string
176+
//!
177+
//! @return <0 - fstat failed, value is -errno.
178+
//! =0 - fstat succeeded, sbuff holds stat information.
179+
//! >0 - fstat could not be done, forward operation to next level.
180+
//------------------------------------------------------------------------------
181+
virtual int Fcntl(const XrdCl::Buffer& args, XrdCl::Buffer*& res) { return -1; }
182+
168183
//-----------------------------------------------------------------------------
169184
//! Get the file's location (i.e. endpoint hostname and port)
170185
//!

src/XrdPfc/XrdPfc.cc

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@
2222
#include <sys/statvfs.h>
2323

2424
#include "XrdCl/XrdClURL.hh"
25+
#include "XrdCl/XrdClFileSystem.hh"
26+
#include "XrdCl/XrdClFileStateHandler.hh"
2527

2628
#include "XrdOuc/XrdOucEnv.hh"
2729
#include "XrdOuc/XrdOucUtils.hh"
2830
#include "XrdOuc/XrdOucPrivateUtils.hh"
31+
#include "XrdOuc/XrdOucJson.hh"
2932

3033
#include "XrdSys/XrdSysTimer.hh"
3134
#include "XrdSys/XrdSysTrace.hh"
@@ -425,6 +428,7 @@ File* Cache::GetFile(const std::string& path, IO* io, long long off, long long f
425428
}
426429

427430
// This is always true, now that IOFileBlock is unsupported.
431+
428432
if (filesize == 0)
429433
{
430434
struct stat st;
@@ -444,7 +448,7 @@ File* Cache::GetFile(const std::string& path, IO* io, long long off, long long f
444448

445449
if (filesize >= 0)
446450
{
447-
file = File::FileOpen(path, off, filesize);
451+
file = File::FileOpen(path, off, filesize, io->GetInput());
448452
}
449453

450454
{
@@ -904,6 +908,20 @@ int Cache::LocalFilePath(const char *curl, char *buff, int blen,
904908
return -ENOENT;
905909
}
906910

911+
//______________________________________________________________________________
912+
// If supported, write Cache-Control as xattr to cinfo file.
913+
// One can use file descriptor or full path interchangeably
914+
//------------------------------------------------------------------------------
915+
void Cache::WriteCacheControlXAttr(int cinfo_fd, const char* path, const std::string& cc)
916+
{
917+
if (m_metaXattr) {
918+
int res = XrdSysXAttrActive->Set("pfc.cache-control", cc.c_str(), cc.size(), path, cinfo_fd, 0);
919+
if (res != 0) {
920+
TRACE(Error, "WritecacheControlXAttr error setting xattr " << res);
921+
}
922+
}
923+
}
924+
907925
//______________________________________________________________________________
908926
// If supported, write file_size as xattr to cinfo file.
909927
//------------------------------------------------------------------------------
@@ -958,6 +976,47 @@ long long Cache::DetermineFullFileSize(const std::string &cinfo_fname)
958976
return ret;
959977
}
960978

979+
//______________________________________________________________________________
980+
// Get cache control attributes from the corresponding cinfo-file name.
981+
// Returns -error on failure.
982+
//------------------------------------------------------------------------------
983+
int Cache::GetCacheControlXAttr(const std::string &cinfo_fname, std::string& ival)
984+
{
985+
if (m_metaXattr) {
986+
987+
char pfn[4096];
988+
m_oss->Lfn2Pfn(cinfo_fname.c_str(), pfn, 4096);
989+
990+
char cc[512];
991+
int res = XrdSysXAttrActive->Get("pfc.cache-control", &cc, 512, pfn, -1);
992+
if (res > 0)
993+
{
994+
std::string tmp(cc, res);
995+
ival = tmp;
996+
}
997+
return res;
998+
}
999+
return 0;
1000+
}
1001+
1002+
//______________________________________________________________________________
1003+
// Get cache control attributes from the corresponding cinfo-file name.
1004+
// Returns -error on failure.
1005+
//------------------------------------------------------------------------------
1006+
int Cache::GetCacheControlXAttr(int fd, std::string& ival)
1007+
{
1008+
if (m_metaXattr) {
1009+
char cc[512];
1010+
int res = XrdSysXAttrActive->Get("pfc.cache-control", &cc, 512, nullptr, fd);
1011+
if (res > 0)
1012+
{
1013+
ival = std::string(cc, res);
1014+
return res;
1015+
}
1016+
}
1017+
return 0;
1018+
}
1019+
9611020
//______________________________________________________________________________
9621021
// Calculate if the file is to be considered cached for the purposes of
9631022
// only-if-cached and setting of atime of the Stat() calls.
@@ -1097,6 +1156,64 @@ int Cache::Prepare(const char *curl, int oflags, mode_t mode)
10971156
if (m_oss->Stat(i_name.c_str(), &sbuff) == XrdOssOK)
10981157
{
10991158
TRACE(Dump, "Prepare defer open " << f_name);
1159+
1160+
std::string icc;
1161+
if (GetCacheControlXAttr(i_name, icc) > 0) {
1162+
using namespace nlohmann;
1163+
json cc_json = json::parse(icc);
1164+
1165+
bool mustRevalidate = cc_json.contains("revalidate") && (cc_json["revalidate"] == true);
1166+
bool hasExpired = false;
1167+
if (cc_json.contains("expire"))
1168+
{
1169+
time_t current_time;
1170+
current_time = time(NULL);
1171+
if (current_time > cc_json["expire"])
1172+
hasExpired = true;
1173+
}
1174+
1175+
bool ccIsValid = true;
1176+
1177+
if (cc_json.contains("ETag") && (mustRevalidate || hasExpired)) {
1178+
// Compare cinfo xattr etag and the etag from file system query response
1179+
// Note: qeury returns only etag value, not a json string
1180+
XrdCl::FileSystem fs(url);
1181+
XrdCl::Buffer queryArgs(1024); // pass file path throug args: reserve bytes to store path
1182+
queryArgs.FromString(curl);
1183+
XrdCl::Buffer *response = nullptr;
1184+
XrdCl::XRootDStatus st = fs.Query(XrdCl::QueryCode::Head, queryArgs, response);
1185+
1186+
if (st.IsOK())
1187+
{
1188+
std::string etag = response->ToString();
1189+
ccIsValid = (etag == cc_json["ETag"]);
1190+
TRACE(Info, "Prepare " << f_name << ", ETag valid res: " << ccIsValid);
1191+
1192+
// update expiration time if Etag is valid
1193+
if (cc_json.contains("max-age"))
1194+
{
1195+
time_t ma = cc_json["max-age"];
1196+
cc_json["expire"] = ma + time(NULL);
1197+
char pfn[4096];
1198+
m_oss->Lfn2Pfn(i_name.c_str(), pfn, 4096);
1199+
WriteCacheControlXAttr(-1, pfn, cc_json.dump());
1200+
}
1201+
}
1202+
else
1203+
{
1204+
// Message has a status beacuse we are in the block condition for cache-contol xattr
1205+
TRACE(Error, "Prepare() XrdCl::FileSystem::Query failed " << f_name.c_str());
1206+
ccIsValid = false;
1207+
}
1208+
}
1209+
1210+
if (!ccIsValid)
1211+
{
1212+
// invalidate cinfo on ETag mismatch
1213+
UnlinkFile(f_name, false);
1214+
}
1215+
} // end chekcing cache control xattr in cinfo file
1216+
11001217
return 1;
11011218
}
11021219
else

src/XrdPfc/XrdPfc.hh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,12 @@ public:
186186
virtual int ConsiderCached(const char *url);
187187

188188
bool DecideIfConsideredCached(long long file_size, long long bytes_on_disk);
189+
void WriteCacheControlXAttr(int cinfo_fd, const char* path, const std::string& cc);
189190
void WriteFileSizeXAttr(int cinfo_fd, long long file_size);
190191
long long DetermineFullFileSize(const std::string &cinfo_fname);
192+
int GetCacheControlXAttr(const std::string &cinfo_fname, std::string& res);
193+
int GetCacheControlXAttr(int fd, std::string& res);
194+
191195

192196
//--------------------------------------------------------------------
193197
//! \brief Makes decision if the original XrdOucCacheIO should be cached.

src/XrdPfc/XrdPfcFile.cc

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@
2727
#include "XrdSys/XrdSysTimer.hh"
2828
#include "XrdOss/XrdOss.hh"
2929
#include "XrdOuc/XrdOucEnv.hh"
30+
#include "XrdOuc/XrdOucJson.hh"
3031
#include "XrdSfs/XrdSfsInterface.hh"
3132

33+
#include "XrdCl/XrdClFileStateHandler.hh"
34+
3235
#include <cstdio>
3336
#include <sstream>
3437
#include <fcntl.h>
@@ -135,10 +138,10 @@ void File::Close()
135138

136139
//------------------------------------------------------------------------------
137140

138-
File* File::FileOpen(const std::string &path, long long offset, long long fileSize)
141+
File* File::FileOpen(const std::string &path, long long offset, long long fileSize, XrdOucCacheIO* inputIO)
139142
{
140143
File *file = new File(path, offset, fileSize);
141-
if ( ! file->Open())
144+
if ( ! file->Open(inputIO))
142145
{
143146
delete file;
144147
file = 0;
@@ -420,7 +423,7 @@ void File::RemoveIO(IO *io)
420423

421424
//------------------------------------------------------------------------------
422425

423-
bool File::Open()
426+
bool File::Open(XrdOucCacheIO* inputIO)
424427
{
425428
// Sets errno accordingly.
426429

@@ -531,6 +534,34 @@ bool File::Open()
531534
m_cfi.Write(m_info_file, ifn.c_str());
532535
m_info_file->Fsync();
533536
cache()->WriteFileSizeXAttr(m_info_file->getFD(), m_file_size);
537+
538+
// access and write cache-control attributes
539+
XrdCl::QueryCode::Code queryCode = XrdCl::QueryCode::Head;
540+
XrdCl::Buffer queryArgs(5);
541+
std::string qs = std::to_string(queryCode);
542+
queryArgs.FromString(qs);
543+
XrdCl::Buffer *responseFctl = nullptr;
544+
int resFctl = inputIO->Fcntl(queryArgs, responseFctl);
545+
if (resFctl == 0)
546+
{
547+
std::string cc_str = responseFctl->ToString();
548+
nlohmann::json cc_json = nlohmann::json::parse(cc_str);
549+
if (cc_json.contains("max-age"))
550+
{
551+
time_t ma = cc_json["max-age"];
552+
ma += time(NULL);
553+
cc_json["expire"] = ma;
554+
cc_str = cc_json.dump();
555+
}
556+
TRACE(Error, "GetFile() XrdCl::File::Fcntl value " << cc_str);
557+
cache()->WriteCacheControlXAttr(m_info_file->getFD(), nullptr, cc_str);
558+
}
559+
else if (resFctl != kXR_Unsupported)
560+
{
561+
// Query XrdCl::QueryCode::Head is optional, print error only if informatin is supported
562+
TRACE(Error, "GetFile() XrdCl::File::Fcntl query XrdCl::QueryCode::Head failed " << inputIO->Path());
563+
}
564+
534565
TRACEF(Debug, tpfx << "Creating new file info, data size = " << m_file_size << " num blocks = " << m_cfi.GetNBlocks());
535566
}
536567
else

src/XrdPfc/XrdPfcFile.hh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ public:
208208
// Constructor, destructor, Open() and Close() are private.
209209

210210
//! Static constructor that also does Open. Returns null ptr if Open fails.
211-
static File* FileOpen(const std::string &path, long long offset, long long fileSize);
211+
static File* FileOpen(const std::string &path, long long offset, long long fileSize, XrdOucCacheIO*);
212212

213213
//! Handle removal of a block from Cache's write queue.
214214
void BlockRemovedFromWriteQ(Block*);
@@ -301,7 +301,7 @@ private:
301301
void Close();
302302

303303
//! Open file handle for data file and info file on local disk.
304-
bool Open();
304+
bool Open(XrdOucCacheIO* inputOrigin);
305305

306306
static const char *m_traceID;
307307

src/XrdPosix/XrdPosixFile.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,13 @@ int XrdPosixFile::Fstat(struct stat &buf)
386386
buf.st_mode = myMode;
387387
return 0;
388388
}
389+
390+
int XrdPosixFile::Fcntl(const XrdCl::Buffer &arg, XrdCl::Buffer *&response)
391+
{
392+
// AMT: temporary solution to handle unsuported operations in XrdPfc::File::Open()
393+
XrdCl::XRootDStatus status = clFile.Fcntl(arg, response);
394+
return status.IsOK() ? 0 : status.errNo;
395+
}
389396

390397
/******************************************************************************/
391398
/* H a n d l e R e s p o n s e */

src/XrdPosix/XrdPosixFile.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ static void DelayedDestroy(XrdPosixFile *fp);
100100

101101
int Fstat(struct stat &buf) override;
102102

103+
int Fcntl(const XrdCl::Buffer& args, XrdCl::Buffer*& res) override;
104+
103105
const char *Location(bool refresh=false) override;
104106

105107
void HandleResponse(XrdCl::XRootDStatus *status,

src/XrdPosix/XrdPosixPrepIO.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ long long FSize() {return (Init() ? fileP->FSize() : openRC);}
4848
int Fstat(struct stat &buf)
4949
{return (Init() ? fileP->Fstat(buf) : openRC);}
5050

51+
int Fcntl(const XrdCl::Buffer& args, XrdCl::Buffer*& res) { return (Init() ? fileP->Fcntl(args, res) : openRC); }
52+
5153
int Open() {Init(); return openRC;}
5254

5355
const char *Path() {return fileP->Path();}

0 commit comments

Comments
 (0)