Posted on
Artificial Intelligence

Artificial Intelligence Database Automation

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

Bash-first AI: Automating Database Ops on Linux

Ever been paged at 2 a.m. because a database suddenly slowed to a crawl? Most “incidents” are patterns you’ve seen before—spikes in transactions, cache hit dips, bloated indexes, or a slow query going wild. Artificial Intelligence (AI) can learn those patterns from your own telemetry and then flag anomalies, trigger safe remediations, and buy you precious time. The best part: you don’t need to rip-and-replace anything. With a few Bash scripts, a lightweight Python model, and standard Linux tools, you can start automating database operations today.

This guide shows you how to:

  • Collect meaningful DB signals with a portable Bash script

  • Detect anomalies using a tiny, train-on-the-fly ML model

  • Trigger safe actions or alerts automatically

  • Schedule the whole pipeline with cron or systemd timers

We’ll focus on PostgreSQL and SQLite for portability, but the pattern applies broadly.


Why AI for database automation?

  • Databases already emit rich, quantitative signals (transactions, buffer hits, I/O). That’s perfect ML fodder.

  • “Toil” tasks (watching dashboards, firing routine ANALYZE, notifying SREs) are repetitive and rule-like—ideal for automation.

  • Lightweight models can run at the edge (on your Linux host) with no expensive infrastructure.

  • Bash is the glue—simple to deploy, easy to read, and integrates with your existing ops stack.


1) Install the toolbox

We’ll use:

  • Bash, jq, curl, git: shell glue and HTTP/webhook helpers

  • Python 3 + venv + pip: to run a tiny anomaly detector

  • SQLite and PostgreSQL client (psql): to demo data collection

Run the commands for your distro:

Debian/Ubuntu (apt):

sudo apt update
sudo apt install -y \
  python3 python3-venv python3-pip \
  sqlite3 postgresql-client \
  jq curl git cron

Fedora/RHEL/CentOS (dnf):

sudo dnf install -y \
  python3 python3-pip python3-virtualenv \
  sqlite postgresql \
  jq curl git cronie

openSUSE (zypper):

sudo zypper refresh
sudo zypper install -y \
  python3 python3-pip python3-virtualenv \
  sqlite3 postgresql \
  jq curl git cron

Set up a Python virtual environment for the anomaly detector:

python3 -m venv .venv
. .venv/bin/activate
pip install --upgrade pip
pip install pandas scikit-learn

Tip: systemd is installed by default on most modern distros, so no package step is needed if you choose the timer approach later.


2) Collect meaningful DB signals (Bash)

The goal: produce a compact CSV of time series features the model can learn from. For PostgreSQL, we’ll read transaction counters and buffer I/O counters from pg_stat_database and compute rates. If Postgres isn’t available, we’ll fall back to a synthetic demo mode so you can try the pipeline end-to-end.

Save as collect_db_metrics.sh:

#!/usr/bin/env bash
set -euo pipefail

# Config
METRICS_CSV="${METRICS_CSV:-./metrics.csv}"
STATE_FILE="${STATE_FILE:-/var/tmp/db_metrics.state}"
PSQL_CONNSTR="${PSQL_CONNSTR:-}"   # e.g. "postgresql://user:pass@host:5432/db?sslmode=require"
DEMO="${DEMO:-0}"                  # DEMO=1 to generate synthetic metrics if no Postgres
NOW_EPOCH="$(date +%s)"
NOW_ISO="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"

# Ensure CSV header
if [ ! -f "$METRICS_CSV" ]; then
  echo "timestamp_iso,tps,cache_hit_ratio" > "$METRICS_CSV"
fi

# Load previous state if present
PREV_EPOCH=0; PREV_XACTS=0; PREV_BLK_READ=0; PREV_BLK_HIT=0
if [ -f "$STATE_FILE" ]; then
  # shellcheck disable=SC1090
  source "$STATE_FILE"
fi

XACTS=0; BLK_READ=0; BLK_HIT=0

if command -v psql >/dev/null 2>&1 && [ -n "$PSQL_CONNSTR" ]; then
  # Query cumulative counters from pg_stat_database
  read -r XACTS BLK_READ BLK_HIT < <(
    psql "$PSQL_CONNSTR" -Atc \
      "SELECT COALESCE(sum(xact_commit+xact_rollback),0),
              COALESCE(sum(blks_read),0),
              COALESCE(sum(blks_hit),0)
       FROM pg_stat_database;"
  )
elif [ "$DEMO" = "1" ]; then
  # Synthetic counters (monotonically increasing with random deltas)
  inc_x=$(( RANDOM % 300 + 200 ))
  inc_r=$(( RANDOM % 500 + 100 ))
  inc_h=$(( RANDOM % 3000 + 2000 ))
  XACTS=$(( PREV_XACTS + inc_x ))
  BLK_READ=$(( PREV_BLK_READ + inc_r ))
  BLK_HIT=$(( PREV_BLK_HIT + inc_h ))
else
  echo "No Postgres connection (PSQL_CONNSTR) and DEMO=0. Nothing to collect." >&2
  exit 1
fi

# Compute deltas and rates
ELAPSED=$(( NOW_EPOCH - PREV_EPOCH ))
if [ "$ELAPSED" -le 0 ]; then ELAPSED=60; fi

DX=$(( XACTS - PREV_XACTS ))
DR=$(( BLK_READ - PREV_BLK_READ ))
DH=$(( BLK_HIT - PREV_BLK_HIT ))
if [ "$DX" -lt 0 ]; then DX=0; fi
if [ "$DR" -lt 0 ]; then DR=0; fi
if [ "$DH" -lt 0 ]; then DH=0; fi

# Transactions per minute (rate-normalized)
TPS=$(awk -v dx="$DX" -v el="$ELAPSED" 'BEGIN{ printf "%.2f", (dx/el)*60 }')

# Cache hit ratio over the last interval
DENOM=$(( DR + DH ))
if [ "$DENOM" -gt 0 ]; then
  CHR=$(awk -v dh="$DH" -v d="$DENOM" 'BEGIN{ printf "%.4f", dh/d }')
else
  CHR="1.0000"
fi

# Append to CSV
echo "$NOW_ISO,$TPS,$CHR" >> "$METRICS_CSV"

# Persist state
cat > "$STATE_FILE" <<EOF
PREV_EPOCH=$NOW_EPOCH
PREV_XACTS=$XACTS
PREV_BLK_READ=$BLK_READ
PREV_BLK_HIT=$BLK_HIT
EOF

echo "collected: ts=$NOW_ISO tps=$TPS cache_hit=$CHR"

Make it executable:

chmod +x collect_db_metrics.sh

Notes:

  • For richer Postgres features (e.g., query latencies), consider enabling pg_stat_statements on the server:

    • postgresql.conf: shared_preload_libraries = 'pg_stat_statements'
    • SQL (superuser): CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
  • Non-superusers may need pg_read_all_stats to query some views.


3) Detect anomalies with a tiny ML model (Python)

We’ll fit an Isolation Forest on recent data and check if the latest row is unusual given history. It’s fast and requires no labels.

Save as detect_anomalies.py:

#!/usr/bin/env python3
import argparse, json, sys
import pandas as pd
from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import StandardScaler

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--csv", default="metrics.csv")
    ap.add_argument("--window", type=int, default=200, help="rows of history to learn from")
    ap.add_argument("--contamination", type=float, default=0.03, help="expected anomaly ratio")
    args = ap.parse_args()

    try:
        df = pd.read_csv(args.csv, parse_dates=["timestamp_iso"])
    except FileNotFoundError:
        print(json.dumps({"ok": False, "error": "metrics.csv not found"}))
        sys.exit(1)

    if len(df) < 20:
        print(json.dumps({"ok": True, "is_anomaly": False, "reason": "insufficient_history"}))
        return

    dfw = df.tail(args.window).copy()
    X = dfw[["tps", "cache_hit_ratio"]].astype(float).values

    scaler = StandardScaler()
    Xs = scaler.fit_transform(X)

    model = IsolationForest(
        n_estimators=200,
        contamination=args.contamination,
        random_state=42,
        n_jobs=1
    )
    model.fit(Xs)

    latest = Xs[-1].reshape(1, -1)
    pred = model.predict(latest)[0]             # -1 anomaly, 1 normal
    score = model.decision_function(latest)[0]  # the more negative, the more anomalous

    out = {
        "ok": True,
        "is_anomaly": (pred == -1),
        "score": float(score),
        "latest": {
            "timestamp": dfw.iloc[-1]["timestamp_iso"].isoformat(),
            "tps": float(dfw.iloc[-1]["tps"]),
            "cache_hit_ratio": float(dfw.iloc[-1]["cache_hit_ratio"])
        }
    }
    print(json.dumps(out))
    return

if __name__ == "__main__":
    main()

Make it executable:

chmod +x detect_anomalies.py

Try it:

# Demo mode (no DB needed)
DEMO=1 ./collect_db_metrics.sh
. .venv/bin/activate
./detect_anomalies.py --csv metrics.csv

4) Automate safe actions (notify, nudge, or analyze)

Glue it together in one script that collects, runs the detector, then decides what to do. Start conservatively: log and notify. Later, you can add guarded remediations (e.g., run ANALYZE off-hours if cache-hit tanks).

Save as auto_remediate.sh:

#!/usr/bin/env bash
set -euo pipefail

# Optional config
WEBHOOK_URL="${WEBHOOK_URL:-}"     # e.g., Slack/Mattermost/Discord incoming webhook
PSQL_CONNSTR="${PSQL_CONNSTR:-}"   # to run optional ANALYZE if you want
SAFE_ANALYZE="${SAFE_ANALYZE:-0}"  # set to 1 to allow ANALYZE when anomaly seen

# 1) Collect latest metrics
./collect_db_metrics.sh >/dev/null

# 2) Run detector (ensure venv or system Python has deps installed)
if [ -f ".venv/bin/python" ]; then
  PY=".venv/bin/python"
else
  PY="python3"
fi
RES="$($PY ./detect_anomalies.py --csv metrics.csv || true)"

# 3) Parse result
OK=$(echo "$RES" | jq -r '.ok // false')
if [ "$OK" != "true" ]; then
  logger -t ai-db-auto "Detector error: $RES"
  exit 0
fi

ANOM=$(echo "$RES" | jq -r '.is_anomaly')
SCORE=$(echo "$RES" | jq -r '.score')
TS=$(echo "$RES" | jq -r '.latest.timestamp')
TPS=$(echo "$RES" | jq -r '.latest.tps')
CHR=$(echo "$RES" | jq -r '.latest.cache_hit_ratio')

if [ "$ANOM" = "true" ]; then
  MSG="Anomaly detected at $TS (score=$SCORE, tps=$TPS, cache_hit=$CHR)"
  echo "$MSG"
  logger -t ai-db-auto "$MSG"

  # Optional: send webhook
  if [ -n "$WEBHOOK_URL" ]; then
    curl -fsSL -X POST -H 'Content-Type: application/json' \
      --data "{\"text\": \"${MSG//\"/\\\"}\"}" \
      "$WEBHOOK_URL" >/dev/null || true
  fi

  # Optional: run a safe ANALYZE (no VACUUM by default)
  if [ "$SAFE_ANALYZE" = "1" ] && command -v psql >/dev/null 2>&1 && [ -n "$PSQL_CONNSTR" ]; then
    psql "$PSQL_CONNSTR" -c "ANALYZE;" || true
    logger -t ai-db-auto "ANALYZE issued after anomaly"
  fi
else
  echo "Normal at $TS (score=$SCORE, tps=$TPS, cache_hit=$CHR)"
fi

Make it executable:

chmod +x auto_remediate.sh

Test end-to-end:

# Demo mode
export DEMO=1
. .venv/bin/activate
./auto_remediate.sh

When ready for Postgres, set:

export DEMO=0
export PSQL_CONNSTR="postgresql://user:pass@host:5432/dbname?sslmode=require"
# Optional notifications
export WEBHOOK_URL="https://hooks.example.com/..."
# Optional guarded remediation
export SAFE_ANALYZE=1

Always test remediations in staging first.


5) Schedule it (cron or systemd)

Pick one scheduler.

Option A: cron (every minute)

  • Debian/Ubuntu (apt): cron is provided by the cron package

  • Fedora/RHEL (dnf): use cronie

  • openSUSE (zypper): use cron

Crontab entry:

crontab -e

Add:

* * * * * cd /path/to/ai-db-auto && /bin/bash ./auto_remediate.sh >> auto.log 2>&1

Option B: systemd timer Create ~/.config/systemd/user/ai-db-auto.service:

[Unit]
Description=AI DB Automation (collect + detect + act)

[Service]
Type=oneshot
WorkingDirectory=%h/path/to/ai-db-auto
Environment=DEMO=0
# Environment=PSQL_CONNSTR=postgresql://user:pass@host:5432/db?sslmode=require
# Environment=WEBHOOK_URL=https://hooks.example.com/...
# Environment=SAFE_ANALYZE=1
ExecStart=/bin/bash -lc './auto_remediate.sh'

Create ~/.config/systemd/user/ai-db-auto.timer:

[Unit]
Description=Run AI DB Automation every minute

[Timer]
OnCalendar=*:0/1
AccuracySec=10s
Persistent=true

[Install]
WantedBy=timers.target

Enable and start:

systemctl --user daemon-reload
systemctl --user enable --now ai-db-auto.timer
systemctl --user status ai-db-auto.timer

Real-world patterns this catches

  • Sudden write or TPS surges: detect traffic spikes before saturation; notify and scale app workers or read replicas.

  • Cache hit dips: hint at workload shifts or outdated stats; trigger a safe ANALYZE during a maintenance window.

  • Slow drift over time: a rising anomaly score across days can warn about capacity issues—plan ahead instead of fire-fighting.

Extend the pipeline by:

  • Adding more features (I/O wait from iostat, disk latency, checkpoint frequency, connection counts).

  • Mining pg_stat_statements for the top regressions and attaching their fingerprints to alerts.

  • Learning per-hour baselines (weekday vs. weekend) by enriching your CSV with calendar features.


Conclusion and next steps

You don’t need a giant AI platform to reduce DB toil. Start with: 1) Install the tools 2) Collect a couple of reliable signals 3) Fit a simple model locally 4) Automate a cautious response

From here, iterate:

  • Add features and thresholds

  • Wire to your incident tooling

  • Promote safe remediations to production after staging tests

If you try this recipe, begin in DEMO mode to validate the loop, then point it at Postgres with read-only stats. Keep actions conservative, measure outcomes, and level up from “notify” to “nudge” to “automate” as confidence grows.

Happy automating—and may your next 2 a.m. page be handled by a Bash script before you even wake up.