For the complete documentation index, see llms.txt. This page is also available as Markdown.

Execution Repository Compression Rollback for Downgrades to Versions Incompatible with Compression

Problem Description

Spinnaker Orca Pull Request #4620 introduced improvements for handling large payloads by storing compressed bodies in the database. Unfortunately, in the case of a rollback to a version which does not support compression - Spinnaker will fail to render executions, because the body would effectively be empty, as it would be stored in a corresponding entry as compressed_body in a table with _compressed_executions appended.

Workaround

Below you will find a set of scripts which would help decompress the compressed_body into the corresponding body field matching the id of the record.

Backup script

#!/bin/bash

# Copyright 2024 Harness, Inc. <a href="#copyright-2024-harness-inc" id="copyright-2024-harness-inc"></a>
#
# Licensed under the Apache License, Version 2.0 (the "License"); <a href="#licensed-under-the-apache-license-version-20-the-license" id="licensed-under-the-apache-license-version-20-the-license"></a>
# you may not use this file except in compliance with the License. <a href="#you-may-not-use-this-file-except-in-compliance-with-the-license" id="you-may-not-use-this-file-except-in-compliance-with-the-license"></a>
# You may obtain a copy of the License at <a href="#you-may-obtain-a-copy-of-the-license-at" id="you-may-obtain-a-copy-of-the-license-at"></a>
#
# http://www.apache.org/licenses/LICENSE-2.0 <a href="#httpwwwapacheorglicenseslicense-20" id="httpwwwapacheorglicenseslicense-20"></a>
#
# Unless required by applicable law or agreed to in writing, software <a href="#unless-required-by-applicable-law-or-agreed-to-in-writing-software" id="unless-required-by-applicable-law-or-agreed-to-in-writing-software"></a>
# distributed under the License is distributed on an "AS IS" BASIS, <a href="#distributed-under-the-license-is-distributed-on-an-as-is-basis" id="distributed-under-the-license-is-distributed-on-an-as-is-basis"></a>
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <a href="#without-warranties-or-conditions-of-any-kind-either-express-or-implied" id="without-warranties-or-conditions-of-any-kind-either-express-or-implied"></a>
# See the License for the specific language governing permissions and <a href="#see-the-license-for-the-specific-language-governing-permissions-and" id="see-the-license-for-the-specific-language-governing-permissions-and"></a>
# limitations under the License. <a href="#limitations-under-the-license" id="limitations-under-the-license"></a>

set -euo pipefail

# Logging function <a href="#logging-function" id="logging-function"></a>
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') $1"
}

# Check if required tools are installed <a href="#check-if-required-tools-are-installed" id="check-if-required-tools-are-installed"></a>
check_tool() {
    if ! command -v "$1" &>/dev/null; then
        log "Error: $1 is not installed, please install it to proceed."
        exit 1
    fi
}

check_tool "mysql"
check_tool "mysqldump"

# Variables <a href="#variables" id="variables"></a>
DB_HOST=""
DB_PORT=""
DB_USER=""
DB_PASS=""
DB_NAME=""
BACKUP_DIR="backup"
THRESHOLD_MB=100

# Usage function <a href="#usage-function" id="usage-function"></a>
usage() {
    echo "Usage: $0 --db-host <host> --db-port <port> --db-user <user> --db-pass <password> --db-name <name> --backup-dir <dir> [--threshold <mb>]"
    exit 1
}

# Parse arguments <a href="#parse-arguments" id="parse-arguments"></a>
while [[ $# -gt 0 ]]; do
    case "$1" in
        --db-host) DB_HOST="$2"; shift ;;
        --db-port) DB_PORT="$2"; shift ;;
        --db-user) DB_USER="$2"; shift ;;
        --db-pass) DB_PASS="$2"; shift ;;
        --db-name) DB_NAME="$2"; shift ;;
        --backup-dir) BACKUP_DIR="$2"; shift ;;
        --threshold) THRESHOLD_MB="$2"; shift ;;
        *) usage ;;
    esac
    shift
done

# Ensure required parameters are set <a href="#ensure-required-parameters-are-set" id="ensure-required-parameters-are-set"></a>
if [[ -z $DB_HOST || -z $DB_PORT || -z $DB_USER || -z $DB_PASS || -z $DB_NAME || -z $BACKUP_DIR ]]; then
    usage
fi

tables=("pipelines" "pipeline_stages" "pipelines_compressed_executions" "pipeline_stages_compressed_executions" \
        "orchestrations" "orchestration_stages" "orchestrations_compressed_executions" "orchestration_stages_compressed_executions")


total_size=0

log "Starting backup process..."
mkdir -p "$BACKUP_DIR" || { log "Error: Could not create backup directory."; exit 1; }

for table in "${tables[@]}"; do
    # Check if the table exists
    table_exists=$(mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USER" -p"$DB_PASS" -D "$DB_NAME" -e \
        "SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = '$DB_NAME' AND TABLE_NAME = '$table';" | tail -n 1)

    if [[ "$table_exists" -eq 0 ]]; then
        log "Warning: Table $table does not exist. Skipping..."
        continue
    fi

    # Calculate table size
    size=$(mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USER" -p"$DB_PASS" -D "$DB_NAME" -e \
        "SELECT ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS size_mb FROM information_schema.TABLES WHERE TABLE_NAME = '$table';" | tail -n 1 | tr -d '[:space:]')

    total_size=$(echo "$total_size + ${size:-0}" | bc)

    # Backup table
    log "Backing up table: $table (Size: ${size}MB)"

    # Prompt user if the backup exceeds the threshold
    if (( $(echo "$size > $THRESHOLD_MB" | bc -l) )); then
        read -p "Backup exceeds ${THRESHOLD_MB}MB. Continue? (y/n): " confirm
        if [[ "$confirm" != "y" ]]; then
            log "Warning: Backup of $table has been skipped..."
            continue
        fi
    fi

    mysqldump -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USER" -p"$DB_PASS" --skip-lock-tables --skip-add-drop-table --skip-add-locks --no-create-db --no-create-info --no-tablespaces "$DB_NAME" "$table" > "$BACKUP_DIR/${table}_backup.sql" || {
        log "Error: Failed to back up table $table."
        exit 1
    }
done

log "Backup completed successfully. Total size: ${total_size}MB."

Rollback script

Restore backup script

Example Usage

  1. Connect to an environment with access to the database

  2. Create the backup script and make a backup of the tables using it, if the option of restoring the data is needed

  3. Create the rollback script

  4. Run the script with the --dry-run option to ensure that there are no errors, and that the decompressed JSONs look valid

  5. Execute the script without the --dry-run option

  6. Validate that the executions are now visible in the UI

Last updated

Was this helpful?