From: Christian Marie Date: Wed, 15 Jan 2014 04:55:11 +0000 (+1100) Subject: librados: Add C API coverage for atomic write operations X-Git-Tag: v0.78~304^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=4425f9edaa9f4108bd4a693760c5d7be4359ee4a;p=ceph.git librados: Add C API coverage for atomic write operations Signed-off-by: Christian Marie --- diff --git a/src/include/rados/librados.h b/src/include/rados/librados.h index 4f35a7799fa..60370e4041f 100644 --- a/src/include/rados/librados.h +++ b/src/include/rados/librados.h @@ -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 + * + * 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 */ /** diff --git a/src/librados/librados.cc b/src/librados/librados.cc index 278ddb679d7..4925d5eec1d 100644 --- a/src/librados/librados.cc +++ b/src/librados/librados.cc @@ -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); +} diff --git a/src/test/Makefile.am b/src/test/Makefile.am index c9a6c8cf38b..5ce66353966 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -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 index 00000000000..906a386f41f --- /dev/null +++ b/src/test/librados/c_write_operations.cc @@ -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)); +}