From d4900d85f7a5d791f89821713d02d3dd19361044 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 22 Feb 2024 11:17:25 +0300 Subject: Add support for tenant-associated service notifications --- libbrep/build-extra.sql | 6 ++- libbrep/build-package.hxx | 2 + libbrep/build.cxx | 94 ++++++++++++++++++++++++++++++++++++++++++++++- libbrep/build.hxx | 43 +++++++++++++++++++--- libbrep/build.xml | 2 + libbrep/common.hxx | 30 +++++++++++++++ libbrep/package.cxx | 8 +++- libbrep/package.hxx | 43 +++++++++++++++++++--- libbrep/package.xml | 16 ++++++++ 9 files changed, 229 insertions(+), 15 deletions(-) (limited to 'libbrep') diff --git a/libbrep/build-extra.sql b/libbrep/build-extra.sql index 23015f4..c15ddc1 100644 --- a/libbrep/build-extra.sql +++ b/libbrep/build-extra.sql @@ -36,7 +36,11 @@ CREATE FOREIGN TABLE build_tenant ( id TEXT NOT NULL, private BOOLEAN NOT NULL, interactive TEXT NULL, - archived BOOLEAN NOT NULL) + archived BOOLEAN NOT NULL, + service_id TEXT NULL, + service_type TEXT NULL, + service_data TEXT NULL, + queued_timestamp BIGINT NULL) SERVER package_server OPTIONS (table_name 'tenant'); -- The foreign table for build_repository object. diff --git a/libbrep/build-package.hxx b/libbrep/build-package.hxx index 6d7fb14..a0e1082 100644 --- a/libbrep/build-package.hxx +++ b/libbrep/build-package.hxx @@ -35,6 +35,8 @@ namespace brep bool private_; optional interactive; bool archived; + optional service; + optional queued_timestamp; // Database mapping. // diff --git a/libbrep/build.cxx b/libbrep/build.cxx index c8a2cd1..8fadfa3 100644 --- a/libbrep/build.cxx +++ b/libbrep/build.cxx @@ -12,6 +12,7 @@ namespace brep { switch (s) { + case build_state::queued: return "queued"; case build_state::building: return "building"; case build_state::built: return "built"; } @@ -22,7 +23,8 @@ namespace brep build_state to_build_state (const string& s) { - if (s == "building") return build_state::building; + if (s == "queued") return build_state::queued; + else if (s == "building") return build_state::building; else if (s == "built") return build_state::built; else throw invalid_argument ("invalid build state '" + s + '\''); } @@ -91,6 +93,96 @@ namespace brep { } + build:: + build (string tnt, + package_name_type pnm, + version pvr, + target_triplet trg, + string tcf, + string pcf, + string tnm, version tvr) + : id (package_id (move (tnt), move (pnm), pvr), + move (trg), + move (tcf), + move (pcf), + move (tnm), tvr), + tenant (id.package.tenant), + package_name (id.package.name), + package_version (move (pvr)), + target (id.target), + target_config_name (id.target_config_name), + package_config_name (id.package_config_name), + toolchain_name (id.toolchain_name), + toolchain_version (move (tvr)), + state (build_state::queued), + timestamp (timestamp_type::clock::now ()), + force (force_state::unforced) + { + } + + build:: + build (build&& b) + : id (move (b.id)), + tenant (id.package.tenant), + package_name (id.package.name), + package_version (move (b.package_version)), + target (id.target), + target_config_name (id.target_config_name), + package_config_name (id.package_config_name), + toolchain_name (id.toolchain_name), + toolchain_version (move (b.toolchain_version)), + state (b.state), + interactive (move (b.interactive)), + timestamp (b.timestamp), + force (b.force), + status (b.status), + soft_timestamp (b.soft_timestamp), + hard_timestamp (b.hard_timestamp), + agent_fingerprint (move (b.agent_fingerprint)), + agent_challenge (move (b.agent_challenge)), + machine (move (b.machine)), + machine_summary (move (b.machine_summary)), + results (move (b.results)), + results_section (move (b.results_section)), + controller_checksum (move (b.controller_checksum)), + machine_checksum (move (b.machine_checksum)), + agent_checksum (move (b.agent_checksum)), + worker_checksum (move (b.worker_checksum)), + dependency_checksum (move (b.dependency_checksum)) + { + } + + build& build:: + operator= (build&& b) + { + if (this != &b) + { + id = move (b.id); + package_version = move (b.package_version); + toolchain_version = move (b.toolchain_version); + state = b.state; + interactive = move (b.interactive); + timestamp = b.timestamp; + force = b.force; + status = b.status; + soft_timestamp = b.soft_timestamp; + hard_timestamp = b.hard_timestamp; + agent_fingerprint = move (b.agent_fingerprint); + agent_challenge = move (b.agent_challenge); + machine = move (b.machine); + machine_summary = move (b.machine_summary); + results = move (b.results); + results_section = move (b.results_section); + controller_checksum = move (b.controller_checksum); + machine_checksum = move (b.machine_checksum); + agent_checksum = move (b.agent_checksum); + worker_checksum = move (b.worker_checksum); + dependency_checksum = move (b.dependency_checksum); + } + + return *this; + } + // build_delay // build_delay:: diff --git a/libbrep/build.hxx b/libbrep/build.hxx index adad535..236f73c 100644 --- a/libbrep/build.hxx +++ b/libbrep/build.hxx @@ -28,7 +28,7 @@ // #define LIBBREP_BUILD_SCHEMA_VERSION_BASE 20 -#pragma db model version(LIBBREP_BUILD_SCHEMA_VERSION_BASE, 23, closed) +#pragma db model version(LIBBREP_BUILD_SCHEMA_VERSION_BASE, 24, 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 @@ -129,8 +129,14 @@ namespace brep // build_state // + // The queued build state is semantically equivalent to a non-existent + // build. It is only used for those tenants, which have a third-party + // service associated that requires the `queued` notifications (see + // mod/tenant-service.hxx for background). + // enum class build_state: std::uint8_t { + queued, building, built }; @@ -212,6 +218,23 @@ namespace brep string controller_checksum, string machine_checksum); + // Create the build object with the queued state. + // + build (string tenant, + package_name_type, version, + target_triplet, + string target_config_name, + string package_config_name, + string toolchain_name, version toolchain_version); + + // Move-only type. + // + build (build&&); + build& operator= (build&&); + + build (const build&) = delete; + build& operator= (const build&) = delete; + build_id id; string& tenant; // Tracks id.package.tenant. @@ -315,9 +338,6 @@ namespace brep #pragma db member(results_section) load(lazy) update(always) - build (const build&) = delete; - build& operator= (const build&) = delete; - private: friend class odb::access; @@ -377,7 +397,7 @@ namespace brep canonical_version version_; }; - // Build of an existing buildable package. + // Builds of existing buildable packages. // #pragma db view \ object(build) \ @@ -408,6 +428,19 @@ namespace brep #pragma db member(result) column("count(" + build::id.package.name + ")") }; + // Ids of existing buildable package builds. + // + #pragma db view object(build) \ + object(build_package inner: \ + brep::operator== (build::id.package, build_package::id) && \ + build_package::buildable) + struct package_build_id + { + build_id id; + + operator build_id& () {return id;} + }; + // Used to track the package build delays since the last build or, if not // present, since the first opportunity to build the package. // diff --git a/libbrep/build.xml b/libbrep/build.xml index 0a25488..e757aba 100644 --- a/libbrep/build.xml +++ b/libbrep/build.xml @@ -1,4 +1,6 @@ + + diff --git a/libbrep/common.hxx b/libbrep/common.hxx index fec22e8..11aae67 100644 --- a/libbrep/common.hxx +++ b/libbrep/common.hxx @@ -127,6 +127,20 @@ namespace brep std::chrono::duration_cast ( \ std::chrono::nanoseconds (?)))) + using optional_timestamp = optional; + using optional_uint64 = optional; + + #pragma db map type(optional_timestamp) as(brep::optional_uint64) \ + to((?) \ + ? std::chrono::duration_cast ( \ + (?)->time_since_epoch ()).count () \ + : brep::optional_uint64 ()) \ + from((?) \ + ? brep::timestamp ( \ + std::chrono::duration_cast ( \ + std::chrono::nanoseconds (*(?)))) \ + : brep::optional_timestamp ()) + // version // using bpkg::version; @@ -517,6 +531,22 @@ namespace brep #pragma db member(requirement_key::middle) column("alternative_index") #pragma db member(requirement_key::inner) column("index") + // Third-party service state which may optionally be associated with a + // tenant (see also mod/tenant-service.hxx for background). + // + #pragma db value + struct tenant_service + { + string id; + string type; + optional data; + + tenant_service () = default; + + tenant_service (string i, string t, optional d = nullopt) + : id (move (i)), type (move (t)), data (move (d)) {} + }; + // Version comparison operators. // // They allow comparing objects that have epoch, canonical_upstream, diff --git a/libbrep/package.cxx b/libbrep/package.cxx index e5e7767..2320547 100644 --- a/libbrep/package.cxx +++ b/libbrep/package.cxx @@ -40,11 +40,15 @@ namespace brep // tenant // tenant:: - tenant (string i, bool p, optional r) + tenant (string i, + bool p, + optional r, + optional s) : id (move (i)), private_ (p), interactive (move (r)), - creation_timestamp (timestamp::clock::now ()) + creation_timestamp (timestamp::clock::now ()), + service (move (s)) { } diff --git a/libbrep/package.hxx b/libbrep/package.hxx index 06e6335..9bb9af9 100644 --- a/libbrep/package.hxx +++ b/libbrep/package.hxx @@ -20,7 +20,7 @@ // #define LIBBREP_PACKAGE_SCHEMA_VERSION_BASE 27 -#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 29, closed) +#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 30, closed) namespace brep { @@ -241,31 +241,62 @@ namespace brep // Create the tenant object with the timestamp set to now and the archived // flag set to false. // - explicit - tenant (string id, bool private_, optional interactive); + tenant (string id, + bool private_, + optional interactive, + optional); string id; // If true, display the packages in the web interface only in the tenant // view mode. // - bool private_; // Note: foreign-mapped in build. + bool private_; // Note: foreign-mapped in build. // Interactive package build breakpoint. // // If present, then packages from this tenant will only be built // interactively and only non-interactively otherwise. // - optional interactive; // Note: foreign-mapped in build. + optional interactive; // Note: foreign-mapped in build. timestamp creation_timestamp; - bool archived = false; // Note: foreign-mapped in build. + bool archived = false; // Note: foreign-mapped in build. + + optional service; // Note: foreign-mapped in build. + + // Note that due to the implementation complexity and performance + // considerations, the service notifications are not synchronized. This + // leads to a potential race, so that before we have sent the `queued` + // notification for a package build, some other thread (potentially in a + // different process) could have already sent the `building` notification + // for it. It feels like there is no easy way to reliably fix that. + // Instead, we just decrease the probability of such a notifications + // sequence failure by delaying builds of the freshly queued packages for + // some time. Specifically, whenever the `queued` notification is ought + // to be sent (normally out of the database transaction, since it likely + // sends an HTTP request, etc) the tenant's queued_timestamp member is set + // to the current time. During the configured time interval since that + // time point the build tasks may not be issued for the tenant's packages. + // + // Also note that while there are similar potential races for other + // notification sequences, their probability is rather low due to the + // natural reasons (non-zero build task execution time, etc) and thus we + // just ignore them. + // + optional queued_timestamp; // Note: foreign-mapped in build. // Database mapping. // #pragma db member(id) id #pragma db member(private_) + #pragma db index("tenant_service_i") \ + unique \ + members(service.id, service.type) + + #pragma db index member(service.id) + private: friend class odb::access; tenant () = default; diff --git a/libbrep/package.xml b/libbrep/package.xml index bf7915e..6852f75 100644 --- a/libbrep/package.xml +++ b/libbrep/package.xml @@ -1,4 +1,20 @@ + + + + + + + + + + + + + + + + -- cgit v1.1