]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
os/: Add failure CollectionIndex failure injection
authorSamuel Just <sam.just@inktank.com>
Fri, 16 Nov 2012 22:26:32 +0000 (14:26 -0800)
committerSamuel Just <sam.just@inktank.com>
Wed, 5 Dec 2012 19:34:18 +0000 (11:34 -0800)
Several pieces of HashIndex involve multi-step operations
which are sensitive to OSD crashes.  This patch introduces
failure injection to force retries from various points in
the LFNIndex helper methods to be used with store_test.cc.

Signed-off-by: Samuel Just <sam.just@inktank.com>
src/common/config_opts.h
src/os/HashIndex.cc
src/os/HashIndex.h
src/os/IndexManager.cc
src/os/LFNIndex.cc
src/os/LFNIndex.h
src/test/filestore/store_test.cc

index eedb6d6a2f5bb6b55b1ee132362eeafc14e5c2c7..3ae5870b36629d60921ed3ca7710eb2308310072 100644 (file)
@@ -376,6 +376,10 @@ OPTION(osd_client_op_priority, OPT_INT, 63)
 OPTION(osd_recovery_op_priority, OPT_INT, 30)
 
 OPTION(filestore, OPT_BOOL, false)
+
+// Tests index failure paths
+OPTION(filestore_index_retry_probability, OPT_DOUBLE, 0)
+
 OPTION(filestore_debug_omap_check, OPT_BOOL, 0) // Expensive debugging check on sync
 // Use omap for xattrs for attrs over
 OPTION(filestore_xattr_use_omap, OPT_BOOL, false)
index d9f9e330e8fa1b9405f8b52d31720f120f876658..479e85107d9653dea28681ebf1e5e7c804dfe3b1 100644 (file)
@@ -221,7 +221,7 @@ int HashIndex::col_split_level(
   return 0;
 }
 
-int HashIndex::split(
+int HashIndex::_split(
   uint32_t match,
   uint32_t bits,
   std::tr1::shared_ptr<CollectionIndex> dest) {
index 3705486493f84e1ac78e43ebea6e8cfcbc18e380..659c9876302bc280ddc16db9bc4759d0e0d997b6 100644 (file)
@@ -136,8 +136,10 @@ public:
     const char *base_path, ///< [in] Path to the index root.
     int merge_at,          ///< [in] Merge threshhold.
     int split_multiple,           ///< [in] Split threshhold.
-    uint32_t index_version)///< [in] Index version
-    : LFNIndex(collection, base_path, index_version), merge_threshold(merge_at),
+    uint32_t index_version,///< [in] Index version
+    double retry_probability=0) ///< [in] retry probability
+    : LFNIndex(collection, base_path, index_version, retry_probability),
+      merge_threshold(merge_at),
       split_multiplier(split_multiple) {}
 
   /// @see CollectionIndex
@@ -147,7 +149,7 @@ public:
   int cleanup();
 
   /// @see CollectionIndex
-  int split(
+  int _split(
     uint32_t match,
     uint32_t bits,
     std::tr1::shared_ptr<CollectionIndex> dest
index 85281c4d9265a6158998500b1106cb3e3b290a1f..11bf5c18172a5f7ac68fe56a4429336c216bf6e7 100644 (file)
@@ -75,7 +75,8 @@ int IndexManager::init_index(coll_t c, const char *path, uint32_t version) {
     return r;
   HashIndex index(c, path, g_conf->filestore_merge_threshold,
                  g_conf->filestore_split_multiple,
-                 CollectionIndex::HASH_INDEX_TAG_2);
+                 CollectionIndex::HASH_INDEX_TAG_2,
+                 g_conf->filestore_index_retry_probability);
   return index.init();
 }
 
@@ -110,7 +111,8 @@ int IndexManager::build_index(coll_t c, const char *path, Index *index) {
     // No need to check
     *index = Index(new HashIndex(c, path, g_conf->filestore_merge_threshold,
                                 g_conf->filestore_split_multiple,
-                                CollectionIndex::HOBJECT_WITH_POOL), 
+                                CollectionIndex::HOBJECT_WITH_POOL,
+                                g_conf->filestore_index_retry_probability),
                   RemoveOnDelete(c, this));
     return 0;
   }
index 8949c607d668e193d1d7c306883940597d488a79..5e505638d15a9edf6152f25ae4a50e8a8a6aa87c 100644 (file)
@@ -47,6 +47,18 @@ const string LFNIndex::FILENAME_COOKIE = "long";
 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 */
 
@@ -72,41 +84,47 @@ int LFNIndex::created(const hobject_t &hoid, const char *path) {
 }
 
 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) {
@@ -126,11 +144,14 @@ int LFNIndex::collection_list_partial(const hobject_t &start,
 /* 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
@@ -144,10 +165,13 @@ int LFNIndex::link_object(const vector<string> &from,
   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
@@ -162,7 +186,9 @@ int LFNIndex::remove_objects(const vector<string> &dir,
        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;
@@ -189,14 +215,18 @@ int LFNIndex::remove_objects(const vector<string> &dir,
       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);
@@ -226,10 +256,13 @@ int LFNIndex::move_objects(const vector<string> &from,
     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;
   }
@@ -239,7 +272,9 @@ int LFNIndex::move_objects(const vector<string> &from,
   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;
   }
@@ -250,7 +285,9 @@ int LFNIndex::remove_object(const vector<string> &from,
                            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);
@@ -412,7 +449,9 @@ int LFNIndex::list_subdirs(const vector<string> &to_list,
 }
 
 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
@@ -420,7 +459,9 @@ int LFNIndex::create_path(const vector<string> &to_create) {
 }
 
 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
@@ -448,6 +489,7 @@ int LFNIndex::add_attr_path(const vector<string> &path,
                            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());
@@ -483,6 +525,7 @@ int LFNIndex::remove_attr_path(const vector<string> &path,
                               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());
 }
   
@@ -637,6 +680,7 @@ int LFNIndex::lfn_get_name(const vector<string> &path,
     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)
@@ -663,7 +707,9 @@ int LFNIndex::lfn_get_name(const vector<string> &path,
        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;
       }
@@ -698,6 +744,7 @@ int LFNIndex::lfn_created(const vector<string> &path,
     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());
 }
@@ -707,7 +754,9 @@ int LFNIndex::lfn_unlink(const vector<string> &path,
                         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;
@@ -738,7 +787,9 @@ int LFNIndex::lfn_unlink(const vector<string> &path,
   }
   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
@@ -746,7 +797,9 @@ int LFNIndex::lfn_unlink(const vector<string> &path,
   } 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
index cd5b8cfc266acbf2f46e69d0da3da32735b5e6ce..ca8ee07f912d886f107204164c06703c7bcdf94b 100644 (file)
@@ -21,6 +21,7 @@
 #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;
@@ -78,6 +104,24 @@ class LFNIndex : public CollectionIndex {
 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;
@@ -87,8 +131,14 @@ public:
   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;
@@ -146,6 +196,25 @@ public:
     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;
 
index e1e1491c53ed1cbe8e0f141617a2b9b08d63b4bb..63796700c5412dc2f4c87e74df494d2479b2143a 100644 (file)
@@ -767,6 +767,8 @@ void colsplittest(
     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);
@@ -775,6 +777,7 @@ void colsplittest(
        i != objects.end();
        ++i) {
     ASSERT_EQ(!(i->hash & (1<<common_suffix_size)), 0u);
+    t.remove(cid, *i);
   }
 
   objects.clear();
@@ -785,7 +788,13 @@ void colsplittest(
        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) {
@@ -794,9 +803,12 @@ 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;
@@ -805,6 +817,9 @@ int main(int argc, char **argv) {
   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);