const int LFNIndex::FILENAME_PREFIX_LEN = FILENAME_SHORT_LEN - FILENAME_HASH_LEN -
FILENAME_COOKIE.size() -
FILENAME_EXTRA;
+void LFNIndex::maybe_inject_failure() {
+ if (error_injection_enabled) {
+ if (current_failure > last_failure &&
+ (((double)(rand() % 10000))/((double)(10000))
+ < error_injection_probability)) {
+ last_failure = current_failure;
+ current_failure = 0;
+ throw RetryException();
+ }
+ ++current_failure;
+ }
+}
/* Public methods */
}
int LFNIndex::unlink(const hobject_t &hoid) {
+ WRAP_RETRY(
vector<string> path;
string short_name;
- int r;
r = _lookup(hoid, &path, &short_name, NULL);
- if (r < 0)
- return r;
+ if (r < 0) {
+ goto out;
+ }
r = _remove(path, hoid, short_name);
- if (r < 0)
- return r;
- return 0;
+ if (r < 0) {
+ goto out;
+ }
+ );
}
int LFNIndex::lookup(const hobject_t &hoid,
IndexedPath *out_path,
int *exist) {
+ WRAP_RETRY(
vector<string> path;
string short_name;
- int r;
r = _lookup(hoid, &path, &short_name, exist);
if (r < 0)
- return r;
+ goto out;
string full_path = get_full_path(path, short_name);
struct stat buf;
+ maybe_inject_failure();
r = ::stat(full_path.c_str(), &buf);
+ maybe_inject_failure();
if (r < 0) {
if (errno == ENOENT) {
*exist = 0;
} else {
- return -errno;
+ r = -errno;
+ goto out;
}
} else {
*exist = 1;
}
*out_path = IndexedPath(new Path(full_path, self_ref));
- return 0;
+ r = 0;
+ );
}
int LFNIndex::collection_list(vector<hobject_t> *ls) {
/* Derived class utility methods */
int LFNIndex::fsync_dir(const vector<string> &path) {
+ maybe_inject_failure();
int fd = ::open(get_full_path_subdir(path).c_str(), O_RDONLY);
if (fd < 0)
return -errno;
+ maybe_inject_failure();
int r = ::fsync(fd);
TEMP_FAILURE_RETRY(::close(fd));
+ maybe_inject_failure();
if (r < 0)
return -errno;
else
int r;
string from_path = get_full_path(from, from_short_name);
string to_path;
+ maybe_inject_failure();
r = lfn_get_name(to, hoid, 0, &to_path, 0);
if (r < 0)
return r;
+ maybe_inject_failure();
r = ::link(from_path.c_str(), to_path.c_str());
+ maybe_inject_failure();
if (r < 0)
return -errno;
else
to_clean != to_remove.end();
++to_clean) {
if (!lfn_is_hashed_filename(to_clean->first)) {
+ maybe_inject_failure();
int r = ::unlink(get_full_path(dir, to_clean->first).c_str());
+ maybe_inject_failure();
if (r < 0)
return -errno;
continue;
if (candidate == chain.rend() || *i > candidate->first) {
string remove_path_name =
get_full_path(dir, lfn_get_short_name(to_clean->second, *i));
+ maybe_inject_failure();
int r = ::unlink(remove_path_name.c_str());
+ maybe_inject_failure();
if (r < 0)
return -errno;
continue;
}
string from = get_full_path(dir, candidate->second.first);
string to = get_full_path(dir, lfn_get_short_name(candidate->second.second, *i));
+ maybe_inject_failure();
int r = ::rename(from.c_str(), to.c_str());
+ maybe_inject_failure();
if (r < 0)
return -errno;
remaining->erase(candidate->second.first);
r = lfn_get_name(to, i->second, &to_name, &to_path, 0);
if (r < 0)
return r;
+ maybe_inject_failure();
r = ::link(from_path.c_str(), to_path.c_str());
if (r < 0 && errno != EEXIST)
return -errno;
+ maybe_inject_failure();
r = lfn_created(to, i->second, to_name);
+ maybe_inject_failure();
if (r < 0)
return r;
}
for (map<string,hobject_t>::iterator i = to_move.begin();
i != to_move.end();
++i) {
+ maybe_inject_failure();
r = ::unlink(get_full_path(from, i->first).c_str());
+ maybe_inject_failure();
if (r < 0)
return -errno;
}
const hobject_t &hoid) {
string short_name;
int r, exist;
+ maybe_inject_failure();
r = get_mangled_name(from, hoid, &short_name, &exist);
+ maybe_inject_failure();
if (r < 0)
return r;
return lfn_unlink(from, hoid, short_name);
}
int LFNIndex::create_path(const vector<string> &to_create) {
+ maybe_inject_failure();
int r = ::mkdir(get_full_path_subdir(to_create).c_str(), 0777);
+ maybe_inject_failure();
if (r < 0)
return -errno;
else
}
int LFNIndex::remove_path(const vector<string> &to_remove) {
+ maybe_inject_failure();
int r = ::rmdir(get_full_path_subdir(to_remove).c_str());
+ maybe_inject_failure();
if (r < 0)
return -errno;
else
const string &attr_name,
bufferlist &attr_value) {
string full_path = get_full_path_subdir(path);
+ maybe_inject_failure();
return chain_setxattr(full_path.c_str(), mangle_attr_name(attr_name).c_str(),
reinterpret_cast<void *>(attr_value.c_str()),
attr_value.length());
const string &attr_name) {
string full_path = get_full_path_subdir(path);
string mangled_attr_name = mangle_attr_name(attr_name);
+ maybe_inject_failure();
return chain_removexattr(full_path.c_str(), mangled_attr_name.c_str());
}
if (exists) {
struct stat buf;
string full_path = get_full_path(path, full_name);
+ maybe_inject_failure();
r = ::stat(full_path.c_str(), &buf);
if (r < 0) {
if (errno == ENOENT)
return -errno;
if (errno == ENODATA) {
// Left over from incomplete transaction, it'll be replayed
+ maybe_inject_failure();
r = ::unlink(candidate_path.c_str());
+ maybe_inject_failure();
if (r < 0)
return -errno;
}
return 0;
string full_path = get_full_path(path, mangled_name);
string full_name = lfn_generate_object_name(hoid);
+ maybe_inject_failure();
return chain_setxattr(full_path.c_str(), get_lfn_attr().c_str(),
full_name.c_str(), full_name.size());
}
const string &mangled_name) {
if (!lfn_is_hashed_filename(mangled_name)) {
string full_path = get_full_path(path, mangled_name);
+ maybe_inject_failure();
int r = ::unlink(full_path.c_str());
+ maybe_inject_failure();
if (r < 0)
return -errno;
return 0;
}
if (i == removed_index + 1) {
string full_path = get_full_path(path, mangled_name);
+ maybe_inject_failure();
int r = ::unlink(full_path.c_str());
+ maybe_inject_failure();
if (r < 0)
return -errno;
else
} else {
string rename_to = get_full_path(path, mangled_name);
string rename_from = get_full_path(path, lfn_get_short_name(hoid, i - 1));
+ maybe_inject_failure();
int r = ::rename(rename_from.c_str(), rename_to.c_str());
+ maybe_inject_failure();
if (r < 0)
return -errno;
else
#include <set>
#include <vector>
#include <tr1/memory>
+#include <exception>
#include "osd/osd_types.h"
#include "include/object.h"
* Unless otherwise noted, methods which return an int return 0 on sucess
* and a negative error code on failure.
*/
+#define WRAP_RETRY(x) { \
+ bool failed = false; \
+ int r = 0; \
+ init_inject_failure(); \
+ while (1) { \
+ try { \
+ if (failed) { \
+ r = cleanup(); \
+ assert(r == 0); \
+ } \
+ { x } \
+ out: \
+ complete_inject_failure(); \
+ return r; \
+ } catch (RetryException) { \
+ failed = true; \
+ } catch (...) { \
+ assert(0); \
+ } \
+ } \
+ return -1; \
+ } \
+
+
+
class LFNIndex : public CollectionIndex {
/// Hash digest output size.
static const int FILENAME_LFN_DIGEST_SIZE = CEPH_CRYPTO_SHA1_DIGESTSIZE;
protected:
const uint32_t index_version;
+ /// true if retry injection is enabled
+ struct RetryException : public exception {};
+ bool error_injection_enabled;
+ bool error_injection_on;
+ double error_injection_probability;
+ uint64_t last_failure;
+ uint64_t current_failure;
+ void init_inject_failure() {
+ if (error_injection_on) {
+ error_injection_enabled = true;
+ last_failure = current_failure = 0;
+ }
+ }
+ void maybe_inject_failure();
+ void complete_inject_failure() {
+ error_injection_enabled = false;
+ }
+
private:
string lfn_attribute;
coll_t collection;
LFNIndex(
coll_t collection,
const char *base_path, ///< [in] path to Index root
- uint32_t index_version)
- : base_path(base_path), index_version(index_version),
+ uint32_t index_version,
+ double _error_injection_probability=0)
+ : base_path(base_path),
+ index_version(index_version),
+ error_injection_enabled(false),
+ error_injection_on(_error_injection_probability != 0),
+ error_injection_probability(_error_injection_probability),
+ last_failure(0), current_failure(0),
collection(collection) {
if (index_version == HASH_INDEX_TAG) {
lfn_attribute = LFN_ATTR;
hobject_t *next
);
+ virtual int _split(
+ uint32_t match, //< [in] value to match
+ uint32_t bits, //< [in] bits to check
+ std::tr1::shared_ptr<CollectionIndex> dest //< [in] destination index
+ ) = 0;
+
+ /// @see CollectionIndex
+ int split(
+ uint32_t match,
+ uint32_t bits,
+ std::tr1::shared_ptr<CollectionIndex> dest
+ ) {
+ WRAP_RETRY(
+ r = _split(match, bits, dest);
+ goto out;
+ );
+ }
+
+
protected:
virtual int _init() = 0;
r = store->apply_transaction(t);
ASSERT_EQ(r, 0);
}
+
+ ObjectStore::Transaction t;
vector<hobject_t> objects;
r = store->collection_list(cid, objects);
ASSERT_EQ(r, 0);
i != objects.end();
++i) {
ASSERT_EQ(!(i->hash & (1<<common_suffix_size)), 0u);
+ t.remove(cid, *i);
}
objects.clear();
i != objects.end();
++i) {
ASSERT_EQ(i->hash & (1<<common_suffix_size), 0u);
+ t.remove(tid, *i);
}
+
+ t.remove_collection(cid);
+ t.remove_collection(tid);
+ r = store->apply_transaction(t);
+ ASSERT_EQ(r, 0);
}
TEST_F(StoreTest, ColSplitTest1) {
TEST_F(StoreTest, ColSplitTest2) {
colsplittest(store.get(), 100, 7);
}
+
+#if 0
TEST_F(StoreTest, ColSplitTest3) {
colsplittest(store.get(), 100000, 25);
}
+#endif
int main(int argc, char **argv) {
vector<const char*> args;
global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0);
common_init_finish(g_ceph_context);
g_ceph_context->_conf->set_val("osd_journal_size", "400");
+ g_ceph_context->_conf->set_val("filestore_index_retry_probability", "1");
+ g_ceph_context->_conf->set_val("filestore_op_thread_timeout", "1000");
+ g_ceph_context->_conf->set_val("filestore_op_thread_suicide_timeout", "10000");
g_ceph_context->_conf->apply_changes(NULL);
::testing::InitGoogleTest(&argc, argv);