From 475771f84d3fb6197fea772d67e5a59c46b512e5 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 20 Nov 2024 15:17:26 +0200 Subject: Add support for tenant service reference count --- libbrep/build-extra.sql | 1 + libbrep/build.hxx | 2 +- libbrep/build.xml | 2 ++ libbrep/common.hxx | 7 +++++- libbrep/package.hxx | 2 +- libbrep/package.xml | 6 +++++ migrate/migrate.cxx | 10 ++++---- mod/ci-common.cxx | 64 ++++++++++++++++++++++++++++++++++++++++--------- mod/ci-common.hxx | 12 +++++++++- mod/mod-ci-github.cxx | 2 +- 10 files changed, 88 insertions(+), 20 deletions(-) diff --git a/libbrep/build-extra.sql b/libbrep/build-extra.sql index 0c0f010..3134fbb 100644 --- a/libbrep/build-extra.sql +++ b/libbrep/build-extra.sql @@ -50,6 +50,7 @@ CREATE FOREIGN TABLE build_tenant ( archived BOOLEAN NOT NULL, service_id TEXT NULL, service_type TEXT NULL, + service_ref_count BIGINT NULL, service_data TEXT NULL, unloaded_timestamp BIGINT NULL, unloaded_notify_interval BIGINT NULL, diff --git a/libbrep/build.hxx b/libbrep/build.hxx index b485636..5ebbb0c 100644 --- a/libbrep/build.hxx +++ b/libbrep/build.hxx @@ -28,7 +28,7 @@ // #define LIBBREP_BUILD_SCHEMA_VERSION_BASE 28 -#pragma db model version(LIBBREP_BUILD_SCHEMA_VERSION_BASE, 28, closed) +#pragma db model version(LIBBREP_BUILD_SCHEMA_VERSION_BASE, 29, closed) // We have to keep these mappings at the global scope instead of inside the // brep namespace because they need to be also effective in the bbot namespace diff --git a/libbrep/build.xml b/libbrep/build.xml index d58e5f4..284db49 100644 --- a/libbrep/build.xml +++ b/libbrep/build.xml @@ -1,4 +1,6 @@ + + diff --git a/libbrep/common.hxx b/libbrep/common.hxx index d2009f5..22302f3 100644 --- a/libbrep/common.hxx +++ b/libbrep/common.hxx @@ -543,17 +543,22 @@ namespace brep // // Note that the {id, type} pair must be unique. // + // The reference count is used to keep track of the number of attempts to + // create a duplicate tenant with this {id, type} (see ci_start::create() + // for details). + // #pragma db value struct tenant_service { string id; string type; + uint64_t ref_count; optional data; tenant_service () = default; tenant_service (string i, string t, optional d = nullopt) - : id (move (i)), type (move (t)), data (move (d)) {} + : id (move (i)), type (move (t)), ref_count (1), data (move (d)) {} }; // Version comparison operators. diff --git a/libbrep/package.hxx b/libbrep/package.hxx index 79b2c68..2714d10 100644 --- a/libbrep/package.hxx +++ b/libbrep/package.hxx @@ -20,7 +20,7 @@ // #define LIBBREP_PACKAGE_SCHEMA_VERSION_BASE 34 -#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 35, closed) +#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 36, closed) namespace brep { diff --git a/libbrep/package.xml b/libbrep/package.xml index 8b6c706..ac48ec4 100644 --- a/libbrep/package.xml +++ b/libbrep/package.xml @@ -1,4 +1,10 @@ + + + + + + diff --git a/migrate/migrate.cxx b/migrate/migrate.cxx index 090fcac..095e6a3 100644 --- a/migrate/migrate.cxx +++ b/migrate/migrate.cxx @@ -208,7 +208,6 @@ create (database& db, bool extra_only) const // Register the data migration functions for the package database schema. // -#if 0 template using package_migration_entry_base = data_migration_entry; @@ -220,11 +219,14 @@ struct package_migration_entry: package_migration_entry_base : package_migration_entry_base (f, "package") {} }; -static const package_migration_entry<26> -package_migrate_v26 ([] (database& db) +static const package_migration_entry<36> +package_migrate_v36 ([] (database& db) { + // Set the reference count to 1 for tenant associated services. + // + db.execute ("UPDATE tenant SET service_ref_count = 1 " + "WHERE service_id IS NOT NULL"); }); -#endif // Register the data migration functions for the build database schema. // diff --git a/mod/ci-common.cxx b/mod/ci-common.cxx index 4b9f9f9..cba421b 100644 --- a/mod/ci-common.cxx +++ b/mod/ci-common.cxx @@ -553,7 +553,11 @@ namespace brep assert (!transaction::has_current ()); build_tenant t; + + // Set the reference count to 1 for the `created` result. + // duplicate_tenant_result r (duplicate_tenant_result::created); + service.ref_count = 1; for (string request_id;;) { @@ -584,14 +588,31 @@ namespace brep : duplicate_tenant_mode::ignore); } + // Shouldn't be here otherwise. + // + assert (t->service); + // Bail out in the ignore mode and cancel the tenant in the // replace mode. // if (mode == duplicate_tenant_mode::ignore) + { + // Increment the reference count for the `ignored` result. + // + ++(t->service->ref_count); + + db.update (t); + tr.commit (); + return make_pair (move (t->id), duplicate_tenant_result::ignored); + } assert (mode == duplicate_tenant_mode::replace); + // Preserve the current reference count for the `replaced` result. + // + service.ref_count = t->service->ref_count; + if (t->unloaded_timestamp) { db.erase (t); @@ -678,6 +699,7 @@ namespace brep // request_id = move (t.id); service = move (*t.service); + service.ref_count = 1; r = duplicate_tenant_result::created; } } @@ -788,7 +810,8 @@ namespace brep odb::core::database& db, size_t retry, const string& type, - const string& id) const + const string& id, + bool ref_count) const { using namespace odb::core; @@ -810,25 +833,44 @@ namespace brep if (t == nullptr) return nullopt; - r = move (t->service); + // Shouldn't be here otherwise. + // + assert (t->service && t->service->ref_count != 0); - if (t->unloaded_timestamp) + bool cancel (!ref_count || --(t->service->ref_count) == 0); + + if (cancel) { - db.erase (t); + // Move out the service state before it is dropped from the tenant. + // + r = move (t->service); + + if (t->unloaded_timestamp) + { + db.erase (t); + } + else + { + t->service = nullopt; + t->archived = true; + db.update (t); + } + + if (trace != nullptr) + *trace << "CI request " << t->id << " for service " << id << ' ' + << type << " is canceled"; } else { - t->service = nullopt; - t->archived = true; - db.update (t); + db.update (t); // Update the service reference count. + + // Move out the service state after the tenant is updated. + // + r = move (t->service); } tr.commit (); - if (trace != nullptr) - *trace << "CI request " << t->id << " for service " << id << ' ' - << type << " is canceled"; - // Bail out if we have successfully updated or erased the tenant // object. // diff --git a/mod/ci-common.hxx b/mod/ci-common.hxx index 36d5f0e..b32d397 100644 --- a/mod/ci-common.hxx +++ b/mod/ci-common.hxx @@ -103,6 +103,10 @@ namespace brep // Finally note that only duplicate_tenant_mode::fail can be used if the // service id is empty. // + // The tenant reference count is set to 1 if the result is `created`, + // incremented if the result is `ignored`, and preserved if the result is + // `replaced`. + // // Repeat the attempts on the recoverable database failures (deadlocks, // etc) and throw runtime_error if no more retries left. // @@ -150,6 +154,11 @@ namespace brep // dropped. Note that the latter allow using unloaded tenants as a // relatively cheap asynchronous execution mechanism. // + // If ref_count is true, then decrement the tenant reference count and + // only cancel the CI request if it becomes 0. In this mode the caller can + // determine if the request was actually canceled by checking if the + // reference count in the returned service state is 0. + // // Repeat the attempts on the recoverable database failures (deadlocks, // etc) and throw runtime_error if no more retries left. // @@ -162,7 +171,8 @@ namespace brep odb::core::database&, size_t retry, const string& type, - const string& id) const; + const string& id, + bool ref_count = false) const; // Cancel previously created or started CI request. Return false if there // is no tenant for the specified tenant id. Note that the reason argument diff --git a/mod/mod-ci-github.cxx b/mod/mod-ci-github.cxx index 9d2606b..ba80ed6 100644 --- a/mod/mod-ci-github.cxx +++ b/mod/mod-ci-github.cxx @@ -828,7 +828,7 @@ namespace brep if (system_clock::now () > sd.installation_access.expires_at) { - if (new_iat = get_iat ()) + if ((new_iat = get_iat ())) iat = &*new_iat; else throw server_error (); -- cgit v1.1