aboutsummaryrefslogtreecommitdiff
path: root/clean
diff options
context:
space:
mode:
Diffstat (limited to 'clean')
-rw-r--r--clean/buildfile8
-rw-r--r--clean/clean.cli5
-rw-r--r--clean/clean.cxx210
3 files changed, 155 insertions, 68 deletions
diff --git a/clean/buildfile b/clean/buildfile
index a183ff5..b91b1a0 100644
--- a/clean/buildfile
+++ b/clean/buildfile
@@ -1,5 +1,4 @@
# file : clean/buildfile
-# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
import libs = libodb%lib{odb}
@@ -8,9 +7,14 @@ import libs += libbutl%lib{butl}
import libs += libbbot%lib{bbot}
include ../libbrep/
+include ../mod/
exe{brep-clean}: {hxx ixx cxx}{* -clean-options} {hxx ixx cxx}{clean-options} \
- ../libbrep/lib{brep} $libs
+ ../mod/libue{mod} ../libbrep/lib{brep} $libs
+
+# Build options.
+#
+obj{clean}: cxx.poptions += -DBREP_COPYRIGHT=\"$copyright\"
# Generated options parser.
#
diff --git a/clean/clean.cli b/clean/clean.cli
index 434b32f..d3be4d6 100644
--- a/clean/clean.cli
+++ b/clean/clean.cli
@@ -1,5 +1,4 @@
// file : clean/clean.cli
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
include <vector>;
@@ -128,8 +127,8 @@ Fatal error.|
\li|\cb{2}
-An instance of \cb{brep-clean} or \l{brep-migrate(1)} is already running. Try
-again.|
+An instance of \cb{brep-clean} or some other \cb{brep} utility is already
+running. Try again.|
\li|\cb{3}
diff --git a/clean/clean.cxx b/clean/clean.cxx
index a48308b..80c688b 100644
--- a/clean/clean.cxx
+++ b/clean/clean.cxx
@@ -1,5 +1,4 @@
// file : clean/clean.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
#include <map>
@@ -13,9 +12,7 @@
#include <odb/pgsql/database.hxx>
-#include <libbutl/pager.mxx>
-
-#include <libbbot/build-config.hxx>
+#include <libbutl/pager.hxx>
#include <libbrep/build.hxx>
#include <libbrep/build-odb.hxx>
@@ -25,10 +22,11 @@
#include <libbrep/build-package-odb.hxx>
#include <libbrep/database-lock.hxx>
+#include <mod/build-target-config.hxx>
+
#include <clean/clean-options.hxx>
using namespace std;
-using namespace bbot;
using namespace odb::core;
namespace brep
@@ -62,7 +60,7 @@ namespace brep
<< "libbbot " << LIBBBOT_VERSION_ID << endl
<< "libbpkg " << LIBBPKG_VERSION_ID << endl
<< "libbutl " << LIBBUTL_VERSION_ID << endl
- << "Copyright (c) 2014-2019 Code Synthesis Ltd" << endl
+ << "Copyright (c) " << BREP_COPYRIGHT << "." << endl
<< "This is free software released under the MIT license." << endl;
return 0;
@@ -112,8 +110,8 @@ namespace brep
ops.db_port (),
"options='-c default_transaction_isolation=serializable'");
- // Prevent several brep-clean/migrate instances from updating build
- // database simultaneously.
+ // Prevent several brep utility instances from updating the database
+ // simultaneously.
//
database_lock l (db);
@@ -206,12 +204,13 @@ namespace brep
return 1;
}
- set<string> configs;
+ // Load build target configurations.
+ //
+ build_target_configs configs;
try
{
- for (auto& c: parse_buildtab (cp))
- configs.emplace (move (c.name));
+ configs = bbot::parse_buildtab (cp);
}
catch (const io_error& e)
{
@@ -219,6 +218,13 @@ namespace brep
return 1;
}
+ // Note: contains shallow references to the configuration targets/names.
+ //
+ set<build_target_config_id> configs_set;
+
+ for (const build_target_config& c: configs)
+ configs_set.insert (build_target_config_id {c.target, c.name});
+
// Parse timestamps.
//
map<string, timestamp> timeouts; // Toolchain timeouts.
@@ -260,18 +266,26 @@ namespace brep
//
// Query package builds in chunks in order not to hold locks for too long.
// Sort the result by package version to minimize number of queries to the
- // package database.
+ // package database. Note that we still need to sort by configuration and
+ // toolchain to make sure that builds are sorted consistently across
+ // queries and we don't miss any of them.
//
using bld_query = query<build>;
using prep_bld_query = prepared_query<build>;
size_t offset (0);
bld_query bq ("ORDER BY" +
- bld_query::id.package.tenant + "," +
- bld_query::id.package.name +
+ bld_query::id.package.tenant + "," +
+ bld_query::id.package.name +
order_by_version_desc (bld_query::id.package.version,
- false) +
- "OFFSET" + bld_query::_ref (offset) + "LIMIT 100");
+ false) + "," +
+ bld_query::id.target + "," +
+ bld_query::id.target_config_name + "," +
+ bld_query::id.package_config_name + "," +
+ bld_query::id.toolchain_name +
+ order_by_version (bld_query::id.toolchain_version,
+ false /* first */) +
+ "OFFSET" + bld_query::_ref (offset) + "LIMIT 2000");
connection_ptr conn (db.connection ());
@@ -285,76 +299,143 @@ namespace brep
// be made once per tenant package name due to the builds query sorting
// criteria (see above).
//
- using pkg_query = query<buildable_package>;
- using prep_pkg_query = prepared_query<buildable_package>;
+ using pkg_query = query<build_package_version>;
+ using prep_pkg_query = prepared_query<build_package_version>;
string tnt;
package_name pkg_name;
set<version> package_versions;
- pkg_query pq (
- pkg_query::build_package::id.tenant == pkg_query::_ref (tnt) &&
- pkg_query::build_package::id.name == pkg_query::_ref (pkg_name));
+ pkg_query pq (pkg_query::buildable &&
+ pkg_query::id.tenant == pkg_query::_ref (tnt) &&
+ pkg_query::id.name == pkg_query::_ref (pkg_name));
prep_pkg_query pkg_prep_query (
- conn->prepare_query<buildable_package> ("package-query", pq));
+ conn->prepare_query<build_package_version> ("package-query", pq));
+
+ // On the recoverable database error we will retry querying/traversing
+ // builds in the current chunk, up to 10 times. If we still end up with
+ // the recoverable database error, then just skip this builds chunk.
+ //
+ const size_t max_retries (10);
+ size_t retry (max_retries);
+
+ // If we fail to erase some builds due to the recoverable database error
+ // and no builds are erased during this run, then we terminate with the
+ // exit code 3 (recoverable database error).
+ //
+ bool erased (false);
+ optional<string> re;
for (bool ne (true); ne; )
{
- transaction t (conn->begin ());
-
- // Query builds.
- //
- auto builds (bld_prep_query.execute ());
+ size_t n (0);
- if ((ne = !builds.empty ()))
+ try
{
- for (const auto& b: builds)
- {
- auto i (timeouts.find (b.toolchain_name));
+ transaction t (conn->begin ());
- timestamp et (i != timeouts.end ()
- ? i->second
- : default_timeout);
+ // Query builds.
+ //
+ auto builds (bld_prep_query.execute ());
- bool cleanup (
- // Check that the build is not stale.
- //
- b.timestamp <= et ||
+ n = builds.size ();
- // Check that the build configuration is still present.
- //
- // Note that we unable to detect configuration changes and rely on
- // periodic rebuilds to take care of that.
- //
- configs.find (b.configuration) == configs.end ());
+ size_t not_erased (0);
- // Check that the build package still exists.
- //
- if (!cleanup)
+ if ((ne = (n != 0)))
+ {
+ for (const auto& b: builds)
{
- if (tnt != b.tenant || pkg_name != b.package_name)
+ auto i (timeouts.find (b.toolchain_name));
+
+ timestamp et (i != timeouts.end ()
+ ? i->second
+ : default_timeout);
+
+ // Note that we don't consider the case when both the
+ // configuration and the package still exist but the package now
+ // excludes the configuration (configuration is now of the legacy
+ // class instead of the default class, etc). Should we handle this
+ // case and re-implement in a way brep-monitor does it? Probably
+ // not since the described situation is not very common and
+ // storing some extra builds which sooner or later will be wiped
+ // out due to the timeout is harmless. The current implementation,
+ // however, is simpler and consumes less resources in runtime
+ // (doesn't load build package objects, etc).
+ //
+ bool cleanup (
+ // Check that the build is not stale.
+ //
+ b.timestamp <= et ||
+
+ // Check that the build configuration is still present.
+ //
+ // Note that we unable to detect configuration changes and rely
+ // on periodic rebuilds to take care of that.
+ //
+ configs_set.find (
+ build_target_config_id {
+ b.target, b.target_config_name}) == configs_set.end ());
+
+ // Check that the build package still exists.
+ //
+ if (!cleanup)
{
- tnt = b.tenant;
- pkg_name = b.package_name;
- package_versions.clear ();
-
- for (auto& p: pkg_prep_query.execute ())
- package_versions.emplace (move (p.version));
+ if (tnt != b.tenant || pkg_name != b.package_name)
+ {
+ tnt = b.tenant;
+ pkg_name = b.package_name;
+ package_versions.clear ();
+
+ for (auto& p: pkg_prep_query.execute ())
+ package_versions.emplace (move (p.version));
+ }
+
+ cleanup = package_versions.find (b.package_version) ==
+ package_versions.end ();
}
- cleanup = package_versions.find (b.package_version) ==
- package_versions.end ();
+ if (cleanup)
+ db.erase (b);
+ else
+ ++not_erased;
}
+ }
+
+ t.commit ();
+
+ if (!erased)
+ erased = (not_erased != n);
+
+ offset += not_erased;
+ retry = max_retries;
+ }
+ catch (const recoverable& e)
+ {
+ // Re-iterate over the current builds chunk, unless there are no more
+ // attempts left. In the later case stash the error message, if not
+ // stashed yet, and skip the current builds chunk.
+ //
+ if (retry-- == 0)
+ {
+ offset += n;
+ retry = max_retries;
- if (cleanup)
- db.erase (b);
- else
- ++offset;
+ if (!re)
+ re = e.what ();
}
+
+ tnt = "";
+ pkg_name = package_name ();
+ package_versions.clear ();
}
+ }
- t.commit ();
+ if (re && !erased)
+ {
+ cerr << "recoverable database error: " << *re << endl;
+ return 3;
}
return 0;
@@ -452,8 +533,8 @@ namespace brep
auto tenant_ids (pq.execute ());
if ((ne = !tenant_ids.empty ()))
{
- // Cache tenant ids and erase packages, repositories, and tenants at
- // once.
+ // Cache tenant ids and erase packages, repositories, public keys, and
+ // tenants at once.
//
strings tids;
tids.reserve (tenant_ids.size ());
@@ -469,6 +550,9 @@ namespace brep
db.erase_query<repository> (
query<repository>::id.tenant.in_range (tids.begin (), tids.end ()));
+ db.erase_query<public_key> (
+ query<public_key>::id.tenant.in_range (tids.begin (), tids.end ()));
+
db.erase_query<tenant> (
query<tenant>::id.in_range (tids.begin (), tids.end ()));
}