Commit c89a902d authored by Eric Cano's avatar Eric Cano
Browse files

Created a new unit test for failing migrations (missing disk file).

Reviewed and fixed the error handling for migrations.
A deadlock was created by the competing usage of a stop variable an normal information flow
through the threads. The global variable's usage has been reduced and clarified:
it is set by the tape thread to indicated an error reached it. From that point
on, the remaining disk threads can stop what they are doing (and not before as failing
to read a file should not block the completion of previous ones).
The file errors are now transmitted to the client before end of session, and in case of flush.
The unit tests have been updated accordingly.
Report messages have been improved.
parent d6b978ff
......@@ -480,4 +480,84 @@ TEST(tapeServer, DataTransferSessionGooddayMigration) {
}
}
TEST(tapeServer, DataTransferSessionMissingFilesMigration) {
// TpcpClients only supports 32 bits session number
// This number has to be less than 2^31 as in addition there is a mix
// of signed and unsigned numbers
// As the current ids in prod are ~30M, we are far from overflow (Feb 2013)
// 0) Prepare the logger for everyone
castor::log::StringLogger logger("tapeServerUnitTest");
// 1) prepare the client and run it in another thread
uint32_t volReq = 0xBEEF;
std::string vid = "V12345";
std::string density = "8000GC";
client::ClientSimulator sim(volReq, vid, density,
castor::tape::tapegateway::WRITE_TP, castor::tape::tapegateway::WRITE);
client::ClientSimulator::ipPort clientAddr = sim.getCallbackAddress();
clientRunner simRun(sim);
simRun.start();
// 2) Prepare the VDQM request
castor::legacymsg::RtcpJobRqstMsgBody VDQMjob;
snprintf(VDQMjob.clientHost, CA_MAXHOSTNAMELEN+1, "%d.%d.%d.%d",
clientAddr.a, clientAddr.b, clientAddr.c, clientAddr.d);
snprintf(VDQMjob.driveUnit, CA_MAXUNMLEN+1, "T10D6116");
snprintf(VDQMjob.dgn, CA_MAXDGNLEN+1, "LIBXX");
VDQMjob.clientPort = clientAddr.port;
VDQMjob.volReqId = volReq;
// 3) Prepare the necessary environment (logger, plus system wrapper),
// construct and run the session.
castor::tape::System::mockWrapper mockSys;
mockSys.delegateToFake();
mockSys.disableGMockCallsCounting();
mockSys.fake.setupForVirtualDriveSLC6();
//delete is unnecessary
//pointer with ownership will be passed to the application,
//which will do the delete
mockSys.fake.m_pathToDrive["/dev/nst0"] = new castor::tape::tapeserver::drives::FakeDrive;
// Just label the tape
castor::tape::tapeFile::LabelSession ls(*mockSys.fake.m_pathToDrive["/dev/nst0"],
"V12345", true);
mockSys.fake.m_pathToDrive["/dev/nst0"]->rewind();
// Prepare the files, but delete them immediately. The migration will fail.
for (int fseq=1; fseq<=10; fseq++) {
// Create the file from which we will recall
std::auto_ptr<tempFile> tf(new tempFile(1000));
// Prepare the migrationRequest
castor::tape::tapegateway::FileToMigrateStruct ftm;
ftm.setFileSize(tf->m_size);
ftm.setFileid(1000 + fseq);
ftm.setFseq(fseq);
ftm.setPath(tf->path());
sim.addFileToMigrate(ftm);
}
castor::tape::utils::DriveConfig driveConfig;
driveConfig.unitName = "T10D6116";
driveConfig.dgn = "T10KD6";
driveConfig.devFilename = "/dev/tape_T10D6116";
driveConfig.densities.push_back("8000GC");
driveConfig.densities.push_back("5000GC");
driveConfig.librarySlot = "manual";
driveConfig.devType = "T10000";
DataTransferSession::CastorConf castorConf;
castorConf.rtcopydBufsz = 1024*1024; // 1 MB memory buffers
castorConf.rtcopydNbBufs = 10;
castorConf.tapebridgeBulkRequestMigrationMaxBytes = UINT64_C(100)*1000*1000*1000;
castorConf.tapebridgeBulkRequestMigrationMaxFiles = 1000;
castorConf.tapeserverdDiskThreads = 1;
castor::legacymsg::VmgrProxyDummy vmgr;
castor::legacymsg::VdqmProxyDummy vdqm(VDQMjob);
castor::legacymsg::RmcProxyDummy rmc;
castor::messages::TapeserverProxyDummy initialProcess;
castor::server::ProcessCapDummy capUtils;
DataTransferSession sess("tapeHost", VDQMjob, logger, mockSys,
driveConfig, rmc, initialProcess, capUtils, castorConf);
sess.execute();
simRun.wait();
}
} // namespace unitTest
......@@ -25,24 +25,6 @@
#include "castor/tape/utils/Timer.hpp"
#include "castor/log/LogContext.hpp"
namespace{
/** Use RAII to make sure the memory block is released
*(ie pushed back to the memory manager) in any case (exception or not)
*/
class AutoPushBlock{
castor::tape::tapeserver::daemon::MemBlock *block;
castor::tape::tapeserver::daemon::DataConsumer& next;
public:
AutoPushBlock(castor::tape::tapeserver::daemon::MemBlock* mb,
castor::tape::tapeserver::daemon::DataConsumer& task):
block(mb),next(task){}
~AutoPushBlock(){
next.pushDataBlock(block);
}
};
}
namespace castor {
namespace tape {
namespace tapeserver {
......@@ -68,12 +50,13 @@ void DiskReadTask::execute(log::LogContext& lc) {
utils::Timer localTime;
size_t blockId=0;
size_t migratingFileSize=m_migratedFile->fileSize();
MemBlock* mb=NULL;
try{
//we first check here to not even try to open the disk if a previous task has failed
//because the disk could the very reason why the previous one failed,
//so dont do the same mistake twice !
hasAnotherTaskTailed();
checkMigrationFailing();
tape::diskFile::ReadFile sourceFile(m_migratedFile->path());
if(migratingFileSize != sourceFile.size()){
......@@ -88,11 +71,10 @@ void DiskReadTask::execute(log::LogContext& lc) {
while(migratingFileSize>0){
hasAnotherTaskTailed();
checkMigrationFailing();
MemBlock* const mb = m_nextTask.getFreeBlock();
mb = m_nextTask.getFreeBlock();
m_stats.waitFreeMemoryTime+=localTime.secs(utils::Timer::resetCounter);
AutoPushBlock push(mb,m_nextTask);
//set metadata and read the data
mb->m_fileid = m_migratedFile->fileid();
......@@ -112,6 +94,10 @@ void DiskReadTask::execute(log::LogContext& lc) {
}
m_stats.checkingErrorTime += localTime.secs(utils::Timer::resetCounter);
// We are done with the block, push it to the write task
m_nextTask.pushDataBlock(mb);
mb=NULL;
} //end of while(migratingFileSize>0)
m_stats.filesCount++;
......@@ -120,36 +106,49 @@ void DiskReadTask::execute(log::LogContext& lc) {
lc.log(LOG_INFO,"DiskReadTask: a previous file has failed for migration "
"Do nothing except circulating blocks");
circulateAllBlocks(blockId);
circulateAllBlocks(blockId,mb);
}
catch(const castor::exception::Exception& e){
//signal to all others task that this session is screwed
m_errorFlag.set();
//we have to pump the blocks anyway, mark them failed and then pass them back to TapeWrite
//Otherwise they would be stuck into TapeWriteTask free block fifo
// We have to pump the blocks anyway, mark them failed and then pass them back
// to TapeWriteTask
// Otherwise they would be stuck into TapeWriteTask free block fifo
// If we got here we had some job to do so there shall be at least one
// block either at hand or available.
// The tape write task, upon reception of the failed block will mark the
// session as failed, hence signalling to the remaining disk read tasks to
// cancel as nothing more will be written to tape.
if (!mb) {
mb=m_nextTask.getFreeBlock();
++blockId;
}
mb->markAsFailed(e.getMessageValue(),e.code());
m_nextTask.pushDataBlock(mb);
mb=NULL;
LogContext::ScopedParam sp(lc, Param("blockID",blockId));
LogContext::ScopedParam sp0(lc, Param("exceptionCode",e.code()));
LogContext::ScopedParam sp1(lc, Param("exceptionMessage", e.getMessageValue()));
lc.log(LOG_ERR,"Exception while reading a file");
//deal here the number of mem block
circulateAllBlocks(blockId);
circulateAllBlocks(blockId,mb);
} //end of catch
}
//------------------------------------------------------------------------------
// DiskReadTask::circulateAllBlocks
//------------------------------------------------------------------------------
void DiskReadTask::circulateAllBlocks(size_t fromBlockId){
void DiskReadTask::circulateAllBlocks(size_t fromBlockId, MemBlock * mb){
size_t blockId = fromBlockId;
while(blockId<m_numberOfBlock) {
MemBlock * mb = m_nextTask.getFreeBlock();
if (!mb) {
mb = m_nextTask.getFreeBlock();
++blockId;
}
mb->m_fileid = m_migratedFile->fileid();
// mb->markAsFailed();
mb->markAsCancelled();
m_nextTask.pushDataBlock(mb);
++blockId;
mb=NULL;
} //end of while
}
......
......@@ -64,14 +64,14 @@ private:
/**
* Throws an exception if m_errorFlag is set
*/
void hasAnotherTaskTailed() const {
void checkMigrationFailing() const {
//if a task has signaled an error, we stop our job
if(m_errorFlag){
throw castor::tape::tapeserver::daemon::ErrorFlag();
}
}
/**
/**
* log into lc all m_stats parameters with the given message at the
* given level
* @param level
......@@ -79,7 +79,7 @@ private:
*/
void logWithStat(int level,const std::string& msg,log::LogContext& lc) ;
void circulateAllBlocks(size_t fromBlockId);
void circulateAllBlocks(size_t fromBlockId, MemBlock * mb);
/**
* The task (a TapeWriteTask) that will handle the read blocks
*/
......
......@@ -102,8 +102,6 @@ public:
* Throw an exception if there is no context
* @return
*/
/* Out of the process, waiting to check if it is a good idea or not
* send the error code
int errorCode() const {
if(m_context.get()) {
return m_context->m_errorCode;
......@@ -111,7 +109,7 @@ public:
throw castor::exception::Exception("Error Context is not set ="
" no error code to give");
} */
}
/**
* Return true if the block has been marked as failed
......
......@@ -160,6 +160,34 @@ std::vector<tapegateway::FileMigratedNotificationStruct*>::iterator end
}
}
//------------------------------------------------------------------------------
//Report::reportFileErrors
//------------------------------------------------------------------------------
void MigrationReportPacker::Report::reportFileErrors(MigrationReportPacker& _this)
{
// Some errors still have to be transmitted to the client, but not the
// successful writes which were not validated by a flush (they will be
// discarded)
if(_this.m_listReports->failedMigrations().size()) {
tapeserver::client::ClientInterface::RequestReport chrono;
// First, cleanup the report of existing successes
for (size_t i=0; i<_this.m_listReports->successfulMigrations().size(); i++) {
delete _this.m_listReports->successfulMigrations()[i];
}
_this.m_listReports->successfulMigrations().resize(0);
// Report those errors to the client
_this.logReportWithError(_this.m_listReports->failedMigrations(),
"Will report failed file to the client before end of session");
_this.m_client.reportMigrationResults(*(_this.m_listReports),chrono);
log::ScopedParamContainer sp(_this.m_lc);
sp.add("connectDuration", chrono.connectDuration)
.add("sendRecvDuration", chrono.sendRecvDuration)
.add("transactionId", chrono.transactionId);
_this.m_lc.log(LOG_INFO, "Reported failed file(s) to the client before end of session");
// Reset the report lists.
_this.m_listReports.reset(new tapegateway::FileMigrationReportList);
}
}
//------------------------------------------------------------------------------
//ReportFlush::execute
//------------------------------------------------------------------------------
void MigrationReportPacker::ReportFlush::execute(MigrationReportPacker& _this){
......@@ -170,31 +198,30 @@ void MigrationReportPacker::ReportFlush::execute(MigrationReportPacker& _this){
computeCompressedSize(_this.m_listReports->successfulMigrations().begin(),
_this.m_listReports->successfulMigrations().end());
_this.m_client.reportMigrationResults(*(_this.m_listReports),chrono);
_this.logReport(_this.m_listReports->successfulMigrations(),"A file was successfully written on the tape");
log::ScopedParamContainer container(_this.m_lc);
container.add("batch size",_this.m_listReports->successfulMigrations().size())
.add("compressed",m_compressStats.toTape)
.add("Non compressed",m_compressStats.fromHost);
_this.m_lc.log(LOG_INFO,"Reported to the client that a batch of file was written on tape");
}
_this.m_client.reportMigrationResults(*(_this.m_listReports),chrono);
_this.logReport(_this.m_listReports->successfulMigrations(),"A file was successfully written on the tape");
log::ScopedParamContainer container(_this.m_lc);
container.add("batch size",_this.m_listReports->successfulMigrations().size())
.add("compressed",m_compressStats.toTape)
.add("Non compressed",m_compressStats.fromHost);
_this.m_lc.log(LOG_INFO,"Reported to the client that a batch of file was written on tape");
}
catch(const castor::exception::Exception& e){
LogContext::ScopedParam sp[]={
LogContext::ScopedParam(_this.m_lc, Param("exceptionCode",e.code())),
LogContext::ScopedParam(_this.m_lc, Param("exceptionMessageValue", e.getMessageValue())),
LogContext::ScopedParam(_this.m_lc, Param("exceptionWhat",e.what()))
};
tape::utils::suppresUnusedVariable(sp);
const std::string msg_error="An exception was caught trying to call reportMigrationResults";
_this.m_lc.log(LOG_ERR,msg_error);
throw failedMigrationRecallResult(msg_error);
LogContext::ScopedParam sp[]={
LogContext::ScopedParam(_this.m_lc, Param("exceptionCode",e.code())),
LogContext::ScopedParam(_this.m_lc, Param("exceptionMessageValue", e.getMessageValue())),
LogContext::ScopedParam(_this.m_lc, Param("exceptionWhat",e.what()))
};
tape::utils::suppresUnusedVariable(sp);
const std::string msg_error="An exception was caught trying to call reportMigrationResults";
_this.m_lc.log(LOG_ERR,msg_error);
throw failedMigrationRecallResult(msg_error);
}
}
else {
const std::string& msg ="A flush on tape has been reported but a writing error happened before";
_this.logReport(_this.m_listReports->failedMigrations(),msg);
//throw castor::exception::Exception(msg);
} else {
// This is an abnormal situation: we should never flush after an error!
_this.m_lc.log(LOG_ALERT,"Received a flush after an error: sending file errors to client");
reportFileErrors(_this);
}
//reset (ie delete and replace) the current m_listReports.
//Thus all current reports are deleted otherwise they would have been sent again at the next flush
......@@ -207,13 +234,24 @@ void MigrationReportPacker::ReportFlush::execute(MigrationReportPacker& _this){
void MigrationReportPacker::ReportEndofSession::execute(MigrationReportPacker& _this){
client::ClientInterface::RequestReport chrono;
if(!_this.m_errorHappened){
_this.m_lc.log(LOG_INFO,"Nominal EndofSession has been reported without incident on tape-writing");
_this.m_client.reportEndOfSession(chrono);
log::ScopedParamContainer sp(_this.m_lc);
sp.add("connectDuration", chrono.connectDuration)
.add("sendRecvDuration", chrono.sendRecvDuration)
.add("transactionId", chrono.transactionId);
_this.m_lc.log(LOG_INFO,"Reported end of session to client");
}
else {
const std::string& msg ="Nominal EndofSession has been reported but an writing error on the tape happened before";
_this.m_client.reportEndOfSessionWithError(msg,SEINTERNAL,chrono);
_this.m_lc.log(LOG_ERR,msg);
reportFileErrors(_this);
// We have some errors: report end of session as such to the client
_this.m_client.reportEndOfSessionWithError("Previous file errors",SEINTERNAL,chrono);
log::ScopedParamContainer sp(_this.m_lc);
sp.add("errorMessage", "Previous file errors")
.add("errorCode", SEINTERNAL)
.add("connectDuration", chrono.connectDuration)
.add("sendRecvDuration", chrono.sendRecvDuration)
.add("transactionId", chrono.transactionId);
_this.m_lc.log(LOG_ERR,"Reported end of session with error to client due to previous file errors");
}
_this.m_continue=false;
}
......@@ -224,14 +262,19 @@ void MigrationReportPacker::ReportEndofSessionWithErrors::execute(MigrationRepor
client::ClientInterface::RequestReport chrono;
if(_this.m_errorHappened) {
_this.m_client.reportEndOfSessionWithError(m_message,m_error_code,chrono);
_this.m_lc.log(LOG_ERR,m_message);
}
else{
const std::string& msg ="MigrationReportPacker::EndofSessionWithErrors has been reported but NO writing error on the tape was detected";
_this.m_client.reportEndOfSessionWithError(msg,SEINTERNAL,chrono);
_this.m_lc.log(LOG_ERR,msg);
//throw castor::exception::Exception(msg);
reportFileErrors(_this);
_this.m_client.reportEndOfSessionWithError(m_message,m_error_code,chrono);
log::ScopedParamContainer sp(_this.m_lc);
sp.add("errorMessage", m_message)
.add("errorCode", m_error_code)
.add("connectDuration", chrono.connectDuration)
.add("sendRecvDuration", chrono.sendRecvDuration)
.add("transactionId", chrono.transactionId);
_this.m_lc.log(LOG_INFO,"Reported end of session with error to client after sending file errors");
} else{
const std::string& msg ="Reported end of session with error to client";
_this.m_client.reportEndOfSessionWithError(msg,SEINTERNAL,chrono);
_this.m_lc.log(LOG_INFO,msg);
}
_this.m_continue=false;
}
......
......@@ -101,6 +101,14 @@ private:
public:
virtual ~Report(){}
virtual void execute(MigrationReportPacker& packer)=0;
protected:
/**
* Utility function to be shared by both ReportEndofSession and
* ReportEndofSessionWithErrors: if an error for a given file is
* recorded, we will transmit it to the client before signaling the
* end of the session.
*/
virtual void reportFileErrors(MigrationReportPacker& _this);
};
class ReportSuccessful : public Report {
const FileStruct m_migratedFile;
......
......@@ -58,7 +58,7 @@ TEST(castor_tape_tapeserver_daemon, MigrationReportPackerNominal) {
TEST(castor_tape_tapeserver_daemon, MigrationReportPackerFaillure) {
testing::StrictMock<MockClient> client;
::testing::InSequence dummy;
EXPECT_CALL(client, reportMigrationResults(_,_)).Times(0);
EXPECT_CALL(client, reportMigrationResults(_,_)).Times(1);
EXPECT_CALL(client, reportEndOfSessionWithError(error,-1,_)).Times(1);
castor::log::StringLogger log("castor_tape_tapeserver_daemon_MigrationReportPackerFaillure");
......@@ -78,13 +78,13 @@ TEST(castor_tape_tapeserver_daemon, MigrationReportPackerFaillure) {
mrp.waitThread();
std::string temp = log.getLog();
ASSERT_NE(std::string::npos, temp.find("A flush on tape has been reported but a writing error happened before"));
ASSERT_NE(std::string::npos, temp.find("Reported failed file(s) to the client before end of session"));
}
TEST(castor_tape_tapeserver_daemon, MigrationReportPackerFaillureGoodEnd) {
testing::StrictMock<MockClient> client;
::testing::InSequence dummy;
EXPECT_CALL(client, reportMigrationResults(_,_)).Times(0);
EXPECT_CALL(client, reportMigrationResults(_,_)).Times(1);
EXPECT_CALL(client, reportEndOfSessionWithError(_,SEINTERNAL,_)).Times(1);
castor::log::StringLogger log("castor_tape_tapeserver_daemon_MigrationReportPackerFaillureGoodEnd");
castor::log::LogContext lc(log);
......@@ -103,7 +103,7 @@ TEST(castor_tape_tapeserver_daemon, MigrationReportPackerFaillureGoodEnd) {
mrp.waitThread();
std::string temp = log.getLog();
ASSERT_NE(std::string::npos, temp.find("Nominal EndofSession has been reported but an writing error on the tape happened before"));
ASSERT_NE(std::string::npos, temp.find("Reported end of session with error to client due to previous file errors"));
}
......@@ -130,7 +130,7 @@ TEST(castor_tape_tapeserver_daemon, MigrationReportPackerGoodBadEnd) {
mrp.waitThread();
std::string temp = log.getLog();
ASSERT_NE(std::string::npos, temp.find("EndofSessionWithErrors has been reported but NO writing error on the tape was detected"));
ASSERT_NE(std::string::npos, temp.find("Reported end of session with error to client"));
}
MATCHER(checkCompressFileSize,"compressedFileSize is zero"){
......
......@@ -118,9 +118,8 @@ namespace daemon {
//------------------------------------------------------------------------------
void MigrationTaskInjector::requestInjection( bool lastCall) {
castor::server::MutexLocker ml(&m_producerProtection);
if(!m_errorFlag) {
m_queue.push(Request(m_maxFiles, m_maxBytes, lastCall));
}
m_queue.push(Request(m_maxFiles, m_maxBytes, lastCall));
}
//------------------------------------------------------------------------------
//synchronousInjection
......@@ -200,7 +199,7 @@ namespace daemon {
if(NULL==filesToMigrateList.get()){
if (req.lastCall) {
m_parent.m_lc.log(LOG_INFO,"No more file to migrate: triggering the end of session.\n");
m_parent.m_lc.log(LOG_INFO,"No more file to migrate: triggering the end of session.");
m_parent.signalEndDataMovement();
break;
} else {
......
......@@ -103,17 +103,35 @@ template <class PlaceHolder> class ReportPackerInterface{
using castor::log::Param;
for(typename C::const_iterator it=c.begin();it!=c.end();++it)
{
LogContext::ScopedParam sp[]={
LogContext::ScopedParam(m_lc, Param("NSFILEID",(*it)->fileid())),
LogContext::ScopedParam(m_lc, Param("NSFSEQ", (*it)->fseq())),
LogContext::ScopedParam(m_lc, Param("NSHOST", (*it)->nshost())),
LogContext::ScopedParam(m_lc, Param("NSFILETRANSACTIONID", (*it)->fileTransactionId()))
};
tape::utils::suppresUnusedVariable(sp);
log::ScopedParamContainer sp(m_lc);
sp.add("NSFILEID",(*it)->fileid())
.add("NSFSEQ", (*it)->fseq())
.add("NSHOST", (*it)->nshost())
.add("NSFILETRANSACTIONID", (*it)->fileTransactionId());
m_lc.log(LOG_INFO,msg);
}
}
/**
* Log a set of files independently of the success/failure
* @param c The set of files to log
* @param msg The message to be append at the end.
*/
template <class C> void logReportWithError(const C& c,const std::string& msg){
using castor::log::LogContext;
using castor::log::Param;
for(typename C::const_iterator it=c.begin();it!=c.end();++it)
{
log::ScopedParamContainer sp(m_lc);
sp.add("NSFILEID",(*it)->fileid())
.add("NSFSEQ", (*it)->fseq())
.add("NSHOST", (*it)->nshost())
.add("NSFILETRANSACTIONID", (*it)->fileTransactionId())
.add("ErrorMessage", (*it)->errorMessage())
.add("ErrorCode", (*it)->errorCode());
m_lc.log(LOG_INFO,msg);
}
}
/**
* Utility function used to log a ClientInterface::RequestReport
* @param chono the time report to log
......@@ -146,7 +164,7 @@ template <class PlaceHolder> class ReportPackerInterface{
/**
* m_listReports is holding all the report waiting to be processed
*/
std::auto_ptr<FileReportList> m_listReports;
std::auto_ptr<FileReportList> m_listReports;
/**
* Define how we should report to the client (by file/in bulk).
*/
......
......@@ -118,6 +118,7 @@ public:
localStats.dataVolume += mb->m_payload.size();
// Pass the block to the disk write task
m_fifo.pushDataBlock(mb);
mb=NULL;
watchdog.notify();
localStats.waitReportingTime += timer.secs(utils::Timer::resetCounter);
} //end of while(stillReading)
......
......@@ -203,7 +203,7 @@ void castor::tape::tapeserver::daemon::TapeWriteSingleThread::run() {
catch(const castor::exception::Exception& e){
//we end there because write session could not be opened
//or because a task failed or because flush failed
//first empty all the tasks and circulate mem blocks
while(1) {
std::auto_ptr<TapeWriteTask> task(m_tasks.pop());
......
......@@ -74,20 +74,11 @@ namespace daemon {
uint32_t memBlockId = 0;
try {
//we first check here to not even try to move the tape if a previous task has failed
//because the tape- could the very reason why the previous one failed,
//so dont do the same mistake twice !
hasAnotherTaskTailed();
//try to open the session
std::auto_ptr<castor::tape::tapeFile::WriteFile> output(openWriteFile(session,lc));
m_taskStats.transferTime += timer.secs(utils::Timer::resetCounter);
m_taskStats.headerVolume += TapeSessionStats::headerVolumePerFile;
while(!m_fifo.finished()) {
//if someone screw somewhere else, we stop
hasAnotherTaskTailed();
MemBlock* const mb = m_fifo.popDataBlock();
m_taskStats.waitDataTime += timer.secs(utils::Timer::resetCounter);
AutoReleaseBlock<MigrationMemoryManager> releaser(mb,m_memManager);
......@@ -118,14 +109,14 @@ namespace daemon {
localTime.secs(),lc);
}
catch(const castor::tape::tapeserver::daemon::ErrorFlag&){
//we end up there because another task has failed
//so we just log, circulate blocks and don't even send a report
// We end up there because another task has failed
// so we just log, circulate blocks and don't even send a report