]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
librados: Add C API coverage for atomic write operations 1088/head
authorChristian Marie <christian@ponies.io>
Wed, 15 Jan 2014 04:55:11 +0000 (15:55 +1100)
committerChristian Marie <christian@ponies.io>
Wed, 15 Jan 2014 10:58:18 +0000 (21:58 +1100)
Signed-off-by: Christian Marie <christian@ponies.io>
src/include/rados/librados.h
src/librados/librados.cc
src/test/Makefile.am
src/test/librados/c_write_operations.cc [new file with mode: 0644]

index 4f35a7799fac1d484161557fa373f52d405be8a1..60370e4041ff069266a73e34a0e674e9bb164a68 100644 (file)
@@ -1,3 +1,17 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2012 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
 #ifndef CEPH_LIBRADOS_H
 #define CEPH_LIBRADOS_H
 
@@ -39,9 +53,11 @@ extern "C" {
  */
 #define LIBRADOS_LOCK_FLAG_RENEW 0x1
 
+#define LIBRADOS_CREATE_EXCLUSIVE 1
+#define LIBRADOS_CREATE_IDEMPOTENT 0
+
 /**
  * @defgroup librados_h_xattr_comp xattr comparison operations
- * @note BUG: there's no way to use these in the C api
  * @{
  */
 /** @cond TODO_enums_not_yet_in_asphyxiate */
@@ -165,6 +181,23 @@ struct rados_cluster_stat_t {
   uint64_t num_objects;
 };
 
+/**
+ * @typedef rados_write_op_t
+ *
+ * An object write operation stores a number of operations which can be
+ * executed atomically. For usage, see:
+ * - Creation and deletion: rados_create_write_op() rados_release_write_op()
+ * - Extended attribute manipulation: rados_write_op_cmpxattr()
+ *   rados_write_op_cmpxattr(), rados_write_op_setxattr(),
+ *   rados_write_op_rmxattr()
+ * - Creating objects: rados_write_op_create()
+ * - IO on objects: rados_write_op_append(), rados_write_op_write(), rados_write_op_zero
+ *   rados_write_op_write_full(), rados_write_op_remove, rados_write_op_truncate(),
+ *   rados_write_op_zero()
+ * - Performing the operation: rados_write_op_operate(), rados_aio_write_op_operate()
+ */
+typedef void *rados_write_op_t;
+
 /**
  * Get the version of librados.
  *
@@ -1650,6 +1683,153 @@ int rados_unwatch(rados_ioctx_t io, const char *o, uint64_t handle);
  */
 int rados_notify(rados_ioctx_t io, const char *o, uint64_t ver, const char *buf, int buf_len);
 
+/** @} Atomic write operations */
+
+/**
+ * Create a new rados_write_op_t write operation. This will store all actions
+ * to be performed atomically. You must call rados_release_write_op when you are
+ * finished with it.
+ *
+ * @returns non-NULL on success, NULL on memory allocation error.
+ */
+rados_write_op_t rados_create_write_op();
+
+/**
+ * Free a rados_write_op_t, must be called when you're done with it.
+ * @param write_op operation to deallocate, created with rados_create_write_op
+ */
+void rados_release_write_op(rados_write_op_t write_op);
+
+/**
+ * Ensure that the object exists before writing
+ * @param write_op operation to add this action to
+ */
+void rados_write_op_assert_exists(rados_write_op_t write_op);
+
+/**
+ * Ensure that given xattr satisfies comparison
+ * @param write_op operation to add this action to
+ * @param name name of the xattr to look up
+ * @param comparison_operator currently undocumented, look for
+ * LIBRADOS_CMPXATTR_OP_EQ in librados.h
+ * @param value buffer to compare actual xattr value to
+ * @param value_len length of buffer to compare actual xattr value to
+ */
+void rados_write_op_cmpxattr(rados_write_op_t write_op,
+                             const char *name,
+                             uint8_t comparison_operator,
+                             const char *value,
+                             size_t value_len);
+
+/**
+ * Set an xattr
+ * @param write_op operation to add this action to
+ * @param name name of the xattr
+ * @param value buffer to set xattr to
+ * @param value_len length of buffer to set xattr to
+ */
+void rados_write_op_setxattr(rados_write_op_t write_op,
+                             const char *name,
+                             const char *value,
+                             size_t value_len);
+
+/**
+ * Remove an xattr
+ * @param write_op operation to add this action to
+ * @param name name of the xattr to remove
+ */
+void rados_write_op_rmxattr(rados_write_op_t write_op, const char *name);
+
+/**
+ * Create the object
+ * @param write_op operation to add this action to
+ * @param exclusive set to either LIBRADOS_CREATE_EXCLUSIVE or
+   LIBRADOS_CREATE_IDEMPOTENT
+ * will error if the object already exists.
+ */
+void rados_write_op_create(rados_write_op_t write_op,
+                           int exclusive,
+                           const char* category);
+
+/**
+ * Write to offset
+ * @param write_op operation to add this action to
+ * @param offset offset to write to
+ * @param buffer bytes to write
+ * @param len length of buffer
+ */
+void rados_write_op_write(rados_write_op_t write_op,
+                          const char *buffer,
+                          size_t len,
+                          uint64_t offset);
+
+/**
+ * Write whole object, atomically replacing it.
+ * @param write_op operation to add this action to
+ * @param buffer bytes to write
+ * @param len length of buffer
+ */
+void rados_write_op_write_full(rados_write_op_t write_op,
+                               const char *buffer,
+                               size_t len);
+
+/**
+ * Append to end of object.
+ * @param write_op operation to add this action to
+ * @param buffer bytes to write
+ * @param len length of buffer
+ */
+void rados_write_op_append(rados_write_op_t write_op,
+                           const char *buffer,
+                           size_t len);
+/**
+ * Remove object
+ * @param write_op operation to add this action to
+ */
+void rados_write_op_remove(rados_write_op_t write_op);
+
+/**
+ * Truncate an object
+ * @param write_op operation to add this action to
+ * @offset Offset to truncate to
+ */
+void rados_write_op_truncate(rados_write_op_t write_op, uint64_t offset);
+
+/**
+ * Zero part of an object
+ * @param write_op operation to add this action to
+ * @offset Offset to zero
+ * @len length to zero
+ */
+void rados_write_op_zero(rados_write_op_t write_op,
+                                   uint64_t offset,
+                                   uint64_t len);
+/**
+ * Perform a write operation synchronously
+ * @param write_op operation to perform
+ * @io the ioctx that the object is in
+ * @oid the object id
+ * @mtime the time to set the mtime to, NULL for the current time
+ */
+int rados_write_op_operate(rados_write_op_t write_op,
+                           rados_ioctx_t io,
+                           const char *oid,
+                           time_t *mtime);
+/**
+ * Perform a write operation asynchronously
+ * @param write_op operation to perform
+ * @io the ioctx that the object is in
+ * @param completion what to do when operation has been attempted
+ * @oid the object id
+ * @mtime the time to set the mtime to, NULL for the current time
+ */
+int rados_aio_write_op_operate(rados_write_op_t write_op,
+                               rados_ioctx_t io,
+                               rados_completion_t completion,
+                               const char *oid,
+                               time_t *mtime);
+
+
 /** @} Watch/Notify */
 
 /**
index 278ddb679d7b26fec386022ceff059f829a06400..4925d5eec1d344ec97df0c2777d78be106017dad 100644 (file)
@@ -3011,3 +3011,132 @@ extern "C" int rados_break_lock(rados_ioctx_t io, const char *o,
 
   return ctx.break_lock(o, name, client, cookie);
 }
+
+extern "C" rados_write_op_t rados_create_write_op()
+{
+  return new (std::nothrow)::ObjectOperation;
+}
+
+extern "C" void rados_release_write_op(rados_write_op_t write_op)
+{
+  delete (::ObjectOperation*)write_op;
+}
+
+extern "C" void rados_write_op_assert_exists(rados_write_op_t write_op)
+{
+  ((::ObjectOperation *)write_op)->stat(NULL, (utime_t *)NULL, NULL);
+}
+
+extern "C" void rados_write_op_cmpxattr(rados_write_op_t write_op,
+                                       const char *name,
+                                      uint8_t comparison_operator,
+                                      const char *value,
+                                      size_t value_len)
+{
+  bufferlist bl;
+  bl.append(value, value_len);
+  ((::ObjectOperation *)write_op)->cmpxattr(name,
+                                           comparison_operator,
+                                           CEPH_OSD_CMPXATTR_MODE_STRING,
+                                           bl);
+}
+
+extern "C" void rados_write_op_setxattr(rados_write_op_t write_op,
+                                       const char *name,
+                                      const char *value,
+                                      size_t value_len)
+{
+  bufferlist bl;
+  bl.append(value, value_len);
+  ((::ObjectOperation *)write_op)->setxattr(name, bl);
+}
+
+extern "C" void rados_write_op_rmxattr(rados_write_op_t write_op,
+                                       const char *name)
+{
+  bufferlist bl;
+  ((::ObjectOperation *)write_op)->rmxattr(name);
+}
+
+extern "C" void rados_write_op_create(rados_write_op_t write_op,
+                                      int exclusive,
+                                     const char* category)
+{
+  ::ObjectOperation *oo = (::ObjectOperation *) write_op;
+  if(category) {
+    std::string cpp_category = category;
+    oo->create(exclusive, category);
+  } else {
+    oo->create(!!exclusive);
+  }
+}
+
+extern "C" void rados_write_op_write(rados_write_op_t write_op,
+                                    const char *buffer,
+                                    size_t len,
+                                     uint64_t offset)
+{
+  bufferlist bl;
+  bl.append(buffer,len);
+  ((::ObjectOperation *)write_op)->write(offset, bl);
+}
+
+extern "C" void rados_write_op_write_full(rados_write_op_t write_op,
+                                         const char *buffer,
+                                         size_t len)
+{
+  bufferlist bl;
+  bl.append(buffer,len);
+  ((::ObjectOperation *)write_op)->write_full(bl);
+}
+
+extern "C" void rados_write_op_append(rados_write_op_t write_op,
+                                     const char *buffer,
+                                     size_t len)
+{
+  bufferlist bl;
+  bl.append(buffer,len);
+  ((::ObjectOperation *)write_op)->append(bl);
+}
+
+extern "C" void rados_write_op_remove(rados_write_op_t write_op)
+{
+  ((::ObjectOperation *)write_op)->remove();
+}
+
+extern "C" void rados_write_op_truncate(rados_write_op_t write_op,
+                                       uint64_t offset)
+{
+  ((::ObjectOperation *)write_op)->truncate(offset);
+}
+
+extern "C" void rados_write_op_zero(rados_write_op_t write_op,
+                                   uint64_t offset,
+                                   uint64_t len)
+{
+  ((::ObjectOperation *)write_op)->zero(offset, len);
+}
+
+extern "C" int rados_write_op_operate(rados_write_op_t write_op,
+                                      rados_ioctx_t io,
+                                      const char *oid,
+                                     time_t *mtime)
+{
+  object_t obj(oid);
+  ::ObjectOperation *oo = (::ObjectOperation *) write_op;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  return ctx->operate(obj, oo, mtime);
+}
+
+extern "C" int rados_aio_write_op_operate(rados_write_op_t write_op,
+                                      rados_ioctx_t io,
+                                     rados_completion_t completion,
+                                      const char *oid,
+                                     time_t *mtime)
+{
+  object_t obj(oid);
+  ::ObjectOperation *oo = (::ObjectOperation *) write_op;
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  librados::AioCompletionImpl *c = (librados::AioCompletionImpl*)completion;
+  return ctx->aio_operate(obj, oo, c, ctx->snapc, 0);
+}
index c9a6c8cf38b581756fff4f05866697bc5b6b1ce3..5ce66353966e3fd786511912b75a607fbf5473d8 100644 (file)
@@ -759,6 +759,13 @@ ceph_test_rados_api_io_LDADD = $(LIBRADOS) $(UNITTEST_LDADD)
 ceph_test_rados_api_io_CXXFLAGS = $(UNITTEST_CXXFLAGS)
 bin_DEBUGPROGRAMS += ceph_test_rados_api_io
 
+ceph_test_rados_api_c_write_operations_SOURCES = \
+       test/librados/c_write_operations.cc \
+       test/librados/test.cc
+ceph_test_rados_api_c_write_operations_LDADD = $(LIBRADOS) $(UNITTEST_LDADD)
+ceph_test_rados_api_c_write_operations_CXXFLAGS = $(UNITTEST_CXXFLAGS)
+bin_DEBUGPROGRAMS += ceph_test_rados_api_c_write_operations
+
 ceph_test_rados_api_aio_SOURCES = \
        test/librados/aio.cc \
        test/librados/test.cc
diff --git a/src/test/librados/c_write_operations.cc b/src/test/librados/c_write_operations.cc
new file mode 100644 (file)
index 0000000..906a386
--- /dev/null
@@ -0,0 +1,117 @@
+// Tests for the C API coverage of atomic write operations
+
+#include "include/rados/librados.h"
+#include "test/librados/test.h"
+#include "gtest/gtest.h"
+
+TEST(LibradosCWriteOps, NewDelete) {
+  rados_write_op_t op = rados_create_write_op();
+  ASSERT_TRUE(op);
+  rados_release_write_op(op);
+}
+
+TEST(LibRadosCWriteOps, assertExists) {
+  rados_t cluster;
+  rados_ioctx_t ioctx;
+  std::string pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
+
+  rados_write_op_t op = rados_create_write_op();
+  ASSERT_TRUE(op);
+  rados_write_op_assert_exists(op);
+
+  // -2, ENOENT
+  ASSERT_EQ(-2, rados_write_op_operate(op, ioctx, "test", NULL));
+  rados_release_write_op(op);
+
+  rados_write_op_t op2 = rados_create_write_op();
+  ASSERT_TRUE(op2);
+  rados_write_op_assert_exists(op2);
+
+  rados_completion_t completion;
+  ASSERT_EQ(0, rados_aio_create_completion(NULL, NULL, NULL, &completion));
+  ASSERT_EQ(0, rados_aio_write_op_operate(op2, ioctx, completion, "test", NULL));
+  rados_aio_wait_for_complete(completion);
+  ASSERT_EQ(-2, rados_aio_get_return_value(completion));
+
+  rados_ioctx_destroy(ioctx);
+  rados_release_write_op(op2);
+  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosCWriteOps, Xattrs) {
+  rados_t cluster;
+  rados_ioctx_t ioctx;
+  std::string pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
+
+  // Create an object with an xattr
+  rados_write_op_t op = rados_create_write_op();
+  ASSERT_TRUE(op);
+  rados_write_op_create(op, LIBRADOS_CREATE_EXCLUSIVE, NULL);
+  rados_write_op_setxattr(op, "key", "value", 5);
+  ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL));
+  rados_release_write_op(op);
+
+  // Check that xattr exists, if it does, delete it.
+  op = rados_create_write_op();
+  ASSERT_TRUE(op);
+  rados_write_op_create(op, LIBRADOS_CREATE_IDEMPOTENT, NULL);
+  rados_write_op_cmpxattr(op, "key", LIBRADOS_CMPXATTR_OP_EQ, "value", 5);
+  rados_write_op_rmxattr(op, "key");
+  ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL));
+  rados_release_write_op(op);
+
+  // Check the xattr exits, if it does, add it again (will fail) with -125
+  // (ECANCELED)
+  op = rados_create_write_op();
+  ASSERT_TRUE(op);
+  rados_write_op_cmpxattr(op, "key", LIBRADOS_CMPXATTR_OP_EQ, "value", 5);
+  rados_write_op_setxattr(op, "key", "value", 5);
+  ASSERT_EQ(-125, rados_write_op_operate(op, ioctx, "test", NULL));
+
+  rados_release_write_op(op);
+  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}
+
+TEST(LibRadosCWriteOps, Write) {
+  rados_t cluster;
+  rados_ioctx_t ioctx;
+  std::string pool_name = get_temp_pool_name();
+  ASSERT_EQ("", create_one_pool(pool_name, &cluster));
+  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
+
+  // Create an object, write and write full to it
+  rados_write_op_t op = rados_create_write_op();
+  ASSERT_TRUE(op);
+  rados_write_op_create(op, LIBRADOS_CREATE_EXCLUSIVE, NULL);
+  rados_write_op_write(op, "four", 4, 0);
+  rados_write_op_write_full(op, "hi", 2);
+  ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL));
+  char hi[4];
+  ASSERT_EQ(2, rados_read(ioctx, "test", hi, 4, 0));
+  rados_release_write_op(op);
+
+  // Truncate and append
+  op = rados_create_write_op();
+  ASSERT_TRUE(op);
+  rados_write_op_truncate(op, 1);
+  rados_write_op_append(op, "hi", 2);
+  ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL));
+  ASSERT_EQ(3, rados_read(ioctx, "test", hi, 4, 0));
+  rados_release_write_op(op);
+
+  // zero and remove
+  op = rados_create_write_op();
+  ASSERT_TRUE(op);
+  rados_write_op_zero(op, 0, 3);
+  rados_write_op_remove(op);
+  ASSERT_EQ(0, rados_write_op_operate(op, ioctx, "test", NULL));
+  // ENOENT
+  ASSERT_EQ(-2, rados_read(ioctx, "test", hi, 4, 0));
+  rados_release_write_op(op);
+
+  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
+}