[asterisk-commits] bucket: Add clone/staleness operations for ast bucket/ast bu... (asterisk[master])

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sat Jul 11 10:47:00 CDT 2015


Matt Jordan has submitted this change and it was merged.

Change subject: bucket: Add clone/staleness operations for ast_bucket/ast_bucket_file
......................................................................


bucket: Add clone/staleness operations for ast_bucket/ast_bucket_file

This patch enhances the bucket API in two ways.

First, since ast_bucket and ast_bucket_file instances are immutable, a 'clone'
operation has been added that provides a 'clone' of an existing
ast_bucket/ast_bucket_file object. Note that this makes use of the
ast_sorcery_copy operation, along with the copy callback handler on the
"bucket" and "file" object types for the bucket sorcery instance.

Second, there is a need for the bucket API to ask a wizard if an object
is stale. This is particularly useful with the upcoming media cache
enhancements, where we want to ask the backing data storage if the
object we are currently operating on has known updates. This patch adds
API calls for ast_bucket and ast_bucket_file objects, which callback
into their respective sorcery wizards via the sorcery API.

Unit tests have also been added to cover the respective
ast_bucket/ast_bucket_file clone and staleness operations.

Change-Id: Ib0240ba915ece313f1678a085a716021d75d6b4a
---
M include/asterisk/bucket.h
M main/bucket.c
M tests/test_bucket.c
3 files changed, 327 insertions(+), 0 deletions(-)

Approvals:
  Anonymous Coward #1000019: Verified
  Matt Jordan: Looks good to me, approved
  Joshua Colp: Looks good to me, but someone else must approve



diff --git a/include/asterisk/bucket.h b/include/asterisk/bucket.h
index da83759..c335fd3 100644
--- a/include/asterisk/bucket.h
+++ b/include/asterisk/bucket.h
@@ -218,6 +218,23 @@
 int ast_bucket_create(struct ast_bucket *bucket);
 
 /*!
+ * \brief Clone a bucket
+ *
+ * This will create a copy of the passed in \c ast_bucket structure. While
+ * all properties of the \c ast_bucket structure are copied, any metadata
+ * in the original structure simply has its reference count increased.
+ *
+ * \param file The bucket to clone
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ *
+ * \note This operation should be called prior to updating a bucket
+ * object, as \c ast_bucket instances are immutable
+ */
+struct ast_bucket *ast_bucket_clone(struct ast_bucket *bucket);
+
+/*!
  * \brief Delete a bucket from backend storage
  *
  * \param bucket The bucket
@@ -238,6 +255,23 @@
  * \note The object is returned with reference count increased
  */
 struct ast_bucket *ast_bucket_retrieve(const char *uri);
+
+/*!
+ * \brief Retrieve whether or not the backing datastore views the bucket as stale
+ * \since 14.0.0
+ *
+ * This function will ask whatever data storage backs the bucket's schema
+ * type if the current instance of the object is stale. It will not
+ * update the bucket object itself, as said objects are immutable. If the
+ * caller of this function would like to update the object, it should perform
+ * a retrieve operation.
+ *
+ * \param bucket The bucket object to check
+ *
+ * \retval 0 if \c bucket is not stale
+ * \retval 1 if \c bucket is stale
+ */
+int ast_bucket_is_stale(struct ast_bucket *bucket);
 
 /*!
  * \brief Add an observer for bucket creation and deletion operations
@@ -309,6 +343,24 @@
 struct ast_bucket_file *ast_bucket_file_copy(struct ast_bucket_file *file, const char *uri);
 
 /*!
+ * \brief Clone a bucket file
+ *
+ * This will create a copy of the passed in \c ast_bucket_file structure. While
+ * all properties of the \c ast_bucket_file structure are copied, any metadata
+ * in the original structure simply has its reference count increased. Note that
+ * this copies the structure, not the underlying file.
+ *
+ * \param file The bucket file to clone
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ *
+ * \note This operation should be called prior to updating a bucket file
+ * object, as \c ast_bucket_file instances are immutable
+ */
+struct ast_bucket_file *ast_bucket_file_clone(struct ast_bucket_file *file);
+
+/*!
  * \brief Update an existing bucket file in backend storage
  *
  * \param file The bucket file
@@ -343,6 +395,23 @@
 struct ast_bucket_file *ast_bucket_file_retrieve(const char *uri);
 
 /*!
+ * \brief Retrieve whether or not the backing datastore views the bucket file as stale
+ * \since 14.0.0
+ *
+ * This function will ask whatever data storage backs the bucket file's schema
+ * type if the current instance of the object is stale. It will not
+ * update the bucket file object itself, as said objects are immutable. If the
+ * caller of this function would like to update the object, it should perform
+ * a retrieve operation.
+ *
+ * \param bucket_file The bucket file object to check
+ *
+ * \retval 0 if \c bucket_file is not stale
+ * \retval 1 if \c bucket_file is stale
+ */
+int ast_bucket_file_is_stale(struct ast_bucket_file *file);
+
+/*!
  * \brief Add an observer for bucket file creation and deletion operations
  *
  * \param callbacks Implementation of the sorcery observer interface
diff --git a/main/bucket.c b/main/bucket.c
index e93c66b..afb0175 100644
--- a/main/bucket.c
+++ b/main/bucket.c
@@ -163,12 +163,25 @@
 	return bucket->scheme_impl->bucket->delete(sorcery, data, object);
 }
 
+/*! \brief Callback function for determining if a bucket is stale */
+static int bucket_wizard_is_stale(const struct ast_sorcery *sorcery, void *data, void *object)
+{
+	struct ast_bucket *bucket = object;
+
+	if (!bucket->scheme_impl->bucket->is_stale) {
+		return 0;
+	}
+
+	return bucket->scheme_impl->bucket->is_stale(sorcery, data, object);
+}
+
 /*! \brief Intermediary bucket wizard */
 static struct ast_sorcery_wizard bucket_wizard = {
 	.name = "bucket",
 	.create = bucket_wizard_create,
 	.retrieve_id = bucket_wizard_retrieve,
 	.delete = bucket_wizard_delete,
+	.is_stale = bucket_wizard_is_stale,
 };
 
 /*! \brief Callback function for creating a bucket file */
@@ -240,6 +253,18 @@
 	return file->scheme_impl->file->delete(sorcery, data, object);
 }
 
+/*! \brief Callback function for determining if a bucket is stale */
+static int bucket_file_wizard_is_stale(const struct ast_sorcery *sorcery, void *data, void *object)
+{
+	struct ast_bucket_file *file = object;
+
+	if (!file->scheme_impl->file->is_stale) {
+		return 0;
+	}
+
+	return file->scheme_impl->file->is_stale(sorcery, data, object);
+}
+
 /*! \brief Intermediary file wizard */
 static struct ast_sorcery_wizard bucket_file_wizard = {
 	.name = "bucket_file",
@@ -247,6 +272,7 @@
 	.retrieve_id = bucket_file_wizard_retrieve,
 	.update = bucket_file_wizard_update,
 	.delete = bucket_file_wizard_delete,
+	.is_stale = bucket_file_wizard_is_stale,
 };
 
 int __ast_bucket_scheme_register(const char *name, struct ast_sorcery_wizard *bucket,
@@ -459,6 +485,28 @@
 	return ast_sorcery_create(bucket_sorcery, bucket);
 }
 
+/*!
+ * \internal
+ * \brief Sorcery object type copy handler for \c ast_bucket
+ */
+static int bucket_copy_handler(const void *src, void *dst)
+{
+	const struct ast_bucket *src_bucket = src;
+	struct ast_bucket *dst_bucket = dst;
+
+	dst_bucket->scheme_impl = ao2_bump(src_bucket->scheme_impl);
+	ast_string_field_set(dst_bucket, scheme, src_bucket->scheme);
+	dst_bucket->created = src_bucket->created;
+	dst_bucket->modified = src_bucket->modified;
+
+	return 0;
+}
+
+struct ast_bucket *ast_bucket_clone(struct ast_bucket *bucket)
+{
+	return ast_sorcery_copy(bucket_sorcery, bucket);
+}
+
 struct ast_bucket *ast_bucket_retrieve(const char *uri)
 {
 	if (ast_strlen_zero(uri)) {
@@ -466,6 +514,11 @@
 	}
 
 	return ast_sorcery_retrieve_by_id(bucket_sorcery, "bucket", uri);
+}
+
+int ast_bucket_is_stale(struct ast_bucket *bucket)
+{
+	return ast_sorcery_is_stale(bucket_sorcery, bucket);
 }
 
 int ast_bucket_observer_add(const struct ast_sorcery_observer *callbacks)
@@ -730,6 +783,29 @@
 	return 0;	/* success */
 }
 
+/*!
+ * \internal
+ * \brief Sorcery object type copy handler for \c ast_bucket_file
+ */
+static int bucket_file_copy_handler(const void *src, void *dst)
+{
+	const struct ast_bucket_file *src_file = src;
+	struct ast_bucket_file *dst_file = dst;
+
+	dst_file->scheme_impl = ao2_bump(src_file->scheme_impl);
+	ast_string_field_set(dst_file, scheme, src_file->scheme);
+	dst_file->created = src_file->created;
+	dst_file->modified = src_file->modified;
+	strcpy(dst_file->path, src_file->path); /* safe */
+
+	dst_file->metadata = ao2_container_clone(src_file->metadata, 0);
+	if (!dst_file->metadata) {
+		return -1;
+	}
+
+	return 0;
+}
+
 struct ast_bucket_file *ast_bucket_file_copy(struct ast_bucket_file *file, const char *uri)
 {
 	RAII_VAR(struct ast_bucket_file *, copy, ast_bucket_file_alloc(uri), ao2_cleanup);
@@ -749,6 +825,11 @@
 	return copy;
 }
 
+struct ast_bucket_file *ast_bucket_file_clone(struct ast_bucket_file *file)
+{
+	return ast_sorcery_copy(bucket_sorcery, file);
+}
+
 struct ast_bucket_file *ast_bucket_file_retrieve(const char *uri)
 {
 	if (ast_strlen_zero(uri)) {
@@ -756,6 +837,11 @@
 	}
 
 	return ast_sorcery_retrieve_by_id(bucket_sorcery, "file", uri);
+}
+
+int ast_bucket_file_is_stale(struct ast_bucket_file *file)
+{
+	return ast_sorcery_is_stale(bucket_sorcery, file);
 }
 
 int ast_bucket_file_observer_add(const struct ast_sorcery_observer *callbacks)
@@ -945,6 +1031,7 @@
 	ast_sorcery_object_field_register(bucket_sorcery, "bucket", "scheme", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_bucket, scheme));
 	ast_sorcery_object_field_register_custom(bucket_sorcery, "bucket", "created", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket, created));
 	ast_sorcery_object_field_register_custom(bucket_sorcery, "bucket", "modified", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket, modified));
+	ast_sorcery_object_set_copy_handler(bucket_sorcery, "bucket", bucket_copy_handler);
 
 	if (ast_sorcery_apply_default(bucket_sorcery, "file", "bucket_file", NULL) == AST_SORCERY_APPLY_FAIL) {
 		ast_log(LOG_ERROR, "Failed to apply intermediary for 'file' object type in Bucket sorcery\n");
@@ -959,6 +1046,7 @@
 	ast_sorcery_object_field_register(bucket_sorcery, "file", "scheme", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_bucket_file, scheme));
 	ast_sorcery_object_field_register_custom(bucket_sorcery, "file", "created", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket_file, created));
 	ast_sorcery_object_field_register_custom(bucket_sorcery, "file", "modified", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket_file, modified));
+	ast_sorcery_object_set_copy_handler(bucket_sorcery, "file", bucket_file_copy_handler);
 
 	return 0;
 }
diff --git a/tests/test_bucket.c b/tests/test_bucket.c
index 7dadd14..eb8fc90 100644
--- a/tests/test_bucket.c
+++ b/tests/test_bucket.c
@@ -50,6 +50,8 @@
 	unsigned int updated:1;
 	/*! \brief Whether the object has been deleted or not */
 	unsigned int deleted:1;
+	/*! \brief Whether the object is stale or not */
+	unsigned int is_stale:1;
 };
 
 /*! \brief Global scope structure for testing bucket wizards */
@@ -60,6 +62,7 @@
 	bucket_test_wizard_state.created = 0;
 	bucket_test_wizard_state.updated = 0;
 	bucket_test_wizard_state.deleted = 0;
+	bucket_test_wizard_state.is_stale = 0;
 }
 
 static int bucket_test_wizard_create(const struct ast_sorcery *sorcery, void *data, void *object)
@@ -107,11 +110,17 @@
 	return 0;
 }
 
+static int bucket_test_wizard_is_stale(const struct ast_sorcery *sorcery, void *data, void *object)
+{
+	return bucket_test_wizard_state.is_stale;
+}
+
 static struct ast_sorcery_wizard bucket_test_wizard = {
 	.name = "test",
 	.create = bucket_test_wizard_create,
 	.retrieve_id = bucket_test_wizard_retrieve_id,
 	.delete = bucket_test_wizard_delete,
+	.is_stale = bucket_test_wizard_is_stale,
 };
 
 static struct ast_sorcery_wizard bucket_file_test_wizard = {
@@ -120,6 +129,7 @@
 	.update = bucket_test_wizard_update,
 	.retrieve_id = bucket_test_wizard_retrieve_id,
 	.delete = bucket_test_wizard_delete,
+	.is_stale = bucket_test_wizard_is_stale,
 };
 
 AST_TEST_DEFINE(bucket_scheme_register)
@@ -233,6 +243,50 @@
 	return AST_TEST_PASS;
 }
 
+AST_TEST_DEFINE(bucket_clone)
+{
+	RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_bucket *, clone, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_clone";
+		info->category = "/main/bucket/";
+		info->summary = "bucket clone unit test";
+		info->description =
+			"Test cloning a bucket";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(bucket = ast_bucket_alloc("test:///tmp/bob"))) {
+		ast_test_status_update(test, "Failed to allocate bucket\n");
+		return AST_TEST_FAIL;
+	}
+
+	bucket_test_wizard_clear();
+
+	if (ast_bucket_create(bucket)) {
+		ast_test_status_update(test, "Failed to create bucket with URI '%s'\n",
+			ast_sorcery_object_get_id(bucket));
+		return AST_TEST_FAIL;
+	}
+
+	clone = ast_bucket_clone(bucket);
+	if (!clone) {
+		ast_test_status_update(test, "Failed to clone bucket with URI '%s'\n",
+			ast_sorcery_object_get_id(bucket));
+		return AST_TEST_FAIL;
+	}
+
+	ast_test_validate(test, strcmp(ast_sorcery_object_get_id(bucket), ast_sorcery_object_get_id(clone)) == 0);
+	ast_test_validate(test, bucket->scheme_impl == clone->scheme_impl);
+	ast_test_validate(test, strcmp(bucket->scheme, clone->scheme) == 0);
+
+	return AST_TEST_PASS;
+}
+
 AST_TEST_DEFINE(bucket_delete)
 {
 	RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
@@ -272,6 +326,38 @@
 			ast_sorcery_object_get_id(bucket));
 		return AST_TEST_FAIL;
 	}
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(bucket_is_stale)
+{
+	RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_is_stale";
+		info->category = "/main/bucket/";
+		info->summary = "bucket staleness unit test";
+		info->description =
+			"Test if staleness of a bucket is reported correctly";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(bucket = ast_bucket_alloc("test:///tmp/bob"))) {
+		ast_test_status_update(test, "Failed to allocate bucket\n");
+		return AST_TEST_FAIL;
+	}
+
+	bucket_test_wizard_clear();
+
+	ast_test_validate(test, ast_bucket_is_stale(bucket) == 0);
+
+	bucket_test_wizard_state.is_stale = 1;
+
+	ast_test_validate(test, ast_bucket_is_stale(bucket) == 1);
 
 	return AST_TEST_PASS;
 }
@@ -436,6 +522,52 @@
 			ast_sorcery_object_get_id(file));
 		return AST_TEST_FAIL;
 	}
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(bucket_file_clone)
+{
+	RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_bucket_file *, clone, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_file_clone";
+		info->category = "/main/bucket/";
+		info->summary = "file clone unit test";
+		info->description =
+			"Test cloning a file";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
+		ast_test_status_update(test, "Failed to allocate file\n");
+		return AST_TEST_FAIL;
+	}
+
+	bucket_test_wizard_clear();
+
+	if (ast_bucket_file_create(file)) {
+		ast_test_status_update(test, "Failed to create file with URI '%s'\n",
+			ast_sorcery_object_get_id(file));
+		return AST_TEST_FAIL;
+	}
+	ast_bucket_file_metadata_set(file, "bob", "joe");
+
+	clone = ast_bucket_file_clone(file);
+	if (!clone) {
+		ast_test_status_update(test, "Failed to clone file with URI '%s'\n",
+			ast_sorcery_object_get_id(file));
+		return AST_TEST_FAIL;
+	}
+
+	ast_test_validate(test, strcmp(ast_sorcery_object_get_id(file), ast_sorcery_object_get_id(clone)) == 0);
+	ast_test_validate(test, file->scheme_impl == clone->scheme_impl);
+	ast_test_validate(test, strcmp(file->scheme, clone->scheme) == 0);
+	ast_test_validate(test, ao2_container_count(file->metadata) == ao2_container_count(clone->metadata));
 
 	return AST_TEST_PASS;
 }
@@ -621,6 +753,38 @@
 			ast_sorcery_object_get_id(file));
 		return AST_TEST_FAIL;
 	}
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(bucket_file_is_stale)
+{
+	RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_file_is_stale";
+		info->category = "/main/bucket/";
+		info->summary = "file staleness unit test";
+		info->description =
+			"Test if staleness of a bucket file is reported correctly";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
+		ast_test_status_update(test, "Failed to allocate file\n");
+		return AST_TEST_FAIL;
+	}
+
+	bucket_test_wizard_clear();
+
+	ast_test_validate(test, ast_bucket_file_is_stale(file) == 0);
+
+	bucket_test_wizard_state.is_stale = 1;
+
+	ast_test_validate(test, ast_bucket_file_is_stale(file) == 1);
 
 	return AST_TEST_PASS;
 }
@@ -827,11 +991,13 @@
 	AST_TEST_UNREGISTER(bucket_scheme_register);
 	AST_TEST_UNREGISTER(bucket_alloc);
 	AST_TEST_UNREGISTER(bucket_create);
+	AST_TEST_UNREGISTER(bucket_clone);
 	AST_TEST_UNREGISTER(bucket_delete);
 	AST_TEST_UNREGISTER(bucket_retrieve);
 	AST_TEST_UNREGISTER(bucket_json);
 	AST_TEST_UNREGISTER(bucket_file_alloc);
 	AST_TEST_UNREGISTER(bucket_file_create);
+	AST_TEST_UNREGISTER(bucket_file_clone);
 	AST_TEST_UNREGISTER(bucket_file_copy);
 	AST_TEST_UNREGISTER(bucket_file_retrieve);
 	AST_TEST_UNREGISTER(bucket_file_update);
@@ -854,15 +1020,19 @@
 	AST_TEST_REGISTER(bucket_scheme_register);
 	AST_TEST_REGISTER(bucket_alloc);
 	AST_TEST_REGISTER(bucket_create);
+	AST_TEST_REGISTER(bucket_clone);
 	AST_TEST_REGISTER(bucket_delete);
 	AST_TEST_REGISTER(bucket_retrieve);
+	AST_TEST_REGISTER(bucket_is_stale);
 	AST_TEST_REGISTER(bucket_json);
 	AST_TEST_REGISTER(bucket_file_alloc);
 	AST_TEST_REGISTER(bucket_file_create);
+	AST_TEST_REGISTER(bucket_file_clone);
 	AST_TEST_REGISTER(bucket_file_copy);
 	AST_TEST_REGISTER(bucket_file_retrieve);
 	AST_TEST_REGISTER(bucket_file_update);
 	AST_TEST_REGISTER(bucket_file_delete);
+	AST_TEST_REGISTER(bucket_file_is_stale);
 	AST_TEST_REGISTER(bucket_file_metadata_set);
 	AST_TEST_REGISTER(bucket_file_metadata_unset);
 	AST_TEST_REGISTER(bucket_file_metadata_get);

-- 
To view, visit https://gerrit.asterisk.org/672
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ib0240ba915ece313f1678a085a716021d75d6b4a
Gerrit-PatchSet: 3
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Matt Jordan <mjordan at digium.com>
Gerrit-Reviewer: Anonymous Coward #1000019
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Matt Jordan <mjordan at digium.com>



More information about the asterisk-commits mailing list