You've already forked mariadb-columnstore-engine
mirror of
https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
synced 2025-12-13 23:02:14 +03:00
Got the synchronizer stuff to build.
This commit is contained in:
@@ -13,14 +13,32 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
namespace bf = boost::filesystem;
|
namespace bf = boost::filesystem;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
boost::mutex m;
|
||||||
|
storagemanager::Cache *inst = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
namespace storagemanager
|
namespace storagemanager
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Cache * Cache::get()
|
||||||
|
{
|
||||||
|
if (inst)
|
||||||
|
return inst;
|
||||||
|
boost::unique_lock<boost::mutex> s(m);
|
||||||
|
if (inst)
|
||||||
|
return inst;
|
||||||
|
inst = new Cache();
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
Cache::Cache() : currentCacheSize(0)
|
Cache::Cache() : currentCacheSize(0)
|
||||||
{
|
{
|
||||||
Config *conf = Config::get();
|
Config *conf = Config::get();
|
||||||
logger = SMLogging::get();
|
logger = SMLogging::get();
|
||||||
sync = Synchronizer::get();
|
sync = Synchronizer::get();
|
||||||
|
replicator = Replicator::get();
|
||||||
|
|
||||||
string stmp = conf->getValue("Cache", "cache_size");
|
string stmp = conf->getValue("Cache", "cache_size");
|
||||||
if (stmp.empty())
|
if (stmp.empty())
|
||||||
@@ -84,26 +102,28 @@ Cache::~Cache()
|
|||||||
void Cache::populate()
|
void Cache::populate()
|
||||||
{
|
{
|
||||||
bf::directory_iterator dir(prefix);
|
bf::directory_iterator dir(prefix);
|
||||||
bf::diretory_iterator dend;
|
bf::directory_iterator dend;
|
||||||
while (dir != dend)
|
while (dir != dend)
|
||||||
{
|
{
|
||||||
// put everything that doesn't end with '.journal' in lru & m_lru
|
// put everything that doesn't end with '.journal' in lru & m_lru
|
||||||
if (bf::is_regular_file(*dir))
|
const bf::path &p = dir->path();
|
||||||
|
if (bf::is_regular_file(p))
|
||||||
{
|
{
|
||||||
size_t size = bf::file_size(*dir);
|
size_t size = bf::file_size(*dir);
|
||||||
if (dir->extension() == "obj")
|
if (p.extension() == "") // need to decide whether objects should have an extension
|
||||||
{
|
{
|
||||||
lru.push_back(dir->string());
|
lru.push_back(p.string());
|
||||||
m_lru.insert(lru.end() - 1);
|
auto last = lru.end();
|
||||||
|
m_lru.insert(--last);
|
||||||
currentCacheSize += size;
|
currentCacheSize += size;
|
||||||
}
|
}
|
||||||
else if (dir->extension() == "journal")
|
else if (p.extension() == "journal")
|
||||||
currentCacheSize += size;
|
currentCacheSize += size;
|
||||||
else
|
else
|
||||||
logger->log(LOG_WARN, "Cache: found a file in the cache that does not belong '%s'", dir->string().c_str());
|
logger->log(LOG_WARNING, "Cache: found a file in the cache that does not belong '%s'", p.string().c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
logger->log(LOG_WARN, "Cache: found something in the cache that does not belong '%s'", dir->string().c_str());
|
logger->log(LOG_WARNING, "Cache: found something in the cache that does not belong '%s'", p.string().c_str());
|
||||||
++dir;
|
++dir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,7 +167,7 @@ void Cache::read(const vector<string> &keys)
|
|||||||
s.lock();
|
s.lock();
|
||||||
// do makespace() before downloading. Problem is, until the download is finished, this fcn can't tell which
|
// do makespace() before downloading. Problem is, until the download is finished, this fcn can't tell which
|
||||||
// downloads it was responsible for. Need Downloader to make the call...?
|
// downloads it was responsible for. Need Downloader to make the call...?
|
||||||
makeSpace(sum_sizes);
|
_makeSpace(sum_sizes);
|
||||||
currentCacheSize += sum_sizes;
|
currentCacheSize += sum_sizes;
|
||||||
|
|
||||||
// move all keys to the back of the LRU
|
// move all keys to the back of the LRU
|
||||||
@@ -222,14 +242,14 @@ void Cache::exists(const vector<string> &keys, vector<bool> *out)
|
|||||||
bool Cache::exists(const string &key)
|
bool Cache::exists(const string &key)
|
||||||
{
|
{
|
||||||
boost::unique_lock<boost::mutex> s(lru_mutex);
|
boost::unique_lock<boost::mutex> s(lru_mutex);
|
||||||
return m_lru.find(keys[i]) != m_lru.end();
|
return m_lru.find(key) != m_lru.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cache::newObject(const string &key, size_t size)
|
void Cache::newObject(const string &key, size_t size)
|
||||||
{
|
{
|
||||||
boost::unique_lock<boost::mutex> s(lru_mutex);
|
boost::unique_lock<boost::mutex> s(lru_mutex);
|
||||||
assert(m_lru.find(key) == m_lru.end());
|
assert(m_lru.find(key) == m_lru.end());
|
||||||
makeSpace(size);
|
_makeSpace(size);
|
||||||
lru.push_back(key);
|
lru.push_back(key);
|
||||||
LRU_t::iterator back = lru.end();
|
LRU_t::iterator back = lru.end();
|
||||||
m_lru.insert(--back);
|
m_lru.insert(--back);
|
||||||
@@ -239,7 +259,7 @@ void Cache::newObject(const string &key, size_t size)
|
|||||||
void Cache::newJournalEntry(size_t size)
|
void Cache::newJournalEntry(size_t size)
|
||||||
{
|
{
|
||||||
boost::unique_lock<boost::mutex> s(lru_mutex);
|
boost::unique_lock<boost::mutex> s(lru_mutex);
|
||||||
makeSpace(size);
|
_makeSpace(size);
|
||||||
currentCacheSize += size;
|
currentCacheSize += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,13 +282,20 @@ void Cache::deletedObject(const string &key, size_t size)
|
|||||||
|
|
||||||
void Cache::setMaxCacheSize(size_t size)
|
void Cache::setMaxCacheSize(size_t size)
|
||||||
{
|
{
|
||||||
|
boost::unique_lock<boost::mutex> s(lru_mutex);
|
||||||
if (size < maxCacheSize)
|
if (size < maxCacheSize)
|
||||||
makeSpace(maxCacheSize - size);
|
_makeSpace(maxCacheSize - size);
|
||||||
maxCacheSize = size;
|
maxCacheSize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// call this holding lru_mutex
|
|
||||||
void Cache::makeSpace(size_t size)
|
void Cache::makeSpace(size_t size)
|
||||||
|
{
|
||||||
|
boost::unique_lock<boost::mutex> s(lru_mutex);
|
||||||
|
_makeSpace(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// call this holding lru_mutex
|
||||||
|
void Cache::_makeSpace(size_t size)
|
||||||
{
|
{
|
||||||
ssize_t thisMuch = currentCacheSize + size - maxCacheSize;
|
ssize_t thisMuch = currentCacheSize + size - maxCacheSize;
|
||||||
if (thisMuch <= 0)
|
if (thisMuch <= 0)
|
||||||
@@ -303,7 +330,7 @@ void Cache::makeSpace(size_t size)
|
|||||||
currentCacheSize -= statbuf.st_size;
|
currentCacheSize -= statbuf.st_size;
|
||||||
thisMuch -= statbuf.st_size;
|
thisMuch -= statbuf.st_size;
|
||||||
sync->flushObject(*it);
|
sync->flushObject(*it);
|
||||||
replicator->delete(cachedFile, Replicator::LOCAL_ONLY);
|
replicator->remove(cachedFile.string().c_str(), Replicator::LOCAL_ONLY);
|
||||||
LRU_t::iterator toRemove = it++;
|
LRU_t::iterator toRemove = it++;
|
||||||
lru.erase(toRemove);
|
lru.erase(toRemove);
|
||||||
m_lru.erase(*toRemove);
|
m_lru.erase(*toRemove);
|
||||||
@@ -313,7 +340,7 @@ void Cache::makeSpace(size_t size)
|
|||||||
void Cache::rename(const string &oldKey, const string &newKey, ssize_t sizediff)
|
void Cache::rename(const string &oldKey, const string &newKey, ssize_t sizediff)
|
||||||
{
|
{
|
||||||
boost::unique_lock<boost::mutex> s(lru_mutex);
|
boost::unique_lock<boost::mutex> s(lru_mutex);
|
||||||
auto it = m_lru(oldKey);
|
auto it = m_lru.find(oldKey);
|
||||||
assert(it != m_lru.end());
|
assert(it != m_lru.end());
|
||||||
|
|
||||||
auto lit = it->lit;
|
auto lit = it->lit;
|
||||||
|
|||||||
11
src/Cache.h
11
src/Cache.h
@@ -5,6 +5,7 @@
|
|||||||
#include "Downloader.h"
|
#include "Downloader.h"
|
||||||
#include "SMLogging.h"
|
#include "SMLogging.h"
|
||||||
#include "Synchronizer.h"
|
#include "Synchronizer.h"
|
||||||
|
#include "Replicator.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -17,10 +18,12 @@
|
|||||||
namespace storagemanager
|
namespace storagemanager
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class Synchronizer;
|
||||||
|
|
||||||
class Cache : public boost::noncopyable
|
class Cache : public boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Cache();
|
static Cache *get();
|
||||||
virtual ~Cache();
|
virtual ~Cache();
|
||||||
|
|
||||||
void read(const std::vector<std::string> &keys);
|
void read(const std::vector<std::string> &keys);
|
||||||
@@ -35,21 +38,25 @@ class Cache : public boost::noncopyable
|
|||||||
// the size will change in that process; sizediff is by how much
|
// the size will change in that process; sizediff is by how much
|
||||||
void rename(const std::string &oldKey, const std::string &newKey, ssize_t sizediff);
|
void rename(const std::string &oldKey, const std::string &newKey, ssize_t sizediff);
|
||||||
void setMaxCacheSize(size_t size);
|
void setMaxCacheSize(size_t size);
|
||||||
|
void makeSpace(size_t size);
|
||||||
size_t getCurrentCacheSize() const;
|
size_t getCurrentCacheSize() const;
|
||||||
|
|
||||||
// test helpers
|
// test helpers
|
||||||
const boost::filesystem::path &getCachePath();
|
const boost::filesystem::path &getCachePath();
|
||||||
private:
|
private:
|
||||||
|
Cache();
|
||||||
|
|
||||||
boost::filesystem::path prefix;
|
boost::filesystem::path prefix;
|
||||||
size_t maxCacheSize;
|
size_t maxCacheSize;
|
||||||
size_t objectSize;
|
size_t objectSize;
|
||||||
size_t currentCacheSize;
|
size_t currentCacheSize;
|
||||||
Downloader downloader;
|
Downloader downloader;
|
||||||
|
Replicator *replicator;
|
||||||
Synchronizer *sync;
|
Synchronizer *sync;
|
||||||
SMLogging *logger;
|
SMLogging *logger;
|
||||||
|
|
||||||
void populate();
|
void populate();
|
||||||
void makeSpace(size_t size);
|
void _makeSpace(size_t size);
|
||||||
|
|
||||||
/* The main cache structures */
|
/* The main cache structures */
|
||||||
// lru owns the string memory for the filenames it manages. m_lru and DNE point to those strings.
|
// lru owns the string memory for the filenames it manages. m_lru and DNE point to those strings.
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ string tolower(const string &s)
|
|||||||
|
|
||||||
namespace storagemanager
|
namespace storagemanager
|
||||||
{
|
{
|
||||||
|
|
||||||
CloudStorage * CloudStorage::get()
|
CloudStorage * CloudStorage::get()
|
||||||
{
|
{
|
||||||
if (inst)
|
if (inst)
|
||||||
@@ -50,4 +51,9 @@ CloudStorage * CloudStorage::get()
|
|||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CloudStorage::CloudStorage()
|
||||||
|
{
|
||||||
|
logger = SMLogging::get();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <boost/shared_array.hpp>
|
#include <boost/shared_array.hpp>
|
||||||
|
#include "SMLogging.h"
|
||||||
|
|
||||||
namespace storagemanager
|
namespace storagemanager
|
||||||
{
|
{
|
||||||
@@ -23,7 +24,13 @@ class CloudStorage
|
|||||||
|
|
||||||
// this will return a CloudStorage instance of the type specified in StorageManager.cnf
|
// this will return a CloudStorage instance of the type specified in StorageManager.cnf
|
||||||
static CloudStorage *get();
|
static CloudStorage *get();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SMLogging *logger;
|
||||||
|
CloudStorage();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/property_tree/json_parser.hpp>
|
#include <boost/property_tree/json_parser.hpp>
|
||||||
@@ -336,7 +337,7 @@ boost::shared_array<uint8_t> IOCoordinator::mergeJournal(const char *object, con
|
|||||||
boost::property_tree::json_parser::read_json(ss, header);
|
boost::property_tree::json_parser::read_json(ss, header);
|
||||||
assert(header.get<string>("version") == "1");
|
assert(header.get<string>("version") == "1");
|
||||||
string stmp = header.get<string>("max_offset");
|
string stmp = header.get<string>("max_offset");
|
||||||
size_t maxJournalOffset = strtoul(stmp);
|
size_t maxJournalOffset = strtoul(stmp.c_str(), NULL, 0);
|
||||||
|
|
||||||
struct stat objStat;
|
struct stat objStat;
|
||||||
fstat(objFD, &objStat);
|
fstat(objFD, &objStat);
|
||||||
@@ -368,7 +369,7 @@ boost::shared_array<uint8_t> IOCoordinator::mergeJournal(const char *object, con
|
|||||||
// at the EOF of the object. The journal may contain entries that append to the data,
|
// at the EOF of the object. The journal may contain entries that append to the data,
|
||||||
// so 0-fill the remaining bytes.
|
// so 0-fill the remaining bytes.
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
memset(&ret[count], 0, len-count);
|
memset(&ret[count], 0, *len-count);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -385,7 +386,7 @@ boost::shared_array<uint8_t> IOCoordinator::mergeJournal(const char *object, con
|
|||||||
|
|
||||||
// if this entry overlaps, read the overlapping section
|
// if this entry overlaps, read the overlapping section
|
||||||
uint64_t lastJournalOffset = offlen[0] + offlen[1];
|
uint64_t lastJournalOffset = offlen[0] + offlen[1];
|
||||||
uint64_t lastBufOffset = offset + len;
|
uint64_t lastBufOffset = offset + *len;
|
||||||
if (offlen[0] <= lastBufOffset && lastJournalOffset >= offset)
|
if (offlen[0] <= lastBufOffset && lastJournalOffset >= offset)
|
||||||
{
|
{
|
||||||
uint64_t startReadingAt = max(offlen[0], offset);
|
uint64_t startReadingAt = max(offlen[0], offset);
|
||||||
@@ -441,7 +442,7 @@ int IOCoordinator::mergeJournalInMem(boost::shared_array<uint8_t> &objData, size
|
|||||||
boost::property_tree::json_parser::read_json(ss, header);
|
boost::property_tree::json_parser::read_json(ss, header);
|
||||||
assert(header.get<string>("version") == "1");
|
assert(header.get<string>("version") == "1");
|
||||||
string stmp = header.get<string>("max_offset");
|
string stmp = header.get<string>("max_offset");
|
||||||
size_t maxJournalOffset = strtoul(stmp);
|
size_t maxJournalOffset = strtoul(stmp.c_str(), NULL, 0);
|
||||||
|
|
||||||
if (maxJournalOffset > *len)
|
if (maxJournalOffset > *len)
|
||||||
{
|
{
|
||||||
@@ -487,6 +488,12 @@ int IOCoordinator::mergeJournalInMem(boost::shared_array<uint8_t> &objData, size
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IOCoordinator::renameObject(const string &oldKey, const string &newKey)
|
||||||
|
{
|
||||||
|
// does anything need to be done here?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool IOCoordinator::readLock(const string &filename)
|
bool IOCoordinator::readLock(const string &filename)
|
||||||
{
|
{
|
||||||
boost::unique_lock<boost::mutex> s(lockMutex);
|
boost::unique_lock<boost::mutex> s(lockMutex);
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ class IOCoordinator : public boost::noncopyable
|
|||||||
int mergeJournalInMem(boost::shared_array<uint8_t> &objData, size_t *len, const char *journalPath);
|
int mergeJournalInMem(boost::shared_array<uint8_t> &objData, size_t *len, const char *journalPath);
|
||||||
|
|
||||||
/* Lock manipulation fcns. They can lock on any param given to them. */
|
/* Lock manipulation fcns. They can lock on any param given to them. */
|
||||||
|
void renameObject(const std::string &oldKey, const std::string &newKey);
|
||||||
bool readLock(const std::string &filename);
|
bool readLock(const std::string &filename);
|
||||||
bool writeLock(const std::string &filename);
|
bool writeLock(const std::string &filename);
|
||||||
void readUnlock(const std::string &filename);
|
void readUnlock(const std::string &filename);
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <syslog.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include "LocalStorage.h"
|
#include "LocalStorage.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
@@ -24,11 +26,10 @@ LocalStorage::LocalStorage()
|
|||||||
}
|
}
|
||||||
catch (exception &e)
|
catch (exception &e)
|
||||||
{
|
{
|
||||||
syslog(LOG_CRIT, "Failed to create %s, got: %s", prefix.string().c_str(), e.what());
|
logger->log(LOG_CRIT, "Failed to create %s, got: %s", prefix.string().c_str(), e.what());
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger = SMLogging::get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalStorage::~LocalStorage()
|
LocalStorage::~LocalStorage()
|
||||||
@@ -40,10 +41,10 @@ const bf::path & LocalStorage::getPrefix() const
|
|||||||
return prefix;
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LocalStorage::copy(const path &source, const path &dest)
|
int LocalStorage::copy(const bf::path &source, const bf::path &dest)
|
||||||
{
|
{
|
||||||
boost::system::error_code err;
|
boost::system::error_code err;
|
||||||
bf::copy_file(source, dest, copy_option::fail_if_exists, err);
|
bf::copy_file(source, dest, bf::copy_option::fail_if_exists, err);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
errno = err.value();
|
errno = err.value();
|
||||||
@@ -78,13 +79,13 @@ int LocalStorage::getObject(const std::string &sourceKey, boost::shared_array<ui
|
|||||||
data.reset(new uint8_t[l_size]);
|
data.reset(new uint8_t[l_size]);
|
||||||
char buf[80];
|
char buf[80];
|
||||||
|
|
||||||
int fd = open(c_source, O_RDONLY);
|
int fd = ::open(c_source, O_RDONLY);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
{
|
{
|
||||||
logger->log(LOG_CRIT, "LocalStorage::getObject() failed to open %s, got '%s'", c_source, strerror_r(errno, buf, 80));
|
logger->log(LOG_CRIT, "LocalStorage::getObject() failed to open %s, got '%s'", c_source, strerror_r(errno, buf, 80));
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
scoped_closer s(fd);
|
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
while (count < l_size)
|
while (count < l_size)
|
||||||
{
|
{
|
||||||
@@ -92,12 +93,14 @@ int LocalStorage::getObject(const std::string &sourceKey, boost::shared_array<ui
|
|||||||
if (err < 0)
|
if (err < 0)
|
||||||
{
|
{
|
||||||
logger->log(LOG_CRIT, "LocalStorage::getObject() failed to read %s, got '%s'", c_source, strerror_r(errno, buf, 80));
|
logger->log(LOG_CRIT, "LocalStorage::getObject() failed to read %s, got '%s'", c_source, strerror_r(errno, buf, 80));
|
||||||
|
close(fd);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
count += err;
|
count += err;
|
||||||
}
|
}
|
||||||
if (size)
|
if (size)
|
||||||
*size = l_size;
|
*size = l_size;
|
||||||
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,10 +118,10 @@ int LocalStorage::putObject(boost::shared_array<uint8_t> data, size_t len, const
|
|||||||
int fd = ::open(c_dest, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
int fd = ::open(c_dest, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
{
|
{
|
||||||
logger->log("LocalStorage::putObject(): Failed to open %s, got '%s'", c_dest, strerror_r(errno, buf, 80));
|
logger->log(LOG_CRIT, "LocalStorage::putObject(): Failed to open %s, got '%s'", c_dest, strerror_r(errno, buf, 80));
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
scoped_closer s(fd);
|
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
int err;
|
int err;
|
||||||
while (count < len)
|
while (count < len)
|
||||||
@@ -126,11 +129,13 @@ int LocalStorage::putObject(boost::shared_array<uint8_t> data, size_t len, const
|
|||||||
err = ::write(fd, &data[count], len - count);
|
err = ::write(fd, &data[count], len - count);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
{
|
{
|
||||||
logger->log("LocalStorage::putObject(): Failed to write to %s, got '%s'", c_dest, strerror_r(errno, buf, 80));
|
logger->log(LOG_CRIT, "LocalStorage::putObject(): Failed to write to %s, got '%s'", c_dest, strerror_r(errno, buf, 80));
|
||||||
|
close(fd);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
count += err;
|
count += err;
|
||||||
}
|
}
|
||||||
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "CloudStorage.h"
|
#include "CloudStorage.h"
|
||||||
|
#include "SMLogging.h"
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
namespace storagemanager
|
namespace storagemanager
|
||||||
@@ -26,7 +27,6 @@ class LocalStorage : public CloudStorage
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
boost::filesystem::path prefix;
|
boost::filesystem::path prefix;
|
||||||
SMLogging *logger;
|
|
||||||
|
|
||||||
int copy(const boost::filesystem::path &sourceKey, const boost::filesystem::path &destKey);
|
int copy(const boost::filesystem::path &sourceKey, const boost::filesystem::path &destKey);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ int Replicator::addJournalEntry(const char *filename, const uint8_t *data, off_t
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Replicator::remove(const char *filename, uint8_t flags)
|
int Replicator::remove(const char *filename, Flags flags)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
boost::filesystem::path p(filename);
|
boost::filesystem::path p(filename);
|
||||||
@@ -151,4 +151,9 @@ int Replicator::remove(const char *filename, uint8_t flags)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Replicator::updateMetadata(const char *filename, const MetadataFile &meta)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define REPLICATOR_H_
|
#define REPLICATOR_H_
|
||||||
|
|
||||||
//#include "ThreadPool.h"
|
//#include "ThreadPool.h"
|
||||||
|
#include "MetadataFile.h"
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
@@ -18,10 +19,18 @@ class Replicator
|
|||||||
static Replicator *get();
|
static Replicator *get();
|
||||||
virtual ~Replicator();
|
virtual ~Replicator();
|
||||||
|
|
||||||
|
enum Flags
|
||||||
|
{
|
||||||
|
NONE = 0,
|
||||||
|
LOCAL_ONLY = 0x1,
|
||||||
|
NO_LOCAL = 0x2
|
||||||
|
};
|
||||||
|
|
||||||
int addJournalEntry(const char *filename, const uint8_t *data, off_t offset, size_t length);
|
int addJournalEntry(const char *filename, const uint8_t *data, off_t offset, size_t length);
|
||||||
int newObject(const char *filename, const uint8_t *data, size_t length);
|
int newObject(const char *filename, const uint8_t *data, size_t length);
|
||||||
int remove(const char *key ,uint8_t flags);
|
int remove(const char *key, Flags flags = NONE);
|
||||||
|
|
||||||
|
int updateMetadata(const char *filename, const MetadataFile &meta);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Replicator();
|
Replicator();
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ int S3Storage::getObject(const string &sourceKey, const string &destFile, size_t
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int S3Storage::getObject(const string &sourceKey, boost::shared_array<uint8_t> &data, size_t *size = NULL)
|
int S3Storage::getObject(const string &sourceKey, boost::shared_array<uint8_t> &data, size_t *size)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ int S3Storage::putObject(const string &sourceFile, const string &destKey)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int S3Storage::putObject(boost::shared_array<uint8_t> data, uint len, const string &destKey)
|
int S3Storage::putObject(const boost::shared_array<uint8_t> data, size_t len, const string &destKey)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class S3Storage : public CloudStorage
|
|||||||
int getObject(const std::string &sourceKey, const std::string &destFile, size_t *size = NULL);
|
int getObject(const std::string &sourceKey, const std::string &destFile, size_t *size = NULL);
|
||||||
int getObject(const std::string &sourceKey, boost::shared_array<uint8_t> &data, size_t *size = NULL);
|
int getObject(const std::string &sourceKey, boost::shared_array<uint8_t> &data, size_t *size = NULL);
|
||||||
int putObject(const std::string &sourceFile, const std::string &destKey);
|
int putObject(const std::string &sourceFile, const std::string &destKey);
|
||||||
int putObject(boost::shared_array<uint8_t> data, uint len, const std::string &destKey);
|
int putObject(const boost::shared_array<uint8_t> data, size_t len, const std::string &destKey);
|
||||||
void deleteObject(const std::string &key);
|
void deleteObject(const std::string &key);
|
||||||
int copyObject(const std::string &sourceKey, const std::string &destKey);
|
int copyObject(const std::string &sourceKey, const std::string &destKey);
|
||||||
int exists(const std::string &key, bool *out);
|
int exists(const std::string &key, bool *out);
|
||||||
|
|||||||
@@ -1,14 +1,69 @@
|
|||||||
|
|
||||||
#include "Synchronizer.h"
|
#include "Synchronizer.h"
|
||||||
#include "Metadatafile.h"
|
#include "MetadataFile.h"
|
||||||
#include <boost/thread/mutex.hpp>
|
#include <boost/thread/mutex.hpp>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
storagemanager::Synchronizer *instance = NULL;
|
storagemanager::Synchronizer *instance = NULL;
|
||||||
boost::mutex inst_mutex;
|
boost::mutex inst_mutex;
|
||||||
|
|
||||||
|
// a few utility classes. Maybe move these to a utilities header.
|
||||||
|
struct ScopedReadLock
|
||||||
|
{
|
||||||
|
ScopedReadLock(storagemanager::IOCoordinator *i, const string &k) : ioc(i), key(k)
|
||||||
|
{
|
||||||
|
ioc->readLock(key);
|
||||||
|
}
|
||||||
|
~ScopedReadLock()
|
||||||
|
{
|
||||||
|
ioc->readUnlock(key);
|
||||||
|
}
|
||||||
|
storagemanager::IOCoordinator *ioc;
|
||||||
|
const string key;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScopedWriteLock
|
||||||
|
{
|
||||||
|
ScopedWriteLock(storagemanager::IOCoordinator *i, const string &k) : ioc(i), key(k)
|
||||||
|
{
|
||||||
|
ioc->writeLock(key);
|
||||||
|
locked = true;
|
||||||
|
}
|
||||||
|
~ScopedWriteLock()
|
||||||
|
{
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock()
|
||||||
|
{
|
||||||
|
if (locked)
|
||||||
|
{
|
||||||
|
ioc->writeUnlock(key);
|
||||||
|
locked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
storagemanager::IOCoordinator *ioc;
|
||||||
|
bool locked;
|
||||||
|
const string key;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScopedCloser {
|
||||||
|
ScopedCloser(int f) : fd(f) { }
|
||||||
|
~ScopedCloser() {
|
||||||
|
int s_errno = errno;
|
||||||
|
::close(fd);
|
||||||
|
errno = s_errno;
|
||||||
|
}
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace bf = boost::filesystem;
|
namespace bf = boost::filesystem;
|
||||||
@@ -33,8 +88,9 @@ Synchronizer::Synchronizer() : maxUploads(0)
|
|||||||
cache = Cache::get();
|
cache = Cache::get();
|
||||||
replicator = Replicator::get();
|
replicator = Replicator::get();
|
||||||
ioc = IOCoordinator::get();
|
ioc = IOCoordinator::get();
|
||||||
|
cs = CloudStorage::get();
|
||||||
|
|
||||||
string stmp = config->getValue("ObjectStorage", "max_concurrent_uploads")
|
string stmp = config->getValue("ObjectStorage", "max_concurrent_uploads");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
maxUploads = stoul(stmp);
|
maxUploads = stoul(stmp);
|
||||||
@@ -47,21 +103,21 @@ Synchronizer::Synchronizer() : maxUploads(0)
|
|||||||
maxUploads = 20;
|
maxUploads = 20;
|
||||||
|
|
||||||
stmp = config->getValue("ObjectStorage", "journal_path");
|
stmp = config->getValue("ObjectStorage", "journal_path");
|
||||||
if (prefix.empty())
|
if (stmp.empty())
|
||||||
{
|
{
|
||||||
logger->log(LOG_CRIT, "ObjectStorage/journal_path is not set");
|
logger->log(LOG_CRIT, "ObjectStorage/journal_path is not set");
|
||||||
throw runtime_error("Please set ObjectStorage/journal_path in the storagemanager.cnf file");
|
throw runtime_error("Please set ObjectStorage/journal_path in the storagemanager.cnf file");
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bf::create_directories(stmp);
|
journalPath = stmp;
|
||||||
|
bf::create_directories(journalPath);
|
||||||
}
|
}
|
||||||
catch (exception &e)
|
catch (exception &e)
|
||||||
{
|
{
|
||||||
syslog(LOG_CRIT, "Failed to create %s, got: %s", stmp.string().c_str(), e.what());
|
logger->log(LOG_CRIT, "Failed to create %s, got: %s", stmp.c_str(), e.what());
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
journalPath = stmp;
|
|
||||||
cachePath = cache->getCachePath();
|
cachePath = cache->getCachePath();
|
||||||
threadPool.setMaxThreads(maxUploads);
|
threadPool.setMaxThreads(maxUploads);
|
||||||
}
|
}
|
||||||
@@ -100,7 +156,7 @@ void Synchronizer::newObjects(const vector<string> &keys)
|
|||||||
{
|
{
|
||||||
boost::unique_lock<boost::mutex> s(mutex);
|
boost::unique_lock<boost::mutex> s(mutex);
|
||||||
|
|
||||||
for (string &key : keys)
|
for (const string &key : keys)
|
||||||
{
|
{
|
||||||
assert(pendingOps.find(key) == pendingOps.end());
|
assert(pendingOps.find(key) == pendingOps.end());
|
||||||
makeJob(key);
|
makeJob(key);
|
||||||
@@ -112,7 +168,7 @@ void Synchronizer::deletedObjects(const vector<string> &keys)
|
|||||||
{
|
{
|
||||||
boost::unique_lock<boost::mutex> s(mutex);
|
boost::unique_lock<boost::mutex> s(mutex);
|
||||||
|
|
||||||
for (string &key : keys)
|
for (const string &key : keys)
|
||||||
{
|
{
|
||||||
auto it = pendingOps.find(key);
|
auto it = pendingOps.find(key);
|
||||||
if (it != pendingOps.end())
|
if (it != pendingOps.end())
|
||||||
@@ -127,30 +183,85 @@ void Synchronizer::deletedObjects(const vector<string> &keys)
|
|||||||
|
|
||||||
void Synchronizer::flushObject(const string &key)
|
void Synchronizer::flushObject(const string &key)
|
||||||
{
|
{
|
||||||
process(key);
|
boost::unique_lock<boost::mutex> s(mutex);
|
||||||
|
|
||||||
|
// if there is something to do on key, it should be in the objNames list
|
||||||
|
// and either in pendingOps or opsInProgress.
|
||||||
|
// in testing though, going to check whether there is something to do
|
||||||
|
|
||||||
|
bool noExistingJob = false;
|
||||||
|
auto it = pendingOps.find(key);
|
||||||
|
if (it != pendingOps.end())
|
||||||
|
// find the object name and call process()
|
||||||
|
for (auto name = objNames.begin(); name != objNames.end(); ++it)
|
||||||
|
if (*name == key)
|
||||||
|
{
|
||||||
|
process(name, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto op = opsInProgress.find(key);
|
||||||
|
// it's already in progress
|
||||||
|
if (op != opsInProgress.end())
|
||||||
|
op->second->wait(&mutex);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// it's not in either one, check if there is anything to be done as
|
||||||
|
// a sanity check.
|
||||||
|
noExistingJob = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noExistingJob)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check whether this key is in cloud storage
|
||||||
|
bool exists;
|
||||||
|
int err;
|
||||||
|
do {
|
||||||
|
err = cs->exists(key.c_str(), &exists);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
char buf[80];
|
||||||
|
logger->log(LOG_CRIT, "Sync::flushObject(): cloud existence check failed, got '%s'", strerror_r(errno, buf, 80));
|
||||||
|
sleep(5);
|
||||||
|
}
|
||||||
|
} while (err);
|
||||||
|
if (!exists)
|
||||||
|
{
|
||||||
|
logger->log(LOG_DEBUG, "Sync::flushObject(): broken assumption! %s does not exist in cloud storage, but there is no job for it. Uploading it now.");
|
||||||
|
pendingOps[key] = boost::shared_ptr<PendingOps>(new PendingOps(NEW_OBJECT));
|
||||||
|
objNames.push_front(key);
|
||||||
|
process(objNames.begin(), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synchronizer::makeJob(const string &key)
|
void Synchronizer::makeJob(const string &key)
|
||||||
{
|
{
|
||||||
boost::shared_ptr<string> s(new string(key));
|
objNames.push_front(key);
|
||||||
names.push_front(s);
|
|
||||||
|
|
||||||
boost::shared_ptr<Job> j(new Job(this, names.begin()));
|
boost::shared_ptr<Job> j(new Job(this, objNames.begin()));
|
||||||
threadPool.addJob(j);
|
threadPool.addJob(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synchronizer::process(list<string>::iterator &name)
|
void Synchronizer::process(list<string>::iterator name, bool use_lock)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
check if there is a pendingOp for *it
|
check if there is a pendingOp for name
|
||||||
if yes, start processing it
|
if yes, start processing it
|
||||||
if no,
|
if no,
|
||||||
check if there is an ongoing op and block on it
|
check if there is an ongoing op and block on it
|
||||||
if not, return
|
if not, return
|
||||||
*/
|
*/
|
||||||
|
|
||||||
boost::unique_lock<boost::mutex> s(mutex);
|
// had to use this 'use_lock' kludge to let flush() start processing a job immediately
|
||||||
|
boost::unique_lock<boost::mutex> s(mutex, boost::defer_lock);
|
||||||
|
|
||||||
|
if (use_lock)
|
||||||
|
s.lock();
|
||||||
|
|
||||||
|
string &key = *name;
|
||||||
auto it = pendingOps.find(key);
|
auto it = pendingOps.find(key);
|
||||||
if (it == pendingOps.end())
|
if (it == pendingOps.end())
|
||||||
{
|
{
|
||||||
@@ -161,22 +272,27 @@ void Synchronizer::process(list<string>::iterator &name)
|
|||||||
op->second->wait(&mutex);
|
op->second->wait(&mutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
// it's not in pending or opsinprogress, nothing to do
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::shared_ptr<PendingOps> pending = it->second;
|
boost::shared_ptr<PendingOps> pending = it->second;
|
||||||
opsInProgress[key] = *it;
|
opsInProgress[key] = pending;
|
||||||
pendingOps.erase(it);
|
pendingOps.erase(it);
|
||||||
string sourceFile = Metadata::getSourceFilenameFromKey(*name);
|
string sourceFile = MetadataFile::getSourceFromKey(*name);
|
||||||
s.unlock();
|
s.unlock();
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
while (!success)
|
while (!success)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
/* Exceptions should only happen b/c of cloud service errors. Rather than retry here endlessly,
|
||||||
|
probably a better idea to have cloudstorage classes do the retrying */
|
||||||
if (pending->opFlags & DELETE)
|
if (pending->opFlags & DELETE)
|
||||||
synchronizeDelete(sourceFile, name);
|
synchronizeDelete(sourceFile, name);
|
||||||
else if (pending->opFlags & JOURNAL)
|
else if (pending->opFlags & JOURNAL)
|
||||||
synchronizerWithJournal(sourceFile, name);
|
synchronizeWithJournal(sourceFile, name);
|
||||||
else if (pending->opFlags & NEW_OBJECT)
|
else if (pending->opFlags & NEW_OBJECT)
|
||||||
synchronize(sourceFile, name);
|
synchronize(sourceFile, name);
|
||||||
else
|
else
|
||||||
@@ -194,53 +310,10 @@ void Synchronizer::process(list<string>::iterator &name)
|
|||||||
|
|
||||||
s.lock();
|
s.lock();
|
||||||
opsInProgress.erase(key);
|
opsInProgress.erase(key);
|
||||||
names.erase(name);
|
objNames.erase(name);
|
||||||
|
|
||||||
// TBD: On a network outage or S3 outage, it might not be a bad idea to keep retrying
|
|
||||||
// until the end of time. This will (?) naturally make the system unusable until the blockage
|
|
||||||
// is cleared, which is what we want, right? Is there a way to nicely tell the user what
|
|
||||||
// is happening, or are the logs good enough?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ScopedReadLock
|
|
||||||
{
|
|
||||||
ScopedReadLock(IOCoordinator *i, string &key)
|
|
||||||
{
|
|
||||||
ioc = i;
|
|
||||||
ioc->readLock(key.c_str());
|
|
||||||
}
|
|
||||||
~ScopedReadLock()
|
|
||||||
|
|
||||||
ioc->readUnlock(key.c_str());
|
|
||||||
}
|
|
||||||
IOCoordinator *ioc;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ScopedWriteLock
|
|
||||||
{
|
|
||||||
ScopedWriteLock(IOCoordinator *i, string &key)
|
|
||||||
{
|
|
||||||
ioc = i;
|
|
||||||
ioc->writeLock(key.c_str());
|
|
||||||
locked = true;
|
|
||||||
}
|
|
||||||
~ScopedReadLock()
|
|
||||||
{
|
|
||||||
if (locked)
|
|
||||||
ioc->writeUnlock(key.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void unlock()
|
|
||||||
{
|
|
||||||
if (locked)
|
|
||||||
{
|
|
||||||
ioc->writeUnlock(key.c_str());
|
|
||||||
locked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
IOCoordinator *ioc;
|
|
||||||
bool locked;
|
|
||||||
};
|
|
||||||
|
|
||||||
void Synchronizer::synchronize(const string &sourceFile, list<string>::iterator &it)
|
void Synchronizer::synchronize(const string &sourceFile, list<string>::iterator &it)
|
||||||
{
|
{
|
||||||
@@ -266,21 +339,20 @@ void Synchronizer::synchronize(const string &sourceFile, list<string>::iterator
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cs->putObject(cachePath / key, key);
|
err = cs->putObject((cachePath / key).string(), key);
|
||||||
if (err)
|
if (err)
|
||||||
throw runtime_error(string("synchronize(): uploading ") + key + ", got " + strerror_r(errno, buf, 80));
|
throw runtime_error(string("synchronize(): uploading ") + key + ", got " + strerror_r(errno, buf, 80));
|
||||||
replicator->delete(key, Replicator::NO_LOCAL);
|
replicator->remove(key.c_str(), Replicator::NO_LOCAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synchronizer::synchronizeDelete(const string &sourceFile, list<string>::iterator &it)
|
void Synchronizer::synchronizeDelete(const string &sourceFile, list<string>::iterator &it)
|
||||||
{
|
{
|
||||||
ScopedWriteLock s(ioc, sourceFile);
|
ScopedWriteLock s(ioc, sourceFile);
|
||||||
cs->delete(*it);
|
cs->deleteObject(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synchronizer::synchronizeWithJournal(const string &sourceFile, list<string>::iterator &lit)
|
void Synchronizer::synchronizeWithJournal(const string &sourceFile, list<string>::iterator &lit)
|
||||||
{
|
{
|
||||||
// interface to Metadata TBD
|
|
||||||
ScopedWriteLock s(ioc, sourceFile);
|
ScopedWriteLock s(ioc, sourceFile);
|
||||||
|
|
||||||
string &key = *lit;
|
string &key = *lit;
|
||||||
@@ -289,7 +361,7 @@ void Synchronizer::synchronizeWithJournal(const string &sourceFile, list<string>
|
|||||||
|
|
||||||
if (!bf::exists(journalName))
|
if (!bf::exists(journalName))
|
||||||
{
|
{
|
||||||
logger->(LOG_WARNING, "synchronizeWithJournal(): no journal file found for %s", key.c_str());
|
logger->log(LOG_WARNING, "synchronizeWithJournal(): no journal file found for %s", key.c_str());
|
||||||
// I don't think this should happen, maybe throw a logic_error here
|
// I don't think this should happen, maybe throw a logic_error here
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -307,15 +379,15 @@ void Synchronizer::synchronizeWithJournal(const string &sourceFile, list<string>
|
|||||||
err = cs->getObject(key, data, &size);
|
err = cs->getObject(key, data, &size);
|
||||||
if (err)
|
if (err)
|
||||||
throw runtime_error(string("Synchronizer: getObject() failed: ") + strerror_r(errno, buf, 80));
|
throw runtime_error(string("Synchronizer: getObject() failed: ") + strerror_r(errno, buf, 80));
|
||||||
err = ios->mergeJournalInMem(data, journalName, &size);
|
err = ioc->mergeJournalInMem(data, &size, journalName.c_str());
|
||||||
assert(!err);
|
assert(!err);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
data = ios->mergeJournal(oldCachePath.string(), journalName, 0, &size);
|
data = ioc->mergeJournal(oldCachePath.string().c_str(), journalName.c_str(), 0, &size);
|
||||||
assert(data);
|
assert(data);
|
||||||
|
|
||||||
// get a new key for the resolved version & upload it
|
// get a new key for the resolved version & upload it
|
||||||
string newKey = ioc->newKeyFromOldKey(key);
|
string newKey = MetadataFile::getNewKeyFromOldKey(key, size);
|
||||||
err = cs->putObject(data, size, newKey);
|
err = cs->putObject(data, size, newKey);
|
||||||
if (err)
|
if (err)
|
||||||
throw runtime_error(string("Synchronizer: putObject() failed: ") + strerror_r(errno, buf, 80));
|
throw runtime_error(string("Synchronizer: putObject() failed: ") + strerror_r(errno, buf, 80));
|
||||||
@@ -346,24 +418,23 @@ void Synchronizer::synchronizeWithJournal(const string &sourceFile, list<string>
|
|||||||
}
|
}
|
||||||
|
|
||||||
cache->rename(key, newKey, size - bf::file_size(oldCachePath));
|
cache->rename(key, newKey, size - bf::file_size(oldCachePath));
|
||||||
replicator->delete(key);
|
replicator->remove(key.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the metadata for the source file
|
// update the metadata for the source file
|
||||||
// waiting for stubs to see what these calls look like
|
|
||||||
/*
|
MetadataFile md(sourceFile.c_str());
|
||||||
Metadata md(sourceFilename);
|
md.updateEntry(MetadataFile::getOffsetFromKey(key), newKey, size);
|
||||||
md.rename(key, newKey);
|
replicator->updateMetadata(sourceFile.c_str(), md);
|
||||||
replicator->updateMetadata(sourceFilename, md);
|
|
||||||
*/
|
|
||||||
rename(key, newKey);
|
rename(key, newKey);
|
||||||
ioc->renameObject(oldkey, newkey);
|
ioc->renameObject(key, newKey);
|
||||||
s.unlock();
|
s.unlock();
|
||||||
|
|
||||||
// delete the old object & journal file
|
// delete the old object & journal file
|
||||||
cache->deletedJournal(bf::file_size(journalName);
|
cache->deletedJournal(bf::file_size(journalName));
|
||||||
replicator->delete(journalName);
|
replicator->remove(journalName.c_str());
|
||||||
cs->delete(key);
|
cs->deleteObject(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synchronizer::rename(const string &oldKey, const string &newKey)
|
void Synchronizer::rename(const string &oldKey, const string &newKey)
|
||||||
@@ -377,8 +448,8 @@ void Synchronizer::rename(const string &oldKey, const string &newKey)
|
|||||||
pendingOps.erase(it);
|
pendingOps.erase(it);
|
||||||
|
|
||||||
for (auto &name: objNames)
|
for (auto &name: objNames)
|
||||||
if (*name == oldKey)
|
if (name == oldKey)
|
||||||
*name = newKey;
|
name = newKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
bf::path Synchronizer::getJournalPath()
|
bf::path Synchronizer::getJournalPath()
|
||||||
@@ -393,22 +464,18 @@ bf::path Synchronizer::getCachePath()
|
|||||||
|
|
||||||
/* The helper objects & fcns */
|
/* The helper objects & fcns */
|
||||||
|
|
||||||
Synchronizer::PendingOps(int flags) : opFlags(flags), finished(false)
|
Synchronizer::PendingOps::PendingOps(int flags) : opFlags(flags), finished(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Synchronizer::~PendingOps()
|
void Synchronizer::PendingOps::notify(boost::mutex *m)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Synchronizer::PendingOps::notify(boost::mutex *m)
|
|
||||||
{
|
{
|
||||||
boost::unique_lock<boost::mutex> s(*m);
|
boost::unique_lock<boost::mutex> s(*m);
|
||||||
finished = true;
|
finished = true;
|
||||||
condvar.notify_all();
|
condvar.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
Synchronizer::PendingOps::wait(boost::mutex *m)
|
void Synchronizer::PendingOps::wait(boost::mutex *m)
|
||||||
{
|
{
|
||||||
while (!finished)
|
while (!finished)
|
||||||
condvar.wait(*m);
|
condvar.wait(*m);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <boost/utility.hpp>
|
#include <boost/utility.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
#include "SMLogging.h"
|
#include "SMLogging.h"
|
||||||
#include "Cache.h"
|
#include "Cache.h"
|
||||||
@@ -16,6 +17,8 @@
|
|||||||
namespace storagemanager
|
namespace storagemanager
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class Cache;
|
||||||
|
|
||||||
/* TODO: Need to think about how errors are handled / propagated */
|
/* TODO: Need to think about how errors are handled / propagated */
|
||||||
class Synchronizer : public boost::noncopyable
|
class Synchronizer : public boost::noncopyable
|
||||||
{
|
{
|
||||||
@@ -36,10 +39,10 @@ class Synchronizer : public boost::noncopyable
|
|||||||
private:
|
private:
|
||||||
Synchronizer();
|
Synchronizer();
|
||||||
|
|
||||||
void process(const std::string &key);
|
void process(std::list<std::string>::iterator key, bool use_lock=true);
|
||||||
void synchronize(const std::string &key, bool isFlush);
|
void synchronize(const std::string &sourceFile, std::list<std::string>::iterator &it);
|
||||||
void synchronizeDelete(const std::string &key);
|
void synchronizeDelete(const std::string &sourceFile, std::list<std::string>::iterator &it);
|
||||||
void synchronizeWithJournal(const std::string &key, bool isFlush);
|
void synchronizeWithJournal(const std::string &sourceFile, std::list<std::string>::iterator &it);
|
||||||
void rename(const std::string &oldkey, const std::string &newkey);
|
void rename(const std::string &oldkey, const std::string &newkey);
|
||||||
void makeJob(const std::string &key);
|
void makeJob(const std::string &key);
|
||||||
|
|
||||||
@@ -56,12 +59,13 @@ class Synchronizer : public boost::noncopyable
|
|||||||
|
|
||||||
struct Job : public ThreadPool::Job
|
struct Job : public ThreadPool::Job
|
||||||
{
|
{
|
||||||
Job(Synchronizer *s, std::list<std::string>::iterator &i) : sync(s), it(i) { }
|
Job(Synchronizer *s, std::list<std::string>::iterator i) : sync(s), it(i) { }
|
||||||
void operator()() { sync->process(it); }
|
void operator()() { sync->process(it); }
|
||||||
Synchronizer *sync;
|
Synchronizer *sync;
|
||||||
std::list<std::string>::iterator it;
|
std::list<std::string>::iterator it;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint maxUploads;
|
||||||
ThreadPool threadPool;
|
ThreadPool threadPool;
|
||||||
std::map<std::string, boost::shared_ptr<PendingOps> > pendingOps;
|
std::map<std::string, boost::shared_ptr<PendingOps> > pendingOps;
|
||||||
std::map<std::string, boost::shared_ptr<PendingOps> > opsInProgress;
|
std::map<std::string, boost::shared_ptr<PendingOps> > opsInProgress;
|
||||||
@@ -76,6 +80,7 @@ class Synchronizer : public boost::noncopyable
|
|||||||
Cache *cache;
|
Cache *cache;
|
||||||
Replicator *replicator;
|
Replicator *replicator;
|
||||||
IOCoordinator *ioc;
|
IOCoordinator *ioc;
|
||||||
|
CloudStorage *cs;
|
||||||
|
|
||||||
boost::filesystem::path cachePath;
|
boost::filesystem::path cachePath;
|
||||||
boost::filesystem::path journalPath;
|
boost::filesystem::path journalPath;
|
||||||
|
|||||||
@@ -177,8 +177,8 @@ bool replicatorTest()
|
|||||||
cout << "replicator addJournalEntry OK" << endl;
|
cout << "replicator addJournalEntry OK" << endl;
|
||||||
::close(fd);
|
::close(fd);
|
||||||
|
|
||||||
repli->remove(newobject,0);
|
repli->remove(newobject);
|
||||||
repli->remove(newobjectJournal,0);
|
repli->remove(newobjectJournal);
|
||||||
assert(!boost::filesystem::exists(newobject));
|
assert(!boost::filesystem::exists(newobject));
|
||||||
cout << "replicator remove OK" << endl;
|
cout << "replicator remove OK" << endl;
|
||||||
return true;
|
return true;
|
||||||
@@ -535,7 +535,7 @@ bool localstorageTest1()
|
|||||||
bool cacheTest1()
|
bool cacheTest1()
|
||||||
{
|
{
|
||||||
|
|
||||||
Cache cache;
|
Cache *cache = Cache::get();
|
||||||
CloudStorage *cs = CloudStorage::get();
|
CloudStorage *cs = CloudStorage::get();
|
||||||
LocalStorage *ls = dynamic_cast<LocalStorage *>(cs);
|
LocalStorage *ls = dynamic_cast<LocalStorage *>(cs);
|
||||||
if (ls == NULL) {
|
if (ls == NULL) {
|
||||||
@@ -544,15 +544,15 @@ bool cacheTest1()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bf::path storagePath = ls->getPrefix();
|
bf::path storagePath = ls->getPrefix();
|
||||||
bf::path cachePath = cache.getCachePath();
|
bf::path cachePath = cache->getCachePath();
|
||||||
vector<string> v_bogus;
|
vector<string> v_bogus;
|
||||||
vector<bool> exists;
|
vector<bool> exists;
|
||||||
|
|
||||||
// make sure nothing shows up in the cache path for files that don't exist
|
// make sure nothing shows up in the cache path for files that don't exist
|
||||||
v_bogus.push_back("does-not-exist");
|
v_bogus.push_back("does-not-exist");
|
||||||
cache.read(v_bogus);
|
cache->read(v_bogus);
|
||||||
assert(!bf::exists(cachePath / "does-not-exist"));
|
assert(!bf::exists(cachePath / "does-not-exist"));
|
||||||
cache.exists(v_bogus, &exists);
|
cache->exists(v_bogus, &exists);
|
||||||
assert(exists.size() == 1);
|
assert(exists.size() == 1);
|
||||||
assert(!exists[0]);
|
assert(!exists[0]);
|
||||||
|
|
||||||
@@ -560,21 +560,21 @@ bool cacheTest1()
|
|||||||
string realFile("storagemanager.cnf");
|
string realFile("storagemanager.cnf");
|
||||||
bf::copy_file(realFile, storagePath / realFile, bf::copy_option::overwrite_if_exists);
|
bf::copy_file(realFile, storagePath / realFile, bf::copy_option::overwrite_if_exists);
|
||||||
v_bogus[0] = realFile;
|
v_bogus[0] = realFile;
|
||||||
cache.read(v_bogus);
|
cache->read(v_bogus);
|
||||||
assert(bf::exists(cachePath / realFile));
|
assert(bf::exists(cachePath / realFile));
|
||||||
exists.clear();
|
exists.clear();
|
||||||
cache.exists(v_bogus, &exists);
|
cache->exists(v_bogus, &exists);
|
||||||
assert(exists.size() == 1);
|
assert(exists.size() == 1);
|
||||||
assert(exists[0]);
|
assert(exists[0]);
|
||||||
ssize_t currentSize = cache.getCurrentCacheSize();
|
ssize_t currentSize = cache->getCurrentCacheSize();
|
||||||
assert(currentSize == bf::file_size(cachePath / realFile));
|
assert(currentSize == bf::file_size(cachePath / realFile));
|
||||||
|
|
||||||
// lie about the file being deleted and then replaced
|
// lie about the file being deleted and then replaced
|
||||||
cache.deletedObject(realFile, currentSize);
|
cache->deletedObject(realFile, currentSize);
|
||||||
assert(cache.getCurrentCacheSize() == 0);
|
assert(cache->getCurrentCacheSize() == 0);
|
||||||
cache.newObject(realFile, currentSize);
|
cache->newObject(realFile, currentSize);
|
||||||
assert(cache.getCurrentCacheSize() == currentSize);
|
assert(cache->getCurrentCacheSize() == currentSize);
|
||||||
cache.exists(v_bogus, &exists);
|
cache->exists(v_bogus, &exists);
|
||||||
assert(exists.size() == 1);
|
assert(exists.size() == 1);
|
||||||
assert(exists[0]);
|
assert(exists[0]);
|
||||||
|
|
||||||
@@ -584,6 +584,33 @@ bool cacheTest1()
|
|||||||
cout << "cache test 1 OK" << endl;
|
cout << "cache test 1 OK" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void makeTestObject()
|
||||||
|
{
|
||||||
|
int objFD = open("test-object", O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||||
|
assert(objFD >= 0);
|
||||||
|
scoped_closer s1(objFD);
|
||||||
|
|
||||||
|
for (int i = 0; i < 2048; i++)
|
||||||
|
assert(write(objFD, &i, 4) == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the merged version should look like
|
||||||
|
// (ints) 0 1 2 3 4 0 1 2 3 4 10 11 12 13...
|
||||||
|
void makeTestJournal()
|
||||||
|
{
|
||||||
|
int journalFD = open("test-journal", O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||||
|
assert(journalFD >= 0);
|
||||||
|
scoped_closer s2(journalFD);
|
||||||
|
|
||||||
|
char header[] = "{ \"version\" : 1, \"max_offset\": 39 }";
|
||||||
|
write(journalFD, header, strlen(header) + 1);
|
||||||
|
|
||||||
|
uint64_t offlen[2] = { 20, 20 };
|
||||||
|
write(journalFD, offlen, 16);
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
assert(write(journalFD, &i, 4) == 4);
|
||||||
|
}
|
||||||
|
|
||||||
bool mergeJournalTest()
|
bool mergeJournalTest()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -592,30 +619,13 @@ bool mergeJournalTest()
|
|||||||
verify the expected values
|
verify the expected values
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int objFD = open("test-object", O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
makeTestObject();
|
||||||
assert(objFD >= 0);
|
makeTestJournal();
|
||||||
scoped_closer s1(objFD);
|
|
||||||
int journalFD = open("test-journal", O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
|
||||||
assert(journalFD >= 0);
|
|
||||||
scoped_closer s2(journalFD);
|
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < 2048; i++)
|
|
||||||
assert(write(objFD, &i, 4) == 4);
|
|
||||||
|
|
||||||
char header[] = "{ \"version\" : 1 }";
|
|
||||||
write(journalFD, header, strlen(header) + 1);
|
|
||||||
|
|
||||||
uint64_t offlen[2] = { 20, 20 };
|
|
||||||
write(journalFD, offlen, 16);
|
|
||||||
for (i = 0; i < 5; i++)
|
|
||||||
assert(write(journalFD, &i, 4) == 4);
|
|
||||||
|
|
||||||
// the merged version should look like
|
|
||||||
// (ints) 0 1 2 3 4 0 1 2 3 4 10 11 12 13...
|
|
||||||
|
|
||||||
IOCoordinator *ioc = IOCoordinator::get();
|
IOCoordinator *ioc = IOCoordinator::get();
|
||||||
boost::shared_array<uint8_t> data = ioc->mergeJournal("test-object", "test-journal");
|
size_t len = 0;
|
||||||
|
boost::shared_array<uint8_t> data = ioc->mergeJournal("test-object", "test-journal", 0, &len);
|
||||||
assert(data);
|
assert(data);
|
||||||
int *idata = (int *) data.get();
|
int *idata = (int *) data.get();
|
||||||
for (i = 0; i < 5; i++)
|
for (i = 0; i < 5; i++)
|
||||||
@@ -627,7 +637,8 @@ bool mergeJournalTest()
|
|||||||
|
|
||||||
// try different range parameters
|
// try different range parameters
|
||||||
// read at the beginning of the change
|
// read at the beginning of the change
|
||||||
data = ioc->mergeJournal("test-object", "test-journal", 20, 40);
|
len = 40;
|
||||||
|
data = ioc->mergeJournal("test-object", "test-journal", 20, &len);
|
||||||
assert(data);
|
assert(data);
|
||||||
idata = (int *) data.get();
|
idata = (int *) data.get();
|
||||||
for (i = 0; i < 5; i++)
|
for (i = 0; i < 5; i++)
|
||||||
@@ -636,7 +647,8 @@ bool mergeJournalTest()
|
|||||||
assert(idata[i] == i+5);
|
assert(idata[i] == i+5);
|
||||||
|
|
||||||
// read s.t. beginning of the change is in the middle of the range
|
// read s.t. beginning of the change is in the middle of the range
|
||||||
data = ioc->mergeJournal("test-object", "test-journal", 8, 24);
|
len = 24;
|
||||||
|
data = ioc->mergeJournal("test-object", "test-journal", 8, &len);
|
||||||
assert(data);
|
assert(data);
|
||||||
idata = (int *) data.get();
|
idata = (int *) data.get();
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++)
|
||||||
@@ -645,7 +657,8 @@ bool mergeJournalTest()
|
|||||||
assert(idata[i] == i - 3);
|
assert(idata[i] == i - 3);
|
||||||
|
|
||||||
// read s.t. end of the change is in the middle of the range
|
// read s.t. end of the change is in the middle of the range
|
||||||
data = ioc->mergeJournal("test-object", "test-journal", 28, 20);
|
len = 20;
|
||||||
|
data = ioc->mergeJournal("test-object", "test-journal", 28, &len);
|
||||||
assert(data);
|
assert(data);
|
||||||
idata = (int *) data.get();
|
idata = (int *) data.get();
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++)
|
||||||
@@ -662,7 +675,7 @@ bool syncTest1()
|
|||||||
{
|
{
|
||||||
Synchronizer *sync = Synchronizer::get();
|
Synchronizer *sync = Synchronizer::get();
|
||||||
Cache *cache = Cache::get();
|
Cache *cache = Cache::get();
|
||||||
CloudStorage *cs = CloudStorage.get();
|
CloudStorage *cs = CloudStorage::get();
|
||||||
bf::path cachePath = sync->getCachePath();
|
bf::path cachePath = sync->getCachePath();
|
||||||
bf::path journalPath = sync->getJournalPath();
|
bf::path journalPath = sync->getJournalPath();
|
||||||
|
|
||||||
@@ -689,16 +702,13 @@ bool syncTest1()
|
|||||||
sleep(1); // let it do what it does
|
sleep(1); // let it do what it does
|
||||||
|
|
||||||
// check that the original objects no longer exist
|
// check that the original objects no longer exist
|
||||||
assert(!cache->exists("test-object.obj");
|
assert(!cache->exists("test-object.obj"));
|
||||||
assert(!bf::exists(cachePath
|
//working here assert(!bf::exists(cachePath
|
||||||
|
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
bf::remove(cachePath / "test-object");
|
bf::remove(cachePath / "test-object.obj");
|
||||||
bf::remove(cachePath / "test-journal");
|
bf::remove(journalPath / "test-object.journal");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
|
|||||||
Reference in New Issue
Block a user