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_property_value='true'
|
||||||
declare -r zfs_max_name_length=255
|
declare -r zfs_max_name_length=255
|
||||||
|
|
||||||
|
set -xv
|
||||||
|
|
||||||
if [[ -r "${config_file}" ]]; then
|
if [[ -r "${config_file}" ]]; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source "${config_file}"
|
source "${config_file}"
|
||||||
@@ -33,6 +35,7 @@ mixed_suffix="${mixed_suffix:-mixd}"
|
|||||||
package_separator="${package_separator:-:}"
|
package_separator="${package_separator:-:}"
|
||||||
|
|
||||||
declare -a packages operations
|
declare -a packages operations
|
||||||
|
declare existing_snapshots
|
||||||
|
|
||||||
print_msg() {
|
print_msg() {
|
||||||
local level="$1" message="$2"
|
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}"
|
read -r package old_version _old_arch direction new_version _new_arch _multiarch action <<<"${line}"
|
||||||
[[ -n "${package}" ]] || continue
|
[[ -n "${package}" ]] || continue
|
||||||
if [[ "${action}" == '**REMOVE**' ]]; then
|
if [[ "${action}" == '**CONFIGURE**' ]]; then
|
||||||
|
continue
|
||||||
|
elif [[ "${action}" == '**REMOVE**' ]]; then
|
||||||
add_package "${package}" 'remove'
|
add_package "${package}" 'remove'
|
||||||
elif [[ "${direction}" == '>' ]]; then
|
elif [[ "${direction}" == '>' ]]; then
|
||||||
add_package "${package}" 'downgrade'
|
add_package "${package}" 'downgrade'
|
||||||
@@ -157,17 +162,18 @@ shorten_packages() {
|
|||||||
package_list="${value}"
|
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() {
|
make_snapshot_name() {
|
||||||
local counter="$1" dataset base
|
local counter="$1" dataset base
|
||||||
names=()
|
names=()
|
||||||
for dataset in "${snappable_datasets[@]}"; do
|
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; }
|
(( ${#base} <= zfs_max_name_length )) || { print_msg ERR "Snapshot name exceeds ${zfs_max_name_length} characters: ${base}"; return 2; }
|
||||||
names+=("${base}")
|
names+=("${base}")
|
||||||
existing_snapshot "${base}" && return 1
|
existing_snapshot "${base}" && return 1
|
||||||
done
|
done
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
prune_snapshots() {
|
prune_snapshots() {
|
||||||
@@ -196,9 +202,11 @@ main() {
|
|||||||
(( ${#packages[@]} > 0 )) || { print_msg INFO 'No package actions received; skipping snapshot'; return 0; }
|
(( ${#packages[@]} > 0 )) || { print_msg INFO 'No package actions received; skipping snapshot'; return 0; }
|
||||||
get_datasets
|
get_datasets
|
||||||
(( ${#snappable_datasets[@]} > 0 )) || { print_msg INFO "No eligible ZFS datasets; skipping snapshot"; return 0; }
|
(( ${#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}")"
|
date_string="$(TZ="${timezone}" date +"${date_format}")"
|
||||||
severity_and_packages
|
severity_and_packages
|
||||||
shorten_packages
|
shorten_packages
|
||||||
|
operation_suffix_result="$(operation_suffix)"
|
||||||
while :; do
|
while :; do
|
||||||
make_snapshot_name "$((++counter))"
|
make_snapshot_name "$((++counter))"
|
||||||
case "$?" in
|
case "$?" in
|
||||||
|
|||||||
Reference in New Issue
Block a user