fix(apt): cache snapshot collision checks
Cache the complete snapshot-name listing once per hook invocation instead\nof querying ZFS separately for every dataset and counter candidate. This\nremoves the repeated full-pool scan that becomes expensive when recursive\nsnapshot histories are large.\n\nCache the operation suffix as well and return an explicit success status\nfrom snapshot-name generation so collision retries are unambiguous. Ignore\nAPT configuration-only records because they do not represent package data\nchanges requiring a new pre-install snapshot.
This commit is contained in:
14
apt-zfs-snapshot.sh
Normal file → Executable file
14
apt-zfs-snapshot.sh
Normal file → Executable file
@@ -7,6 +7,8 @@ declare -r zfs_property='space.quico:auto-snapshot'
|
||||
declare -r zfs_property_value='true'
|
||||
declare -r zfs_max_name_length=255
|
||||
|
||||
set -xv
|
||||
|
||||
if [[ -r "${config_file}" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "${config_file}"
|
||||
@@ -33,6 +35,7 @@ mixed_suffix="${mixed_suffix:-mixd}"
|
||||
package_separator="${package_separator:-:}"
|
||||
|
||||
declare -a packages operations
|
||||
declare existing_snapshots
|
||||
|
||||
print_msg() {
|
||||
local level="$1" message="$2"
|
||||
@@ -77,7 +80,9 @@ parse_apt_input() {
|
||||
|
||||
read -r package old_version _old_arch direction new_version _new_arch _multiarch action <<<"${line}"
|
||||
[[ -n "${package}" ]] || continue
|
||||
if [[ "${action}" == '**REMOVE**' ]]; then
|
||||
if [[ "${action}" == '**CONFIGURE**' ]]; then
|
||||
continue
|
||||
elif [[ "${action}" == '**REMOVE**' ]]; then
|
||||
add_package "${package}" 'remove'
|
||||
elif [[ "${direction}" == '>' ]]; then
|
||||
add_package "${package}" 'downgrade'
|
||||
@@ -157,17 +162,18 @@ shorten_packages() {
|
||||
package_list="${value}"
|
||||
}
|
||||
|
||||
existing_snapshot() { zfs list -H -t snapshot -o name | grep -Fxq -- "$1"; }
|
||||
existing_snapshot() { grep -Fxq -- "$1" <<<"${existing_snapshots}"; }
|
||||
|
||||
make_snapshot_name() {
|
||||
local counter="$1" dataset base
|
||||
names=()
|
||||
for dataset in "${snappable_datasets[@]}"; do
|
||||
base="${dataset}@${snapshot_prefix}${field_separator}${date_string}${field_separator}${counter}${field_separator}op:$(operation_suffix)${field_separator}sev:${severity}${field_separator}pkgs:${package_list}"
|
||||
base="${dataset}@${snapshot_prefix}${field_separator}${date_string}${field_separator}${counter}${field_separator}op:${operation_suffix_result}${field_separator}sev:${severity}${field_separator}pkgs:${package_list}"
|
||||
(( ${#base} <= zfs_max_name_length )) || { print_msg ERR "Snapshot name exceeds ${zfs_max_name_length} characters: ${base}"; return 2; }
|
||||
names+=("${base}")
|
||||
existing_snapshot "${base}" && return 1
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
prune_snapshots() {
|
||||
@@ -196,9 +202,11 @@ main() {
|
||||
(( ${#packages[@]} > 0 )) || { print_msg INFO 'No package actions received; skipping snapshot'; return 0; }
|
||||
get_datasets
|
||||
(( ${#snappable_datasets[@]} > 0 )) || { print_msg INFO "No eligible ZFS datasets; skipping snapshot"; return 0; }
|
||||
existing_snapshots="$(zfs list -H -t snapshot -o name)"
|
||||
date_string="$(TZ="${timezone}" date +"${date_format}")"
|
||||
severity_and_packages
|
||||
shorten_packages
|
||||
operation_suffix_result="$(operation_suffix)"
|
||||
while :; do
|
||||
make_snapshot_name "$((++counter))"
|
||||
case "$?" in
|
||||
|
||||
Reference in New Issue
Block a user