From e0e36b6feba6cbcbbb3516d3a9de4d6fd0199825 Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Fri, 23 Sep 2022 16:39:21 +0900 Subject: NAMESTORE: Towards proper transactional locks --- src/namestore/test_namestore_api_edit_records.c | 404 ++++++++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 src/namestore/test_namestore_api_edit_records.c (limited to 'src/namestore/test_namestore_api_edit_records.c') diff --git a/src/namestore/test_namestore_api_edit_records.c b/src/namestore/test_namestore_api_edit_records.c new file mode 100644 index 000000000..c1c64ee9c --- /dev/null +++ b/src/namestore/test_namestore_api_edit_records.c @@ -0,0 +1,404 @@ +/* + This file is part of GNUnet. + Copyright (C) 2022 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_edit_records.c + * @brief testcase for namestore_api.c: Multiple clients work with record set. + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" +#include "gnunet_dnsparser_lib.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + +#define TEST_RECORD_DATALEN 123 + +#define TEST_RECORD_DATA 'a' + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) + + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_NAMESTORE_Handle *nsh2; + +static struct GNUNET_SCHEDULER_Task *endbadly_task; + +static struct GNUNET_IDENTITY_PrivateKey privkey; + +static struct GNUNET_IDENTITY_PublicKey pubkey; + +static int res; + +static int removed; + +static struct GNUNET_NAMESTORE_QueueEntry *nsqe; + +static int nonce = 0; + +static void +cleanup () +{ + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + */ +static void +endbadly (void *cls) +{ + if (NULL != nsqe) + { + GNUNET_NAMESTORE_cancel (nsqe); + nsqe = NULL; + } + cleanup (); + res = 1; +} + + +static void +end (void *cls) +{ + cleanup (); + res = 0; +} + +static void +lookup_it (void *cls, + const struct GNUNET_IDENTITY_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + GNUNET_assert (0 == rd_count); + GNUNET_SCHEDULER_add_now (&end, NULL); +} + +static void +fail_cb (void *cls) +{ + if (endbadly_task != NULL) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; +} + +static void +remove_cont (void *cls, + int32_t success, + const char *emsg) +{ + nsqe = NULL; + if (GNUNET_YES != success) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to roll back: `%s'\n"), + emsg); + if (NULL != endbadly_task) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, + NULL); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Rolled back, perform lookup\n"); + removed = GNUNET_YES; + if (NULL != endbadly_task) + GNUNET_SCHEDULER_cancel (endbadly_task); + GNUNET_SCHEDULER_add_now (&end, NULL); +} + +static void +fail_cb_lock (void *cls); + +static void +edit_cont_b (void *cls, + const struct GNUNET_IDENTITY_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + const char *name = cls; + /** + * We should probably never get here right at first. + * We may want to change the blocking of nsh2 so that we do get this + * eventually instead of the error callback above when locked. + */ + if (0 == nonce) + { + if (endbadly_task != NULL) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + + } + /* Abort transaction for B */ + nsqe = GNUNET_NAMESTORE_transaction_rollback (nsh2, remove_cont, + (void *) name); +} + + +static void +commit_cont_a (void *cls, + int32_t success, + const char *emsg) +{ + const char *name = cls; + + GNUNET_assert (NULL != cls); + nsqe = NULL; + if (GNUNET_SYSERR == success) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Namestore could not store record: `%s'\n", + emsg); + if (endbadly_task != NULL) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Name store added record for `%s': %s\n", + name, + (success == GNUNET_OK) ? "SUCCESS" : "FAIL"); + /** + * Try again for B + */ + nsqe = GNUNET_NAMESTORE_records_edit (nsh2, + &privkey, + name, + &fail_cb_lock, + (void *) name, + &edit_cont_b, + (void *) name); + + GNUNET_assert (NULL != nsqe); +} + +static void +fail_cb_lock (void *cls) +{ + const char *name = cls; + if (1 == nonce) + { + if (endbadly_task != NULL) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + nonce = 1; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Failed to aquire additional lock\n"); + /* Now, we stop the transaction for B */ + nsqe = GNUNET_NAMESTORE_transaction_commit (nsh, commit_cont_a, + (void *) name); +} + + +static void +begin_cont_b (void *cls, + int32_t success, + const char *emsg) +{ + const char *name = cls; + + GNUNET_assert (success == GNUNET_YES); + /** Now, we expect this to "hang" let's see how this behaves in practice. */ + nsqe = GNUNET_NAMESTORE_records_edit (nsh2, + &privkey, + name, + &fail_cb_lock, + (void *) name, + &edit_cont_b, + (void *) name); + + GNUNET_assert (NULL != nsqe); +} + + +static void +edit_cont (void *cls, + const struct GNUNET_IDENTITY_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + const char *name = cls; + + GNUNET_assert (1 == rd_count); + /* Now, we start a transaction for B */ + nsqe = GNUNET_NAMESTORE_transaction_begin (nsh2, begin_cont_b, (void *) name); +} + + +static void +begin_cont (void *cls, + int32_t success, + const char *emsg) +{ + const char *name = cls; + + GNUNET_assert (success == GNUNET_YES); + nsqe = GNUNET_NAMESTORE_records_edit (nsh, + &privkey, + name, + &fail_cb, + (void *) name, + &edit_cont, + (void *) name); + + GNUNET_assert (NULL != nsqe); +} + +static void +preload_cont (void *cls, + int32_t success, + const char *emsg) +{ + const char *name = cls; + + GNUNET_assert (NULL != cls); + nsqe = NULL; + if (GNUNET_SYSERR == success) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Namestore could not store record: `%s'\n", + emsg); + if (endbadly_task != NULL) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Name store added record for `%s': %s\n", + name, + (success == GNUNET_OK) ? "SUCCESS" : "FAIL"); + /* We start transaction for A */ + nsqe = GNUNET_NAMESTORE_transaction_begin (nsh, begin_cont, (void *) name); + +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + struct GNUNET_GNSRECORD_Data rd; + const char *name = "dummy"; + + endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &endbadly, + NULL); + nsh = GNUNET_NAMESTORE_connect (cfg); + nsh2 = GNUNET_NAMESTORE_connect (cfg); + GNUNET_break (NULL != nsh); + GNUNET_break (NULL != nsh2); + + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + GNUNET_IDENTITY_key_get_public (&privkey, + &pubkey); + + removed = GNUNET_NO; + + rd.expiration_time = GNUNET_TIME_absolute_get ().abs_value_us; + rd.record_type = TEST_RECORD_TYPE; + rd.data_size = TEST_RECORD_DATALEN; + rd.data = GNUNET_malloc (TEST_RECORD_DATALEN); + rd.flags = 0; + memset ((char *) rd.data, + 'a', + TEST_RECORD_DATALEN); + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + name, + 1, + &rd, + &preload_cont, + (void *) name); + GNUNET_assert (NULL != nsqe); + GNUNET_free_nz ((void *) rd.data); + + /*nsqe = GNUNET_NAMESTORE_transaction_commit (nsh, commit_cont); + nsqe = GNUNET_NAMESTORE_transaction_rollback (nsh, rollback_cont); Must also happen on disconnect + nsqe = GNUNET_NAMESTORE_records_edit (nsh, + &privkey, + name, + 1, + &rd, + &edit_cont, + (void *) name); + nsqe = GNUNET_NAMESTORE_records_insert_bulk (nsh, + count, + &rd, + & + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + name, + 1, + &rd, + &put_cont, + (void *) name);*/ + GNUNET_assert (NULL != nsqe); +} + + +#include "test_common.c" + + +int +main (int argc, char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test-namestore-api-remove", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_remove.c */ -- cgit v1.2.3