Commit b1b17d31 authored by Aleksandr Konstantinov's avatar Aleksandr Konstantinov

Merge branch 'bug_3394' into 'master'

Fix for data corruption when transferring data between 2 remote connections. Fixes BUGZ-3394.

See merge request nordugrid/arc!1061
parents 21de79b5 d7809b8f
Pipeline #8506 passed with stages
in 160 minutes and 16 seconds
......@@ -527,7 +527,7 @@ static int runmain(int argc, char **argv) {
signal(SIGTERM, sig_cancel);
signal(SIGINT, sig_cancel);
Arc::LogStream logcerr(std::cerr);
static Arc::LogStream logcerr(std::cerr);
logcerr.setFormat(Arc::ShortFormat);
Arc::Logger::getRootLogger().addDestination(logcerr);
Arc::Logger::getRootLogger().setThreshold(Arc::WARNING);
......
......@@ -272,7 +272,7 @@ static int runmain(int argc, char **argv) {
setlocale(LC_ALL, "");
Arc::LogStream logcerr(std::cerr);
static Arc::LogStream logcerr(std::cerr);
logcerr.setFormat(Arc::ShortFormat);
Arc::Logger::getRootLogger().addDestination(logcerr);
Arc::Logger::getRootLogger().setThreshold(Arc::WARNING);
......
......@@ -80,7 +80,7 @@ static int runmain(int argc, char **argv) {
setlocale(LC_ALL, "");
Arc::LogStream logcerr(std::cerr);
static Arc::LogStream logcerr(std::cerr);
logcerr.setFormat(Arc::ShortFormat);
Arc::Logger::getRootLogger().addDestination(logcerr);
Arc::Logger::getRootLogger().setThreshold(Arc::WARNING);
......
......@@ -96,7 +96,7 @@ static int runmain(int argc, char **argv) {
setlocale(LC_ALL, "");
Arc::LogStream logcerr(std::cerr);
static Arc::LogStream logcerr(std::cerr);
logcerr.setFormat(Arc::ShortFormat);
Arc::Logger::getRootLogger().addDestination(logcerr);
Arc::Logger::getRootLogger().setThreshold(Arc::WARNING);
......
......@@ -105,7 +105,7 @@ static int runmain(int argc, char **argv) {
setlocale(LC_ALL, "");
Arc::LogStream logcerr(std::cerr);
static Arc::LogStream logcerr(std::cerr);
logcerr.setFormat(Arc::ShortFormat);
Arc::Logger::getRootLogger().addDestination(logcerr);
Arc::Logger::getRootLogger().setThreshold(Arc::WARNING);
......
......@@ -811,7 +811,7 @@ namespace ArcDMCFile {
return DataStatus::Success;
}
bool DataPointFile::WriteOutOfOrder() {
bool DataPointFile::WriteOutOfOrder() const {
if (!url)
return false;
if (url.Protocol() == "file")
......
......@@ -36,7 +36,7 @@ namespace ArcDMCFile {
virtual DataStatus Remove();
virtual DataStatus CreateDirectory(bool with_parents=false);
virtual DataStatus Rename(const URL& newurl);
virtual bool WriteOutOfOrder();
virtual bool WriteOutOfOrder() const;
virtual bool RequiresCredentials() const { return false; };
private:
SimpleCounter transfers_started;
......
......@@ -635,6 +635,9 @@ namespace ArcDMCGFAL {
return true;
}
bool DataPointGFAL::WriteOutOfOrder() const {
return true; // seek supported
}
} // namespace ArcDMCGFAL
......
......@@ -45,6 +45,7 @@ namespace ArcDMCGFAL {
// locations so Resolve and AddLocation must be implemented
virtual DataStatus Resolve(bool source = true);
virtual DataStatus AddLocation(const URL& url, const std::string& meta);
virtual bool WriteOutOfOrder() const;
protected:
// 3rd party transfer (destination pulls from source)
......
......@@ -46,6 +46,11 @@ namespace ArcDMCGFAL {
return true;
}
bool DataPointGFALDelegate::WriteOutOfOrder() const {
return true; // seek supported
}
} // namespace ArcDMCGFAL
extern Arc::PluginDescriptor const ARC_PLUGINS_TABLE_NAME[] = {
......
......@@ -23,6 +23,7 @@ namespace ArcDMCGFAL {
virtual ~DataPointGFALDelegate();
static Plugin* Instance(PluginArgument *arg);
virtual bool RequiresCredentials() const;
virtual bool WriteOutOfOrder() const;
};
} // namespace ArcDMCGFAL
......
......@@ -1301,7 +1301,7 @@ namespace ArcDMCGridFTP {
return new DataPointGridFTP(*dmcarg, *dmcarg, dmcarg);
}
bool DataPointGridFTP::WriteOutOfOrder() {
bool DataPointGridFTP::WriteOutOfOrder() const {
return true;
}
......
......@@ -119,7 +119,7 @@ namespace ArcDMCGridFTP {
virtual DataStatus Stat(FileInfo& file, DataPointInfoType verb = INFO_TYPE_ALL);
virtual DataStatus List(std::list<FileInfo>& files, DataPointInfoType verb = INFO_TYPE_ALL);
virtual DataStatus Rename(const URL& newurl);
virtual bool WriteOutOfOrder();
virtual bool WriteOutOfOrder() const;
virtual bool ProvidesMeta() const;
virtual const std::string DefaultCheckSum() const;
virtual bool RequiresCredentials() const;
......
......@@ -20,6 +20,8 @@ namespace ArcDMCGridFTP {
using namespace Arc;
static Logger logger_(Logger::getRootLogger(), "DataPoint.GridFTPDelegate");
DataPointGridFTPDelegate::DataPointGridFTPDelegate(const URL& url, const UserConfig& usercfg, PluginArgument* parg)
: DataPointDelegate((Arc::ArcLocation::GetLibDir()+G_DIR_SEPARATOR_S+"arc-dmcgridftp").c_str(), std::list<std::string>(), url, usercfg, parg) {
......@@ -58,6 +60,12 @@ namespace ArcDMCGridFTP {
return true;
}
bool DataPointGridFTPDelegate::WriteOutOfOrder() const {
// Globus gridftp library does not accept random offsets in stream mode.
// See DataPointGridFTP::set_attributes() on why following combination is used.
return (is_secure && !force_passive);
}
} // namespace ArcDMCGridFTP
......
......@@ -32,6 +32,7 @@ namespace ArcDMCGridFTP {
static Plugin* Instance(PluginArgument *arg);
virtual bool RequiresCredentials() const;
virtual bool SetURL(const Arc::URL&);
virtual bool WriteOutOfOrder() const;
private:
bool is_secure;
......
......@@ -37,6 +37,14 @@ namespace ArcDMCGridFTP {
static std::string const default_checksum("adler32");
class ChunkOffsetMatch {
public:
ChunkOffsetMatch(unsigned long long int offset):offset(offset) {};
bool operator()(DataExternalComm::DataChunkClient const& other) const { return offset == other.getOffset(); };
private:
unsigned long long int offset;
};
void DataPointGridFTPHelper::ftp_complete_callback(void *arg,
globus_ftp_client_handle_t*,
globus_object_t *error) {
......@@ -328,6 +336,7 @@ namespace ArcDMCGridFTP {
DataStatus DataPointGridFTPHelper::Read() {
if (!ftp_active) return DataStatus::NotInitializedError;
set_attributes();
delayed_chunks.clear();
bool limit_length = false;
unsigned long long int range_length = 0;
if (range_end > range_start) {
......@@ -359,6 +368,7 @@ namespace ArcDMCGridFTP {
return DataStatus(DataStatus::ReadStartError, globus_err);
}
// Prepare and inject buffers
max_offset = 0;
int n_buffers = 0;
for(int n = 0; n < (ftp_threads*2);++n) {
globus_byte_t* buffer = new globus_byte_t[ftp_bufsize];
......@@ -428,11 +438,40 @@ namespace ArcDMCGridFTP {
it->data_counter.dec();
it->data_counter_change.signal();
} else {
logger.msg(DEBUG, "ftp_read_callback: success");
logger.msg(DEBUG, "ftp_read_callback: success - offset=%u, length=%u, eof=%u, allow oof=%u",(int)offset,(int)length,(int)eof,(int)it->allow_out_of_order);
if(length > 0) {
// Report received content
DataExternalComm::DataChunkClient dataEntry(buffer, offset, length);
dataEntry.write(outstream<<DataExternalComm::DataChunkTag);
DataExternalComm::DataChunkClient dataChunk(buffer, offset, length);
if(it->allow_out_of_order || (offset == it->max_offset)) {
dataChunk.write(outstream<<DataExternalComm::DataChunkTag);
it->max_offset = offset+length;
// Check for delayed buffers which fit new offset
while(!it->delayed_chunks.empty()) {
DataExternalComm::DataChunkClientList::iterator matching_chunk =
std::find_if(it->delayed_chunks.begin(), it->delayed_chunks.end(), ChunkOffsetMatch(it->max_offset));
if(matching_chunk == it->delayed_chunks.end()) break;
unsigned long long int offset = matching_chunk->getOffset();
unsigned long long int length = matching_chunk->getSize();
logger.msg(VERBOSE, "ftp_read_callback: delayed data chunk: %llu %llu", offset, length);
matching_chunk->write(outstream<<DataExternalComm::DataChunkTag);
it->delayed_chunks.erase(matching_chunk);
it->max_offset = offset+length;
}
} else {
// protection against data coming out of order - receiving end can't handle that
logger.msg(WARNING, "ftp_read_callback: unexpected data out of order: %llu != %llu", offset, it->max_offset);
// The numbers below are just wild guess.
if((it->delayed_chunks.size() < (it->ftp_threads*10)) || ((offset - it->max_offset) < 1024*1024*256)) {
it->delayed_chunks.push_back(dataChunk.MakeCopy()); // move asignment passes ownership
} else {
// Can't use data from this buffer - drop it ad report error
it->data_error = true;
logger.msg(ERROR, "ftp_read_callback: too many unexpected out of order chunks");
delete[] buffer;
it->data_counter.dec();
it->data_counter_change.signal();
}
}
}
if (eof) it->ftp_eof_flag = true;
if (it->ftp_eof_flag) {
......@@ -452,6 +491,7 @@ namespace ArcDMCGridFTP {
}
}
}
if(it->data_counter.get() == 0) {
DataExternalComm::DataChunkClient dataEntry(NULL, offset+length, 0); // using 0 size as eof indication
dataEntry.write(outstream<<DataExternalComm::DataChunkTag);
......@@ -488,6 +528,7 @@ namespace ArcDMCGridFTP {
DataStatus DataPointGridFTPHelper::Write() {
if (!ftp_active) return DataStatus::NotInitializedError;
set_attributes();
delayed_chunks.clear();
// size of file first
bool limit_length = false;
unsigned long long int range_length = 0;
......@@ -528,7 +569,7 @@ namespace ArcDMCGridFTP {
// Read and inject buffers
data_error = false;
data_counter.set(0);
unsigned long long int max_offset = 0;
max_offset = 0;
while(true) {
static globus_byte_t dummy;
logger.msg(VERBOSE, "start_writing_ftp: waiting for data tag");
......@@ -549,10 +590,24 @@ namespace ArcDMCGridFTP {
unsigned long long int length = dataChunk.getSize();
globus_byte_t* data = reinterpret_cast<globus_byte_t*>(dataChunk.get());
if(length == 0) data = &dummy;
if(stream_mode) {
// protection against data coming out of order - stream mode can't handle that
if(offset != max_offset) {
logger.msg(WARNING, "ftp_write_thread: data out of order in stream mode: %llu != %llu", offset, max_offset);
// The numbers below are just wild guess. It i snot even proper place to compensate for out
// of order data chunks. It should have been fixed on reading side.
if((delayed_chunks.size() < (ftp_threads*10)) || ((offset-max_offset) < 1024*1024*256)) {
delayed_chunks.push_back(dataChunk); // move asignment passes ownership
continue;
}
logger.msg(ERROR, "ftp_write_thread: too many out of order chunks in stream mode");
GlobusResult(globus_ftp_client_abort(&ftp_handle));
break;
}
}
logger.msg(VERBOSE, "start_writing_ftp: data chunk: %llu %llu", offset, length);
data_counter.inc();
GlobusResult res(globus_ftp_client_register_write(&ftp_handle,
data, length, offset,
GlobusResult res(globus_ftp_client_register_write(&ftp_handle, data, length, offset,
dataChunk.getEOF()?GLOBUS_TRUE:GLOBUS_FALSE, &ftp_write_callback, cbarg));
if(!res) {
data_counter.dec();
......@@ -562,6 +617,36 @@ namespace ArcDMCGridFTP {
}
if(length != 0) dataChunk.release(); // pass ownership to globus
if((offset+length) > max_offset) max_offset = offset+length;
// Do we have any delayed buffers we could release?
while(!delayed_chunks.empty()) {
DataExternalComm::DataChunkClientList::iterator matching_chunk =
std::find_if(delayed_chunks.begin(), delayed_chunks.end(), ChunkOffsetMatch(max_offset));
if(matching_chunk == delayed_chunks.end()) break;
dataChunk = *matching_chunk;
delayed_chunks.erase(matching_chunk);
unsigned long long int offset = dataChunk.getOffset();
unsigned long long int length = dataChunk.getSize();
globus_byte_t* data = reinterpret_cast<globus_byte_t*>(dataChunk.get());
if(length == 0) data = &dummy;
logger.msg(VERBOSE, "start_writing_ftp: delayed data chunk: %llu %llu", offset, length);
data_counter.inc();
res = globus_ftp_client_register_write(&ftp_handle,
data, length, offset,
dataChunk.getEOF()?GLOBUS_TRUE:GLOBUS_FALSE, &ftp_write_callback, cbarg);
if(!res) {
data_counter.dec();
logger.msg(DEBUG, "ftp_write_thread: Globus error: %s", res.str());
GlobusResult(globus_ftp_client_abort(&ftp_handle));
break;
}
if(length != 0) dataChunk.release(); // pass ownership to globus
max_offset = offset+length;
}
if(!res) break;
// Prevent pile up of buffers in memory
while(data_counter.get() >= (ftp_threads*2)) {
// Wait for some buffers to be released
logger.msg(VERBOSE, "start_writing_ftp: waiting for some buffers sent");
......@@ -870,6 +955,7 @@ namespace ArcDMCGridFTP {
range_start(0),
range_end(0),
allow_out_of_order(true),
stream_mode(true),
credential(NULL),
ftp_eof_flag(false),
check_received_length(0),
......@@ -949,6 +1035,17 @@ namespace ArcDMCGridFTP {
}
}
ftp_active = true;
autodir = true;
std::string autodir_s = url.Option("autodir");
if(autodir_s == "yes") {
autodir = true;
} else if(autodir_s == "no") {
autodir = false;
}
lister = new Lister();
}
void DataPointGridFTPHelper::set_attributes(void) {
ftp_threads = 1;
ftp_bufsize = 65536;
if (allow_out_of_order) {
......@@ -961,17 +1058,6 @@ namespace ArcDMCGridFTP {
ftp_threads = 1;
}
}
autodir = true;
std::string autodir_s = url.Option("autodir");
if(autodir_s == "yes") {
autodir = true;
} else if(autodir_s == "no") {
autodir = false;
}
lister = new Lister();
}
void DataPointGridFTPHelper::set_attributes(void) {
globus_ftp_control_parallelism_t paral;
if (ftp_threads > 1) {
paral.fixed.mode = GLOBUS_FTP_CONTROL_PARALLELISM_FIXED;
......@@ -998,6 +1084,7 @@ namespace ArcDMCGridFTP {
logger.msg(VERBOSE, "globus_ftp_client_operationattr_set_authorization: error: %s", r.str());
}
stream_mode = true;
GlobusResult(globus_ftp_client_operationattr_set_mode(&ftp_opattr,
GLOBUS_FTP_CONTROL_MODE_STREAM));
GlobusResult(globus_ftp_client_operationattr_set_data_protection(&ftp_opattr,
......@@ -1040,9 +1127,11 @@ namespace ArcDMCGridFTP {
GlobusResult(globus_ftp_client_operationattr_set_dcau(&ftp_opattr, &dcau));
}
if (force_passive) {
stream_mode = true;
GlobusResult(globus_ftp_client_operationattr_set_mode(&ftp_opattr,
GLOBUS_FTP_CONTROL_MODE_STREAM));
} else {
stream_mode = false;
GlobusResult(globus_ftp_client_operationattr_set_mode(&ftp_opattr,
GLOBUS_FTP_CONTROL_MODE_EXTENDED_BLOCK));
}
......@@ -1152,6 +1241,7 @@ int main(int argc, char* argv[]) {
int logger_format = -1;
int secure = 1;
int passive = 1;
int out_of_order = 1;
try {
/* Create options parser */
......@@ -1165,6 +1255,7 @@ int main(int argc, char* argv[]) {
options.AddOption('F', "format", "logger output format", "format", logger_format);
options.AddOption('s', "secure", "force secure data connection", "boolean", secure);
options.AddOption('p', "passive", "force passive data connection", "boolean", passive);
options.AddOption('o', "noorder", "allow out of order reading", "boolean", out_of_order);
params = options.Parse(argc, argv);
if (params.empty()) {
......@@ -1211,6 +1302,7 @@ int main(int argc, char* argv[]) {
handler->SetRange(range_start, range_end);
handler->SetSecure(secure);
handler->SetPassive(passive);
handler->ReadOutOfOrder(out_of_order);
Arc::DataStatus result(Arc::DataStatus::Success);
if(command == Arc::DataPointDelegate::RenameCommand) {
if(params.empty()) {
......
......@@ -65,7 +65,10 @@ namespace ArcDMCGridFTP {
unsigned long long int range_start;
unsigned long long int range_end;
bool allow_out_of_order;
bool stream_mode;
bool autodir;
DataExternalComm::DataChunkClientList delayed_chunks;
unsigned long long int max_offset;
// Current state representation
SimpleCondition cond;
......@@ -120,9 +123,9 @@ namespace ArcDMCGridFTP {
void SetBufferSize(unsigned long long int size) { ftp_bufsize = size; };
void SetStreams(int streams) { ftp_threads = streams; };
void SetRange(unsigned long long int start, unsigned long long int end) { range_start = start; range_end = end; };
void SetAllowOutOfOrder(bool out_of_order) { allow_out_of_order = out_of_order; };
void SetSecure(bool secure) { force_secure = secure; };
void SetPassive(bool passive) { force_passive = passive; };
void ReadOutOfOrder(bool out_of_order) { allow_out_of_order = out_of_order; }
DataStatus Read();
DataStatus Write();
DataStatus Check();
......
......@@ -272,7 +272,10 @@ using namespace Arc;
reading(false),
writing(false),
chunks(NULL),
transfers_tofinish(0) {}
transfers_tofinish(0),
partial_read_allowed(url.Option("httpgetpartial") == "yes"),
partial_write_allowed(url.Option("httpputpartial") == "yes") {
}
DataPointHTTP::~DataPointHTTP() {
StopReading();
......@@ -1073,8 +1076,8 @@ using namespace Arc;
int retries = 0;
std::string path = point.CurrentLocation().FullPathURIEncoded();
DataStatus failure_code;
bool partial_read_allowed = (client_url.Option("httpgetpartial") == "yes");
if(partial_read_allowed) for (;;) {
bool partial_allowed = point.partial_read_allowed && point.allow_out_of_order;
if(partial_allowed) for (;;) {
if(client && client->GetClosed()) client = point.acquire_client(client_url);
if (!client) {
transfer_failure = true;
......@@ -1228,7 +1231,7 @@ using namespace Arc;
}
if (point.transfers_tofinish == 0) {
// TODO: process/report failure?
if(!partial_read_allowed) {
if(!partial_allowed) {
// Reading in single chunk to be done in single thread
if(!read_single(arg)) {
transfer_failure = true;
......@@ -1336,10 +1339,10 @@ using namespace Arc;
bool transfer_failure = false;
int retries = 0;
std::string path = client_url.FullPathURIEncoded();
bool partial_write_failure = (client_url.Option("httpputpartial") != "yes");
bool partial_failure = !point.partial_write_allowed;
DataStatus failure_code;
// Fall through if partial PUT is not allowed
if(!partial_write_failure) for (;;) {
if(!partial_failure) for (;;) {
if(client && client->GetClosed()) client = point.acquire_client(client_url);
if (!client) {
transfer_failure = true;
......@@ -1393,7 +1396,7 @@ using namespace Arc;
}
if (transfer_info.code == 501) {
// Not implemented - probably means server does not accept patial PUT
partial_write_failure = true;
partial_failure = true;
} else {
transfer_failure = true;
failure_code = DataStatus(DataStatus::WriteError, point.http2errno(transfer_info.code), transfer_info.reason);
......@@ -1410,7 +1413,7 @@ using namespace Arc;
point.buffer->error_write(true);
}
if (point.transfers_tofinish == 0) {
if(partial_write_failure) {
if(partial_failure) {
// Writing in single chunk to be done in single thread
if(!write_single(arg)) {
transfer_failure = true;
......@@ -1419,7 +1422,7 @@ using namespace Arc;
}
// TODO: process/report failure?
point.buffer->eof_write(true);
if ((!partial_write_failure) && (!(point.buffer->error())) && (point.buffer->eof_position() == 0)) {
if ((!partial_failure) && (!(point.buffer->error())) && (point.buffer->eof_position() == 0)) {
// Zero size data was transferred - must send at least one empty packet
for (;;) {
if (!client) client = point.acquire_client(client_url);
......
......@@ -38,6 +38,7 @@ using namespace Arc;
virtual DataStatus StopReading();
virtual DataStatus StopWriting();
virtual bool RequiresCredentials() const { return url.Protocol() != "http"; };
virtual bool WriteOutOfOrder() const { return partial_write_allowed; };
private:
static void read_thread(void *arg);
static bool read_single(void *arg);
......@@ -60,6 +61,8 @@ using namespace Arc;
int transfers_tofinish;
Glib::Mutex transfer_lock;
Glib::Mutex clients_lock;
bool partial_read_allowed;
bool partial_write_allowed;
};
} // namespace Arc
......
......@@ -663,7 +663,7 @@ DataStatus DataPointS3::StopWriting() {
return DataStatus::Success;
}
bool DataPointS3::WriteOutOfOrder() { return false; }
bool DataPointS3::WriteOutOfOrder() const { return false; }
} // namespace Arc
......
......@@ -38,7 +38,7 @@ public:
virtual DataStatus Remove();
virtual DataStatus CreateDirectory(bool with_parents = false);
virtual DataStatus Rename(const URL &newurl);
virtual bool WriteOutOfOrder();
virtual bool WriteOutOfOrder() const;
virtual bool RequiresCredentials() const {
return false;
};
......
......@@ -754,6 +754,12 @@ namespace ArcDMCSRM {
return true;
}
bool DataPointSRM::WriteOutOfOrder() const {
// Not sure if underlying data destination protocol will support
// accepting out-of-order data. So simply say no.
return false;
}
std::vector<URL> DataPointSRM::TransferLocations() const {
return turls;
}
......
......@@ -54,6 +54,7 @@ namespace ArcDMCSRM {
virtual bool ProvidesMeta() const;
virtual bool AcceptsMeta() const;
virtual bool IsStageable() const;
virtual bool WriteOutOfOrder() const;
virtual std::vector<URL> TransferLocations() const;
virtual void ClearTransferLocations();
......
......@@ -4,6 +4,8 @@
#include <config.h>
#endif
#include <cstring>
#include <arc/Logger.h>
#include <arc/StringConv.h>
#include "DataExternalComm.h"
......@@ -366,10 +368,35 @@ namespace Arc {
data((char*)data), data_allocated(false), offset(offset), size(size), eof(false) {
}
DataExternalComm::DataChunkClient::DataChunkClient(DataChunkClient& other):
data(other.data), data_allocated(other.data_allocated), offset(other.offset), size(other.size), eof(other.eof) {
other.data_allocated = false;
}
DataExternalComm::DataChunkClient& DataExternalComm::DataChunkClient::operator=(DataExternalComm::DataChunkClient& other) {
if(data_allocated) delete[] data;
data = other.data;
data_allocated = other.data_allocated;
offset= other.offset;
size = other.size;
eof = other.eof;
other.data_allocated = false;
}
DataExternalComm::DataChunkClient::~DataChunkClient() {
if(data_allocated) delete[] data;
}
DataExternalComm::DataChunkClient& DataExternalComm::DataChunkClient::MakeCopy() {
if(data_allocated) return *this; // already copied
if(!data || !size) return *this;
char* new_data = new char[size];
std::memcpy(new_data, data, size);
data = new_data;
data_allocated = true;
return *this;
}
bool DataExternalComm::DataChunkClient::write(std::ostream& outstream) const {
try {
itemOut(outstream, Arc::inttostr(offset));
......
......@@ -72,9 +72,17 @@ namespace Arc {
class DataChunkClient {
public:
DataChunkClient(); // empty
// Empty buffer
DataChunkClient();
// Exteral buffer
DataChunkClient(void* data, unsigned long long int offset, unsigned long long int size);
// Move constructor