/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2018 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#include
#include
#include
#include
#include
#include
#include "namespace/ns_quarkdb/persistency/MetadataFetcher.hh"
#include "namespace/ns_quarkdb/persistency/RequestBuilder.hh"
#include "namespace/ns_quarkdb/Constants.hh"
#include "namespace/ns_quarkdb/persistency/Serialization.hh"
#include "namespace/utils/LocalityHint.hh"
using qclient::redisReplyPtr;
void processFileBucket(qclient::QClient& qcl, uint64_t bucketId)
{
std::string bucketString = SSTR(bucketId << eos::constants::sFileKeySuffix);
std::cout << "Processing file bucket " << bucketString << std::endl;
redisReplyPtr len = qcl.exec("HLEN", bucketString).get();
if (len->type != REDIS_REPLY_INTEGER) {
std::cerr << "Received unexpected response: " << qclient::describeRedisReply(
len) << std::endl;
std::abort();
}
if (len->integer == 0u) {
std::cout << "--- Bucket is empty!" << std::endl;
} else {
std::cout << "--- Found " << len->integer << " items, converting..." <<
std::endl;
}
qclient::QHash bucket(qcl, bucketString);
size_t processed = 0;
for (auto it = bucket.getIterator(); it.valid(); it.next()) {
processed++;
std::cout << "Key: " << it.getKey() << std::endl;
std::string value = it.getValue();
eos::ns::FileMdProto fileProto;
eos::MDStatus status = eos::Serialization::deserialize(value.c_str(),
value.size(), fileProto);
if (!status.ok()) {
std::cout << "ERROR WHILE CONVERTING FileID " << it.getKey() <<
", COULD NOT PARSE" << std::endl;
std::abort();
}
std::string localityHint = eos::LocalityHint::build(eos::ContainerIdentifier(
fileProto.cont_id()), fileProto.name());
qcl.exec("CONVERT-HASH-FIELD-TO-LHASH", bucketString, it.getKey(),
eos::constants::sFileKey, it.getKey(), localityHint);
if (processed % 1024 == 0) {
std::cout << "Processed " << processed << std::endl;
}
}
}
void processContainerBucket(qclient::QClient& qcl, uint64_t bucketId)
{
std::string bucketString = SSTR(bucketId << eos::constants::sContKeySuffix);
std::cout << "Processing container bucket " << bucketString << std::endl;
redisReplyPtr len = qcl.exec("HLEN", bucketString).get();
if (len->type != REDIS_REPLY_INTEGER) {
std::cerr << "Received unexpected response: " << qclient::describeRedisReply(
len) << std::endl;
std::abort();
}
if (len->integer == 0u) {
std::cout << "--- Bucket is empty!" << std::endl;
} else {
std::cout << "--- Found " << len->integer << " items, converting..." <<
std::endl;
}
qclient::QHash bucket(qcl, bucketString);
size_t processed = 0;
for (auto it = bucket.getIterator(); it.valid(); it.next()) {
processed++;
std::cout << "Key: " << it.getKey() << std::endl;
std::string value = it.getValue();
eos::ns::ContainerMdProto containerProto;
eos::MDStatus status = eos::Serialization::deserialize(value.c_str(),
value.size(), containerProto);
if (!status.ok()) {
std::cout << "ERROR WHILE CONVERTING ContainerID " << it.getKey() <<
", COULD NOT PARSE" << std::endl;
std::abort();
}
std::string localityHint = eos::LocalityHint::build(eos::ContainerIdentifier(
containerProto.parent_id()), containerProto.name());
qcl.exec("CONVERT-HASH-FIELD-TO-LHASH", bucketString, it.getKey(),
eos::constants::sContainerKey, it.getKey(), localityHint);
if (processed % 1024 == 0) {
std::cout << "Processed " << processed << std::endl;
}
}
}
int main(int argc, char* argv[])
{
// TODO(gbitzes): Remove this tool eventually in a couple of months, when no
// mixed-layout instances remain
if (argc != 2) {
std::cerr <<
"This tools converts old EOS NS layouts using hash-buckets, to the new one using locality hashes."
<< std::endl;
std::cerr <<
"You most probably never need to run this tool. EOS instances created " <<
std::endl;
std::cerr << "after 18 May 2018 should have the new layout automatically." <<
std::endl;
std::cerr << "Usage: " << argv[0] <<
" "
<< std::endl;
exit(1);
}
qclient::Members members;
if (!members.parse(argv[1])) {
std::cerr << "Cannot parse cluster members." << std::endl;
exit(1);
}
const std::uint64_t sNumContBuckets = 128 * 1024;
const std::uint64_t sNumFileBuckets = 1024 * 1024;
qclient::Options opts;
opts.transparentRedirects = true;
opts.retryStrategy = qclient::RetryStrategy::WithTimeout(std::chrono::seconds(
20));
qclient::QClient qcl(members, std::move(opts));
for (uint64_t i = 0; i < sNumFileBuckets; i++) {
processFileBucket(qcl, i);
processFileBucket(qcl, i);
}
for (uint64_t i = 0; i < sNumContBuckets; i++) {
processContainerBucket(qcl, i);
processContainerBucket(qcl, i);
}
return 0;
}