lix/tests/functional/meson.build
raito 2a7a6cb85a fix(local-store): invalidate phantom referrers at garbage collection time
Sometimes, a path can disappear from the `ValidPaths` table (I have 23
such cases on my 1.4TB Nix store).

When this occurs and you try to run a garbage collection,
`queryReferrers` will report no referrer because it's performing a
*RIGHT JOIN* between `Refs` and `ValidPaths`, finally, when you issue
the deletion SQL statement, this will throw an uncaught exception from
SQLite side regarding a foreign key violation because `reference` in
`Refs` is a foreign key to `ValidPaths` (which we are trying to delete).

Why can this happen?

Two reasons:

* `PRAGMA foreign_keys=off;` will disable deletion on cascade.
* Trigger recursion *limits*, a deletion on cascade is a *trigger*, when
  a delete is issued and it triggers a bunch of deletion on cascade,
  there's a documented limit by SQLite: https://www.sqlite.org/limits.html#max_trigger_depth

  > Recursion limit on foreign key actions. The SQLITE_MAX_TRIGGER_DEPTH
  > and SQLITE_LIMIT_TRIGGER_DEPTH settings determine the maximum
  > allowable depth of trigger program recursion. For the purposes of
  > these limits, foreign key actions are considered trigger programs. The
  > PRAGMA recursive_triggers setting does not affect the operation of
  > foreign key actions. It is not possible to disable recursive foreign
  > key actions.

As I do not see easy ways to solve the root cause, garbage collection
should be self-healing in that regards, so I propose to invalidate
phantom referrers as we go.

As part of a work improving the consistency of the SQLite database, it
would make sense to count how many times this happen and try to find
ways to reproduce this issue.

Change-Id: I055a8a1d8c0e44d4388a411abe8e5a5e385f7b55
Signed-off-by: Raito Bezarius <raito@lix.systems>
2024-09-14 20:09:17 +02:00

248 lines
6.1 KiB
Meson

test_confdata = {
'bindir': bindir,
'coreutils': fs.parent(coreutils.full_path()),
'lsof': lsof.full_path(),
'dot': dot.found() ? dot.full_path() : '',
'bash': bash.full_path(),
'sandbox_shell': busybox.found() ? busybox.full_path() : '',
'PACKAGE_VERSION': meson.project_version(),
'system': host_system,
'python': python.full_path(),
'runinpty': runinpty.full_path(),
}
# Just configures `common/vars-and-functions.sh.in`.
# Done as a subdir() so Meson places it under `common` in the build directory as well.
subdir('common')
config_nix_in = configure_file(
input : 'config.nix.in',
output : 'config.nix',
configuration : test_confdata,
)
# Just configures `ca/config.nix.in`. Done as a subdir() for the same reason as above.
subdir('ca')
# Just configures `dyn-drv/config.nix.in`. Same as above.
subdir('dyn-drv')
subdir('plugins')
subdir('test-libstoreconsumer')
functional_tests_scripts = [
'init.sh',
'test-infra.sh',
'ca/build.sh',
'ca/build-cache.sh',
'ca/concurrent-builds.sh',
'ca/derivation-json.sh',
'ca/duplicate-realisation-in-closure.sh',
'ca/eval-store.sh',
'ca/gc.sh',
'ca/import-derivation.sh',
'ca/new-build-cmd.sh',
'ca/nix-copy.sh',
'ca/nix-run.sh',
'ca/nix-shell.sh',
'ca/post-hook.sh',
'ca/recursive.sh',
'ca/repl.sh',
'ca/selfref-gc.sh',
'ca/signatures.sh',
'ca/substitute.sh',
'ca/why-depends.sh',
'dyn-drv/text-hashed-output.sh',
'dyn-drv/recursive-mod-json.sh',
'dyn-drv/build-built-drv.sh',
'dyn-drv/eval-outputOf.sh',
'dyn-drv/dep-built-drv.sh',
'dyn-drv/old-daemon-error-hack.sh',
'flakes/flakes.sh',
'flakes/develop.sh',
'flakes/develop-r8854.sh',
'flakes/run.sh',
'flakes/mercurial.sh',
'flakes/circular.sh',
'flakes/init.sh',
'flakes/inputs.sh',
'flakes/follow-paths.sh',
'flakes/bundle.sh',
'flakes/check.sh',
'flakes/unlocked-override.sh',
'flakes/absolute-paths.sh',
'flakes/build-paths.sh',
'flakes/flake-in-submodule.sh',
'flakes/flake-metadata.sh',
'flakes/flake-registry.sh',
'flakes/subdir-flake.sh',
'gc.sh',
'phantom-referrers-gc.sh',
'nix-collect-garbage-d.sh',
'nix-collect-garbage-dry-run.sh',
'remote-store.sh',
'legacy-ssh-store.sh',
'lang.sh',
'lang-test-infra.sh',
'experimental-features.sh',
'fetchMercurial.sh',
'gc-auto.sh',
'user-envs.sh',
'user-envs-migration.sh',
'binary-cache.sh',
'multiple-outputs.sh',
'nix-build.sh',
'gc-concurrent.sh',
'repair.sh',
'fixed.sh',
'export-graph.sh',
'timeout.sh',
'fetchGitRefs.sh',
'gc-runtime.sh',
'tarball.sh',
'fetchers.sh',
'fetchGit.sh',
'fetchurl.sh',
'fetchPath.sh',
'fetchTree-file.sh',
'simple.sh',
'referrers.sh',
'optimise-store.sh',
'substitute-with-invalid-ca.sh',
'signing.sh',
'hash.sh',
'gc-non-blocking.sh',
'check.sh',
'nix-shell.sh',
'check-refs.sh',
'build-remote-input-addressed.sh',
'secure-drv-outputs.sh',
'restricted.sh',
'fetchGitSubmodules.sh',
'flakes/search-root.sh',
'readfile-context.sh',
'nix-channel.sh',
'recursive.sh',
'dependencies.sh',
'check-reqs.sh',
'build-remote-content-addressed-fixed.sh',
'build-remote-content-addressed-floating.sh',
'build-remote-trustless-should-pass-0.sh',
'build-remote-trustless-should-pass-1.sh',
'build-remote-trustless-should-pass-2.sh',
'build-remote-trustless-should-pass-3.sh',
'build-remote-trustless-should-fail-0.sh',
'build-jobless.sh',
'nar-access.sh',
'impure-eval.sh',
'pure-eval.sh',
'eval.sh',
'repl.sh',
'binary-cache-build-remote.sh',
'search.sh',
'logging.sh',
'export.sh',
'config.sh',
'add.sh',
'local-store.sh',
'filter-source.sh',
'misc.sh',
'dump-db.sh',
'linux-sandbox.sh',
'supplementary-groups.sh',
'build-dry.sh',
'structured-attrs.sh',
'shell.sh',
'brotli.sh',
'zstd.sh',
'compression-levels.sh',
'nix-copy-ssh.sh',
'nix-copy-ssh-ng.sh',
'post-hook.sh',
'function-trace.sh',
'flakes/config.sh',
'fmt.sh',
'eval-store.sh',
'why-depends.sh',
'derivation-json.sh',
'import-derivation.sh',
'nix_path.sh',
'case-hack.sh',
'placeholders.sh',
'ssh-relay.sh',
'build.sh',
'build-delete.sh',
'output-normalization.sh',
'selfref-gc.sh',
'db-migration.sh',
'bash-profile.sh',
'pass-as-file.sh',
'nix-profile.sh',
'suggestions.sh',
'store-ping.sh',
'fetchClosure.sh',
'completions.sh',
'flakes/show.sh',
'impure-derivations.sh',
'path-from-hash-part.sh',
'toString-path.sh',
'read-only-store.sh',
'nested-sandboxing.sh',
'debugger.sh',
'test-libstoreconsumer.sh',
'extra-sandbox-profile.sh',
'substitute-truncated-nar.sh',
'regression-484.sh',
]
# Plugin tests require shared libraries support.
if get_option('default_library') != 'static'
functional_tests_scripts += ['plugins.sh']
endif
# TODO(Qyriad): this will hopefully be able to be removed when we remove the autoconf+Make
# buildsystem. See the comments at the top of setup-functional-tests.py for why this is here.
meson.add_install_script(
python,
meson.project_source_root() / 'meson/setup-functional-tests.py',
)
foreach script : functional_tests_scripts
# Turns, e.g., `tests/functional/flakes/show.sh` into a Meson test target called
# `functional-flakes-show`.
name = 'functional-@0@'.format(fs.replace_suffix(script, '')).replace('/', '-')
extra_deps = []
if script == 'plugins.sh'
extra_deps += [
# Set in tests/functional/plugins/meson.build
libplugintest,
libplugintestfail,
]
elif script == 'test-libstoreconsumer.sh'
extra_deps += [
# Set in tests/functional/test-libstoreconsumer/meson.build
libstoreconsumer_tester,
]
endif
test(
name,
python,
args: [
meson.project_source_root() / 'meson/run-test.py',
script,
],
suite : 'installcheck',
env : {
'MESON_BUILD_ROOT': meson.project_build_root(),
},
# some tests take 15+ seconds even on an otherwise idle machine, on a loaded machine
# this can easily drive them to failure. give them more time, 5min rather than 30sec
timeout : 300,
# Used for target dependency/ordering tracking, not adding compiler flags or anything.
depends : extra_deps,
)
endforeach
subdir('repl_characterization')