From 6cb77d97d659441036fac18af6479eeac4127dcb Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 29 Mar 2024 21:13:23 +0300 Subject: Stash build toolchain in tenant object --- libbrep/build-extra.sql | 9 ++++- libbrep/build-package.hxx | 1 + libbrep/build.hxx | 2 +- libbrep/build.xml | 2 + libbrep/common.hxx | 9 +++++ libbrep/package.hxx | 23 +++++++++++- libbrep/package.xml | 12 ++++++ mod/mod-build-task.cxx | 93 ++++++++++++++++++++++++++++++++++++----------- mod/mod-builds.cxx | 29 +++++++++++---- mod/page.cxx | 14 +++++-- mod/page.hxx | 13 ++++--- 11 files changed, 167 insertions(+), 40 deletions(-) diff --git a/libbrep/build-extra.sql b/libbrep/build-extra.sql index 374cf73..a931f31 100644 --- a/libbrep/build-extra.sql +++ b/libbrep/build-extra.sql @@ -44,7 +44,14 @@ CREATE FOREIGN TABLE build_tenant ( service_id TEXT NULL, service_type TEXT NULL, service_data TEXT NULL, - queued_timestamp BIGINT NULL) + queued_timestamp BIGINT NULL, + toolchain_name TEXT OPTIONS (column_name 'build_toolchain_name') NULL, + toolchain_version_epoch INTEGER OPTIONS (column_name 'build_toolchain_version_epoch') NULL, + toolchain_version_canonical_upstream TEXT OPTIONS (column_name 'build_toolchain_version_canonical_upstream') NULL, + toolchain_version_canonical_release TEXT OPTIONS (column_name 'build_toolchain_version_canonical_release') NULL, + toolchain_version_revision INTEGER OPTIONS (column_name 'build_toolchain_version_revision') NULL, + toolchain_version_upstream TEXT OPTIONS (column_name 'build_toolchain_version_upstream') NULL, + toolchain_version_release TEXT OPTIONS (column_name 'build_toolchain_version_release') 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 8377158..08fb781 100644 --- a/libbrep/build-package.hxx +++ b/libbrep/build-package.hxx @@ -37,6 +37,7 @@ namespace brep bool archived; optional service; optional queued_timestamp; + optional toolchain; // Database mapping. // diff --git a/libbrep/build.hxx b/libbrep/build.hxx index be7cdf5..4c470cd 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, 25, closed) +#pragma db model version(LIBBREP_BUILD_SCHEMA_VERSION_BASE, 26, 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 0dc37ee..815c915 100644 --- a/libbrep/build.xml +++ b/libbrep/build.xml @@ -1,4 +1,6 @@ + + diff --git a/libbrep/common.hxx b/libbrep/common.hxx index 6220149..ea18fd4 100644 --- a/libbrep/common.hxx +++ b/libbrep/common.hxx @@ -354,6 +354,15 @@ namespace brep #pragma db value(build_auxiliary) definition + // build_toolchain + // + #pragma db value + struct build_toolchain + { + string name; + brep::version version; + }; + // email // using bpkg::email; diff --git a/libbrep/package.hxx b/libbrep/package.hxx index 96e51a3..b8c3c33 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, 31, closed) +#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 32, closed) namespace brep { @@ -286,6 +286,27 @@ namespace brep // optional queued_timestamp; // Note: foreign-mapped in build. + // Note that after the package tenant is created but before the first + // build object is created, there is no easy way to produce a list of + // unbuilt package configurations. That would require to know the build + // toolchain(s), which are normally extracted from the build objects. + // Thus, the empty unbuilt package configurations list is ambiguous and + // can either mean that no more package configurations can be built or + // that we have not enough information to produce the list. To + // disambiguate the empty list in the interface, in the latter case we + // want to display the question mark instead of 0 as an unbuilt package + // configurations count. To achieve this we will stash the build toolchain + // in the tenant when a package from this tenant is considered for a build + // for the first time but no configuration is picked for the build (the + // target configurations are excluded, an auxiliary machine is not + // available, etc). We will also use the stashed toolchain as a fallback + // until we are able to retrieve the toolchain(s) from the tenant builds + // to produce the unbuilt package configurations list. + // + // Note: foreign-mapped in build. + // + optional build_toolchain; + // Database mapping. // #pragma db member(id) id diff --git a/libbrep/package.xml b/libbrep/package.xml index 966861b..b66a66d 100644 --- a/libbrep/package.xml +++ b/libbrep/package.xml @@ -1,4 +1,16 @@ + + + + + + + + + + + + diff --git a/mod/mod-build-task.cxx b/mod/mod-build-task.cxx index e2f69f7..773d041 100644 --- a/mod/mod-build-task.cxx +++ b/mod/mod-build-task.cxx @@ -231,7 +231,7 @@ handle (request& rq, response& rs) // The resulting task manifest and the related build, package, and // configuration objects. Note that the latter 3 are only meaningful if the - // session in the task manifest is not empty. + // the task manifest is present. // task_response_manifest task_response; shared_ptr task_build; @@ -1344,9 +1344,9 @@ handle (request& rq, response& rs) // bool unforced (true); - for (bool done (false); task_response.session.empty () && !done; ) + for (bool done (false); !task_response.task && !done; ) { - transaction t (conn->begin ()); + transaction tr (conn->begin ()); // We need to be careful in the random package ordering mode not to // miss the end after having wrapped around. @@ -1381,7 +1381,7 @@ handle (request& rq, response& rs) // if (chunk_size == 0) { - t.commit (); + tr.commit (); if (start_offset != 0 && offset >= start_offset) offset = 0; @@ -1398,12 +1398,24 @@ handle (request& rq, response& rs) // to bail out in the random package ordering mode for some reason (no // more untried positions, need to restart, etc). // + // Note that it is not uncommon for the sequentially examined packages + // to belong to the same tenant (single tenant mode, etc). Thus, we + // will cache the loaded tenant objects. + // + shared_ptr t; + for (auto& bp: packages) { shared_ptr& p (bp.package); id = p->id; + // Reset the tenant cache if the current package belongs to a + // different tenant. + // + if (t != nullptr && t->id != id.tenant) + t = nullptr; + // If we are in the random package ordering mode, then cache the // tenant the start offset refers to, if not cached yet, and check // if we are still iterating over packages from this tenant @@ -1463,8 +1475,6 @@ handle (request& rq, response& rs) // if (bp.interactive) { - shared_ptr t; - // Note that the tenant can be archived via some other package on // some previous iteration. Skip the package if that's the case. // @@ -1473,7 +1483,9 @@ handle (request& rq, response& rs) // if (!bp.archived) { - t = build_db_->load (id.tenant); + if (t == nullptr) + t = build_db_->load (id.tenant); + bp.archived = t->archived; } @@ -1583,6 +1595,14 @@ handle (request& rq, response& rs) } } + // If true, then the package is (being) built for some + // configurations. + // + // Note that since we only query the built and forced rebuild + // objects there can be false negatives. + // + bool package_built (false); + for (const build_package_config& pc: p->configs) { pkg_config = pc.name; @@ -1597,6 +1617,9 @@ handle (request& rq, response& rs) config_machines configs (conf_machines); // Make copy for this pkg. auto pkg_builds (bld_prep_query.execute ()); + if (!package_built && !pkg_builds.empty ()) + package_built = true; + for (auto i (pkg_builds.begin ()); i != pkg_builds.end (); ++i) { auto j ( @@ -1750,8 +1773,8 @@ handle (request& rq, response& rs) build_db_->update (b); } - shared_ptr t ( - build_db_->load (b->tenant)); + if (t == nullptr) + t = build_db_->load (b->tenant); // Archive an interactive tenant. // @@ -1798,7 +1821,7 @@ handle (request& rq, response& rs) } if (tsb != nullptr || tsq != nullptr) - tss = make_pair (move (*t->service), b); + tss = make_pair (*t->service, b); } } @@ -1814,25 +1837,51 @@ handle (request& rq, response& rs) task_package = move (p); task_config = &pc; + package_built = true; + break; // Bail out from the package configurations loop. } } } - // If the task response manifest is prepared, then bail out from the - // package loop, commit the transaction and respond. + // If the task manifest is prepared, then bail out from the package + // loop, commit the transaction and respond. Otherwise, stash the + // build toolchain into the tenant, unless it is already stashed or + // the current package already has some configurations (being) + // built. // - if (!task_response.session.empty ()) + if (!task_response.task) + { + // Note that since there can be false negatives for the + // package_built flag (see above), there can be redundant tenant + // queries which, however, seems harmless (query uses the primary + // key and the object memory footprint is small). + // + if (!package_built) + { + if (t == nullptr) + t = build_db_->load (p->id.tenant); + + if (!t->toolchain) + { + t->toolchain = build_toolchain {toolchain_name, + toolchain_version}; + + build_db_->update (t); + } + } + } + else break; } - t.commit (); + tr.commit (); } // If we don't have an unbuilt package, then let's see if we have a // build configuration to rebuild. // - if (task_response.session.empty () && !rebuilds.empty ()) + if (!task_response.task && !rebuilds.empty ()) { // Sort the configuration rebuild list with the following sort // priority: @@ -2047,15 +2096,15 @@ handle (request& rq, response& rs) catch (const odb::deadlock&) { // Just try with the next rebuild. But first, reset the task - // response manifest that we may have prepared. + // manifest and the session that we may have prepared. // task_response = task_response_manifest (); } - // If the task response manifest is prepared, then bail out from the - // package configuration rebuilds loop and respond. + // If the task manifest is prepared, then bail out from the package + // configuration rebuilds loop and respond. // - if (!task_response.session.empty ()) + if (task_response.task) break; } } @@ -2135,9 +2184,9 @@ handle (request& rq, response& rs) update_tenant_service_state (conn, b.tenant, f); } - // If the task response manifest is prepared, then check that the number - // of the build auxiliary machines is less than 10. If that's not the - // case, then turn the build into the built state with the abort status. + // If the task manifest is prepared, then check that the number of the + // build auxiliary machines is less than 10. If that's not the case, + // then turn the build into the built state with the abort status. // if (task_response.task && task_response.task->auxiliary_machines.size () > 9) diff --git a/mod/mod-builds.cxx b/mod/mod-builds.cxx index b0de618..30562f3 100644 --- a/mod/mod-builds.cxx +++ b/mod/mod-builds.cxx @@ -393,8 +393,11 @@ handle (request& rq, response& rs) if (!tenant.empty ()) tn = tenant; - // Return the list of distinct toolchain name/version pairs. The build db - // transaction must be started. + // Return the list of distinct toolchain name/version pairs. If no builds + // are present for the tenant, then fallback to the toolchain recorded in + // the tenant object, if present. + // + // Note: the build db transaction must be started. // using toolchains = vector>; @@ -410,11 +413,19 @@ handle (request& rq, response& rs) false /* first */))) r.emplace_back (move (t.name), move (t.version)); + if (r.empty ()) + { + shared_ptr t (build_db_->find (tenant)); + + if (t != nullptr && t->toolchain) + r.emplace_back (t->toolchain->name, t->toolchain->version); + } + return r; }; auto print_form = [&s, ¶ms, this] (const toolchains& toolchains, - size_t build_count) + optional build_count) { // Print the package builds filter form on the first page only. // @@ -525,7 +536,7 @@ handle (request& rq, response& rs) conf_ids.push_back (c.first); } - size_t count; + optional count; size_t page (params.page ()); if (params.result () != "unbuilt") // Print package build configurations. @@ -656,7 +667,7 @@ handle (request& rq, response& rs) --print; } - ++count; + ++(*count); } } @@ -937,7 +948,7 @@ handle (request& rq, response& rs) count = npos - ncur; } else - count = 0; + count = nullopt; // Unknown count. } // Print the filter form. @@ -1148,7 +1159,11 @@ handle (request& rq, response& rs) add_filter ("pc", pkg_cfg); add_filter ("rs", params.result (), "*"); - s << DIV_PAGER (page, count, page_configs, options_->build_pages (), u) + s << DIV_PAGER (page, + count ? *count : 0, + page_configs, + options_->build_pages (), + u) << ~DIV << ~BODY << ~HTML; diff --git a/mod/page.cxx b/mod/page.cxx index 5483183..bc2e42d 100644 --- a/mod/page.cxx +++ b/mod/page.cxx @@ -137,9 +137,17 @@ namespace brep void DIV_COUNTER:: operator() (serializer& s) const { - s << DIV(ID="count") - << count_ << " " - << (count_ % 10 == 1 && count_ % 100 != 11 ? singular_ : plural_) + s << DIV(ID="count"); + + if (count_) + s << *count_; + else + s << '?'; + + s << ' ' + << (count_ && *count_ % 10 == 1 && *count_ % 100 != 11 + ? singular_ + : plural_) << ~DIV; } diff --git a/mod/page.hxx b/mod/page.hxx index cac2b8b..7329e2d 100644 --- a/mod/page.hxx +++ b/mod/page.hxx @@ -82,21 +82,24 @@ namespace brep // Generate counter element. // - // It could be redunant to distinguish between singular and plural word forms - // if it wouldn't be so cheap in English, and phrase '1 Packages' wouldn't - // look that ugly. + // If the count argument is nullopt, then it is assumed that the count is + // unknown and the '?' character is printed instead of the number. + // + // Note that it could be redunant to distinguish between singular and plural + // word forms if it wouldn't be so cheap in English, and phrase '1 Packages' + // wouldn't look that ugly. // class DIV_COUNTER { public: - DIV_COUNTER (size_t c, const char* s, const char* p) + DIV_COUNTER (optional c, const char* s, const char* p) : count_ (c), singular_ (s), plural_ (p) {} void operator() (xml::serializer&) const; private: - size_t count_; + optional count_; const char* singular_; const char* plural_; }; -- cgit v1.1