diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp index ff57919511a3a487836af254eed30c63cba0f8d3..b6d16c23aef4bdcc6a5d9799b36ddc3c1b9acc06 100644 --- a/catalogue/CatalogueTest.cpp +++ b/catalogue/CatalogueTest.cpp @@ -10690,6 +10690,7 @@ TEST_P(cta_catalogue_CatalogueTest, filesWrittenToTape_1_archive_file_1_tape_cop ASSERT_EQ(1, tapes.size()); const common::dataStructures::Tape &tape = tapes.front(); ASSERT_EQ(1, tape.lastFSeq); + ASSERT_TRUE(tape.dirty); } { @@ -12503,7 +12504,11 @@ TEST_P(cta_catalogue_CatalogueTest, deleteArchiveFile) { log::LogContext dummyLc(m_dummyLog); m_catalogue->deleteArchiveFile("disk_instance", archiveFileId, dummyLc); - + + for(auto &tape:m_catalogue->getTapes()){ + ASSERT_TRUE(tape.dirty); + } + ASSERT_FALSE(m_catalogue->getArchiveFilesItor().hasMore()); } @@ -14617,6 +14622,7 @@ TEST_P(cta_catalogue_CatalogueTest, reclaimTape_full_lastFSeq_0_no_tape_files) { ASSERT_FALSE(tape.labelLog); ASSERT_FALSE(tape.lastReadLog); ASSERT_FALSE(tape.lastWriteLog); + ASSERT_TRUE(tape.dirty); const common::dataStructures::EntryLog creationLog = tape.creationLog; ASSERT_EQ(m_admin.username, creationLog.username); diff --git a/catalogue/MysqlCatalogue.cpp b/catalogue/MysqlCatalogue.cpp index edd101bdd79f6041a2d2e37c66cceb6d07f94531..554f0f5bec22215cab00902806370892cb744130 100644 --- a/catalogue/MysqlCatalogue.cpp +++ b/catalogue/MysqlCatalogue.cpp @@ -524,6 +524,12 @@ void MysqlCatalogue::deleteArchiveFile(const std::string &diskInstanceName, cons stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId); stmt.executeNonQuery(); } + + for(auto &tapeFile: archiveFile->tapeFiles){ + //We deleted the TAPE_FILE so the tapes containing them should be set as dirty + setTapeDirty(conn,tapeFile.vid); + } + const auto deleteFromTapeFileTime = t.secs(utils::Timer::resetCounter); { @@ -533,7 +539,6 @@ void MysqlCatalogue::deleteArchiveFile(const std::string &diskInstanceName, cons stmt.executeNonQuery(); } const auto deleteFromArchiveFileTime = t.secs(utils::Timer::resetCounter); - conn.commit(); const auto commitTime = t.secs(); diff --git a/catalogue/OracleCatalogue.cpp b/catalogue/OracleCatalogue.cpp index ec64025b2f958047f484f35eb6cac7e62e8add61..71cc86aaea7694dd0aeb39c406307368b05111d8 100644 --- a/catalogue/OracleCatalogue.cpp +++ b/catalogue/OracleCatalogue.cpp @@ -869,6 +869,12 @@ void OracleCatalogue::deleteArchiveFile(const std::string &diskInstanceName, con stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId); stmt.executeNonQuery(); } + + for(auto &tapeFile: archiveFile->tapeFiles){ + //We deleted the TAPE_FILE so the tapes containing them should be set as dirty + setTapeDirty(conn,tapeFile.vid); + } + const auto deleteFromTapeFileTime = t.secs(utils::Timer::resetCounter); { diff --git a/catalogue/PostgresCatalogue.cpp b/catalogue/PostgresCatalogue.cpp index f9d7bc49096843cc330c8680d345976f42daf057..aaa4762d9be31b22028451cc92ebf4b1142357bf 100644 --- a/catalogue/PostgresCatalogue.cpp +++ b/catalogue/PostgresCatalogue.cpp @@ -808,6 +808,12 @@ void PostgresCatalogue::deleteArchiveFile(const std::string &diskInstanceName, c stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId); stmt.executeNonQuery(); } + + for(auto &tapeFile: archiveFile->tapeFiles){ + //We deleted the TAPE_FILE so the tapes containing them should be set as dirty + setTapeDirty(conn,tapeFile.vid); + } + const auto deleteFromTapeFileTime = t.secs(utils::Timer::resetCounter); { diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp index 9464f340ff7035f7cb2cc9346a5892c3724f99a5..26c8f6aeafb28e5f15b0fe219b1d052c872ed040 100644 --- a/catalogue/RdbmsCatalogue.cpp +++ b/catalogue/RdbmsCatalogue.cpp @@ -2361,11 +2361,14 @@ std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes(rdbms::Conn &co "TAPE.ENCRYPTION_KEY_NAME AS ENCRYPTION_KEY_NAME," "TAPE.CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES," "TAPE.DATA_IN_BYTES AS DATA_IN_BYTES," + "TAPE.NB_MASTER_FILES AS NB_MASTER_FILES," + "TAPE.MASTER_DATA_IN_BYTES AS MASTER_DATA_IN_BYTES," "TAPE.LAST_FSEQ AS LAST_FSEQ," "TAPE.IS_DISABLED AS IS_DISABLED," "TAPE.IS_FULL AS IS_FULL," "TAPE.IS_READ_ONLY AS IS_READ_ONLY," "TAPE.IS_FROM_CASTOR AS IS_FROM_CASTOR," + "TAPE.DIRTY AS DIRTY," "TAPE.LABEL_DRIVE AS LABEL_DRIVE," "TAPE.LABEL_TIME AS LABEL_TIME," @@ -2517,12 +2520,15 @@ std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes(rdbms::Conn &co tape.encryptionKeyName = rset.columnOptionalString("ENCRYPTION_KEY_NAME"); tape.capacityInBytes = rset.columnUint64("CAPACITY_IN_BYTES"); tape.dataOnTapeInBytes = rset.columnUint64("DATA_IN_BYTES"); + tape.nbMasterFiles = rset.columnUint64("NB_MASTER_FILES"); + tape.masterDataInBytes = rset.columnUint64("MASTER_DATA_IN_BYTES"); tape.lastFSeq = rset.columnUint64("LAST_FSEQ"); tape.disabled = rset.columnBool("IS_DISABLED"); tape.full = rset.columnBool("IS_FULL"); tape.readOnly = rset.columnBool("IS_READ_ONLY"); tape.isFromCastor = rset.columnBool("IS_FROM_CASTOR"); - + tape.dirty = rset.columnBool("DIRTY"); + tape.labelLog = getTapeLogFromRset(rset, "LABEL_DRIVE", "LABEL_TIME"); tape.lastReadLog = getTapeLogFromRset(rset, "LAST_READ_DRIVE", "LAST_READ_TIME"); tape.lastWriteLog = getTapeLogFromRset(rset, "LAST_WRITE_DRIVE", "LAST_WRITE_TIME"); @@ -2857,6 +2863,20 @@ void RdbmsCatalogue::deleteTapeFiles(rdbms::Conn& conn, const std::string& vid) } } +void RdbmsCatalogue::setTapeDirty(rdbms::Conn& conn, const std::string& vid) const { + try { + const char * const sql = + "UPDATE TAPE SET DIRTY='1' WHERE VID = :VID"; + auto stmt = conn.createStmt(sql); + stmt.bindString(":VID", vid); + stmt.executeNonQuery(); + } catch(exception::Exception &ex) { + ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str()); + throw; + } +} + + void RdbmsCatalogue::resetTapeCounters(rdbms::Conn& conn, const common::dataStructures::SecurityIdentity& admin, const std::string& vid) const { try { const time_t now = time(nullptr); @@ -2867,7 +2887,8 @@ void RdbmsCatalogue::resetTapeCounters(rdbms::Conn& conn, const common::dataStru "IS_FULL = '0'," "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," - "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME," + "DIRTY = '1' " "WHERE " "VID = :VID"; auto stmt = conn.createStmt(sql); @@ -6112,7 +6133,8 @@ void RdbmsCatalogue::updateTape( "LAST_FSEQ = :LAST_FSEQ," "DATA_IN_BYTES = DATA_IN_BYTES + :DATA_IN_BYTES," "LAST_WRITE_DRIVE = :LAST_WRITE_DRIVE," - "LAST_WRITE_TIME = :LAST_WRITE_TIME " + "LAST_WRITE_TIME = :LAST_WRITE_TIME," + "DIRTY='1' " "WHERE " "VID = :VID"; auto stmt = conn.createStmt(sql); diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp index cd4863683ddef28e4661cba76a4325e5a298c7ab..961dc708333af9c523ede6687e0b8b5a4b89172a 100644 --- a/catalogue/RdbmsCatalogue.hpp +++ b/catalogue/RdbmsCatalogue.hpp @@ -425,6 +425,13 @@ public: */ void deleteTapeFiles(rdbms::Conn &conn, const std::string& vid) const; + /** + * Set the DIRTY flag to true + * @param conn the database connection + * @param vid the vid in which we want to set it as dirty + */ + void setTapeDirty(rdbms::Conn &conn, const std::string &vid) const; + /** * Reset the counters of a tape * @param conn the database connection diff --git a/catalogue/SqliteCatalogue.cpp b/catalogue/SqliteCatalogue.cpp index 5432b89552134b34fb58371bb1c24d3d6d109ffd..f468098f20c22d77602c25cc114dc5e02efb1268 100644 --- a/catalogue/SqliteCatalogue.cpp +++ b/catalogue/SqliteCatalogue.cpp @@ -127,6 +127,12 @@ void SqliteCatalogue::deleteArchiveFile(const std::string &diskInstanceName, con stmt.bindUint64(":ARCHIVE_FILE_ID", archiveFileId); stmt.executeNonQuery(); } + + for(auto &tapeFile: archiveFile->tapeFiles){ + //We deleted the TAPE_FILE so the tapes containing them should be set as dirty + setTapeDirty(conn,tapeFile.vid); + } + const auto deleteFromTapeFileTime = t.secs(utils::Timer::resetCounter); { diff --git a/common/dataStructures/Tape.cpp b/common/dataStructures/Tape.cpp index b8afb13c43e93d56f9ef8f74b17420502e796738..93dcb942932f18505a11ed3528153c74b2ec80c2 100644 --- a/common/dataStructures/Tape.cpp +++ b/common/dataStructures/Tape.cpp @@ -31,9 +31,12 @@ Tape::Tape(): lastFSeq(0), capacityInBytes(0), dataOnTapeInBytes(0), + nbMasterFiles(0), + masterDataInBytes(0), full(false), disabled(false), - readOnly(false) {} + readOnly(false) + {} //------------------------------------------------------------------------------ // operator== diff --git a/common/dataStructures/Tape.hpp b/common/dataStructures/Tape.hpp index fadecc15bcdcdf6deec1b67ab538ed211081351c..433df119aa0d2312e52218af14fc6ed894ddbcc3 100644 --- a/common/dataStructures/Tape.hpp +++ b/common/dataStructures/Tape.hpp @@ -51,6 +51,8 @@ struct Tape { std::string vo; uint64_t capacityInBytes; uint64_t dataOnTapeInBytes; + uint64_t nbMasterFiles; + uint64_t masterDataInBytes; /** * The optional name of the encryption key. @@ -63,7 +65,8 @@ struct Tape { bool full; bool disabled; bool readOnly; - bool isFromCastor; + bool isFromCastor; + bool dirty; uint64_t readMountCount; uint64_t writeMountCount; EntryLog creationLog; diff --git a/xroot_plugins/XrdCtaTapeLs.hpp b/xroot_plugins/XrdCtaTapeLs.hpp index f6455ee0845cd89c3fee3416b613650260d6db5d..ecb8723586b589f58928de2457fec959b153f772 100644 --- a/xroot_plugins/XrdCtaTapeLs.hpp +++ b/xroot_plugins/XrdCtaTapeLs.hpp @@ -114,7 +114,10 @@ int TapeLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) { tape_item->set_from_castor(tape.isFromCastor); tape_item->set_read_mount_count(tape.readMountCount); tape_item->set_write_mount_count(tape.writeMountCount); - + tape_item->set_nb_master_files(tape.nbMasterFiles); + tape_item->set_master_data_in_bytes(tape.masterDataInBytes); + tape_item->set_dirty(tape.dirty); + if(tape.labelLog) { ::cta::common::TapeLog * labelLog = tape_item->mutable_label_log(); labelLog->set_drive(tape.labelLog.value().drive);