Zum Inhalt

🔄 Transactions

Was ist eine Transaction?

Jede Operation in Polycrate wird als Transaction ausgeführt. Eine Transaction ist eine eindeutig identifizierte Ausführung einer Action mit vollständigem Logging, Event-Generation und Audit-Trail.

Jede Transaction erhält eine eindeutige UUID.

Transaction-Logging ist standardmäßig deaktiviert

Das Logging von Workspace Transactions ist standardmäßig deaktiviert. Das .logs/ Verzeichnis wird primär von Event-Handlers verwendet.

Um Transaction-Logging zu aktivieren, konfigurieren Sie dies in Ihrer workspace.poly:

config:
  logging:
    enabled: true
    path: .logs

Alle folgenden Beispiele setzen voraus, dass Transaction-Logging aktiviert ist.

Wenn Transaction-Logging aktiviert ist, werden Transactions vollständig geloggt in:

.logs/YYYY-MM-DD/<transaction-uuid>.yml

Transaction-Lifecycle

sequenceDiagram
    participant User
    participant CLI
    participant Container
    participant Action
    participant Event
    participant Log

    User->>CLI: polycrate run myblock install
    CLI->>CLI: Generiere TX-ID (UUID)
    CLI->>CLI: Lade Workspace
    CLI->>CLI: Erstelle Snapshot

    CLI->>Container: Starte Container mit TX-ID
    Container->>Action: FĂĽhre Action aus

    alt Action erfolgreich
        Action-->>Container: Exit 0
        Container-->>CLI: Success
        CLI->>Event: Generiere Success-Event
        CLI->>Log: Schreibe TX-Log
        Event->>Webhook: POST Event (optional)
    else Action fehlgeschlagen
        Action-->>Container: Exit 1
        Container-->>CLI: Failure
        CLI->>Event: Generiere Failure-Event
        CLI->>Log: Schreibe TX-Log mit Error
        Event->>Webhook: POST Event (optional)
    end

    CLI->>CLI: Cleanup Container
    CLI-->>User: Ausgabe

    style CLI fill:#e1f5ff
    style Log fill:#ffe1e1
    style Event fill:#fff4e1

Transaction-ID

Jede Transaction erhält eine eindeutige UUID im Format:

550e8400-e29b-41d4-a716-446655440000

Diese ID wird verwendet fĂĽr: - Log-Dateinamen: .logs/2025-01-30/<uuid>.yml - Container-Namen: polycrate-<uuid> - Event-Tracking: Transaction-ID in Events - Git-Commits: Transaction-ID in Commit-Messages (optional) - Debugging: Eindeutige Identifikation einer AusfĂĽhrung

Transaction-Logs

Jede Transaction wird vollständig geloggt. Der Log enthält:

Log-Struktur

# .logs/2025-01-30/550e8400-e29b-41d4-a716-446655440000.yml

transaction:
  id: 550e8400-e29b-41d4-a716-446655440000
  start_time: "2025-01-30T10:30:00Z"
  end_time: "2025-01-30T10:32:15Z"
  duration: 135000  # Millisekunden
  command: "polycrate run my-postgres install"
  exit_code: 0
  status: success  # success oder failure

user:
  name: John Doe
  email: john@example.com
  hostname: workstation.local

workspace:
  name: my-workspace
  path: /home/user/workspaces/my-workspace
  git:
    commit_sha: abc123def456
    branch: main
    dirty: false

block:
  name: my-postgres
  kind: k8sapp
  version: 1.0.0
  from: postgres-base

action:
  name: install
  description: Install PostgreSQL
  playbook: playbooks/install.yml

snapshot:
  # Vollständiger Workspace-Snapshot (siehe snapshot.md)
  workspace: { ... }
  block: { ... }
  inventory: { ... }
  kubeconfig: { ... }

output:
  stdout: |
    Installing PostgreSQL...
    TASK [Install Helm Chart] ******************
    ok: [localhost]
    PLAY RECAP *********************************
    localhost: ok=5 changed=2 failed=0

  stderr: |
    # Fehler-Ausgabe (falls vorhanden)

events:
  - type: transaction_started
    timestamp: "2025-01-30T10:30:00Z"
    sent: true

  - type: transaction_completed
    timestamp: "2025-01-30T10:32:15Z"
    sent: true
    webhook_url: https://hooks.example.com/polycrate
    webhook_status: 200

Log-Speicherort

Logs werden nach Datum organisiert:

.logs/
├── 2025-01-28/
│   ├── 123e4567-e89b-12d3-a456-426614174000.yml
│   ├── 234e5678-e89b-12d3-a456-426614174001.yml
│   └── 345e6789-e89b-12d3-a456-426614174002.yml
├── 2025-01-29/
│   ├── 456e7890-e89b-12d3-a456-426614174003.yml
│   └── 567e8901-e89b-12d3-a456-426614174004.yml
└── 2025-01-30/
    └── 550e8400-e29b-41d4-a716-446655440000.yml

Transaction-Logs anzeigen

Letzte Logs anzeigen

# Alle Logs vom heutigen Tag
ls -lah .logs/$(date +%Y-%m-%d)/

# Letzten Log anzeigen
ls -t .logs/*/*.yml | head -1 | xargs cat

# Letzten Log mit yq formatieren
ls -t .logs/*/*.yml | head -1 | xargs yq

Logs einer bestimmten Transaction

# Transaction-ID aus Ausgabe kopieren:
# "Transaction ID: 550e8400-e29b-41d4-a716-446655440000"

cat .logs/2025-01-30/550e8400-e29b-41d4-a716-446655440000.yml

Logs nach Exit-Code filtern

# Alle fehlgeschlagenen Transactions
grep -r "exit_code: [^0]" .logs/

# Alle erfolgreichen Transactions
grep -r "exit_code: 0" .logs/

# Fehlerhafte Transactions mit Details
find .logs/ -name "*.yml" -exec sh -c '
  if grep -q "exit_code: [^0]" "$1"; then
    echo "=== Failed Transaction: $1 ==="
    yq ".transaction.id, .action.name, .output.stderr" "$1"
  fi
' _ {} \;

Logs nach Block filtern

# Alle Transactions fĂĽr einen Block
find .logs/ -name "*.yml" -exec grep -l "block.name: my-postgres" {} \;

# Transactions fĂĽr einen Block mit Datum
find .logs/ -name "*.yml" -exec sh -c '
  if grep -q "name: my-postgres" "$1"; then
    echo "$1: $(yq ".transaction.start_time" "$1")"
  fi
' _ {} \;

Events

Jede Transaction kann optional Events generieren, die an Webhooks gesendet werden.

Event-Konfiguration

In workspace.poly:

name: my-workspace

events:
  handler: webhook  # oder monk
  endpoint: https://hooks.example.com/polycrate
  commit: true  # Git-Commit nach Transaction

Event-Struktur

Events enthalten den vollständigen Transaction-Kontext:

{
  "transaction": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2025-01-30T10:32:15Z",
    "exit_code": 0,
    "duration": 135000
  },
  "workspace": "my-workspace",
  "block": "my-postgres",
  "action": "install",
  "command": "polycrate run my-postgres install",
  "user": {
    "name": "John Doe",
    "email": "john@example.com"
  },
  "snapshot": { ... },
  "output": "Installing PostgreSQL...\nok: [localhost]",
  "message": "Transaction completed successfully"
}

Git-Integration

Polycrate kann automatisch Git-Commits mit Transaction-IDs erstellen:

Auto-Commit aktivieren

# workspace.poly
name: my-workspace

events:
  commit: true  # Git-Commit nach erfolgreicher Transaction

sync_options:
  enabled: true
  auto: true  # Automatischer Push nach Commit
  remote:
    name: origin
    url: git@github.com:user/workspace.git
    branch:
      name: main

Commit-Message-Format

Action: install block my-postgres

Transaction: 550e8400-e29b-41d4-a716-446655440000
Exit Code: 0
Duration: 2m 15s

Changes:
- Updated artifacts/blocks/my-postgres/
- Updated .logs/2025-01-30/

🤖 Generated with Polycrate

Co-Authored-By: Polycrate <noreply@polycrate.io>

Transaction-Context in Actions

Die Transaction-ID ist in Actions verfĂĽgbar:

Als Environment-Variable

echo "Transaction ID: $POLYCRATE_TRANSACTION_ID"
echo "Transaction Start: $POLYCRATE_TRANSACTION_START_TIME"

In Ansible-Playbooks

---
- name: Deploy Application
  hosts: localhost
  connection: local

  tasks:
    - name: Debug Transaction-Info
      debug:
        msg: |
          Transaction ID: {{ transaction.id }}
          Started at: {{ transaction.timestamp }}
          User: {{ user.name }} <{{ user.email }}>

In Bash-Scripts (Template-Syntax)

actions:
  - name: deploy
    script:
      - |
        echo "=== Deployment via Transaction {{ .transaction.id }} ==="
        echo "User: {{ .user.name }}"
        echo "Timestamp: {{ .transaction.timestamp }}"

        # ... deployment logic ...

        echo "Transaction completed"

Monitoring mit Transactions

Transaction-Metriken

Transactions können für Monitoring und Metriken genutzt werden:

#!/bin/bash
# transaction-stats.sh

LOGS_DIR=".logs"

echo "=== Transaction Statistics ==="

# Gesamt-Anzahl
TOTAL=$(find $LOGS_DIR -name "*.yml" | wc -l)
echo "Total Transactions: $TOTAL"

# Erfolgreiche vs. Fehlgeschlagene
SUCCESS=$(grep -r "exit_code: 0" $LOGS_DIR | wc -l)
FAILED=$(grep -r "exit_code: [^0]" $LOGS_DIR | wc -l)
echo "Successful: $SUCCESS"
echo "Failed: $FAILED"

# Durchschnittliche Dauer
AVG_DURATION=$(find $LOGS_DIR -name "*.yml" -exec yq ".transaction.duration" {} \; | \
  awk '{sum+=$1; count++} END {if(count>0) print sum/count/1000 "s"}')
echo "Average Duration: $AVG_DURATION"

# Top 5 längste Transactions
echo -e "\n=== Longest Transactions ==="
find $LOGS_DIR -name "*.yml" -exec sh -c '
  duration=$(yq ".transaction.duration" "$1")
  action=$(yq ".action.name" "$1")
  block=$(yq ".block.name" "$1")
  echo "$duration $block/$action $1"
' _ {} \; | sort -rn | head -5 | \
  awk '{printf "%d seconds - %s - %s\n", $1/1000, $2, $3}'

Prometheus-Metriken (Optional)

Exportieren Sie Transaction-Metriken fĂĽr Prometheus:

#!/usr/bin/env python3
# export-metrics.py

import yaml
import glob
from prometheus_client import CollectorRegistry, Gauge, write_to_textfile

registry = CollectorRegistry()

transactions_total = Gauge('polycrate_transactions_total',
    'Total number of transactions', registry=registry)
transactions_failed = Gauge('polycrate_transactions_failed_total',
    'Number of failed transactions', registry=registry)
transaction_duration = Gauge('polycrate_transaction_duration_seconds',
    'Last transaction duration', ['block', 'action'], registry=registry)

# Parse transaction logs
for log_file in glob.glob('.logs/*/*.yml'):
    with open(log_file, 'r') as f:
        data = yaml.safe_load(f)

        transactions_total.inc()

        if data['transaction']['exit_code'] != 0:
            transactions_failed.inc()

        duration_sec = data['transaction']['duration'] / 1000
        transaction_duration.labels(
            block=data['block']['name'],
            action=data['action']['name']
        ).set(duration_sec)

write_to_textfile('polycrate_metrics.prom', registry)

Best Practices

1. Regelmäßige Log-Rotation

Logs können sich ansammeln. Implementieren Sie Log-Rotation:

# cleanup-old-logs.sh
#!/bin/bash

LOGS_DIR=".logs"
RETENTION_DAYS=90

echo "Cleaning up logs older than $RETENTION_DAYS days..."

find $LOGS_DIR -name "*.yml" -mtime +$RETENTION_DAYS -delete

# Leere Verzeichnisse löschen
find $LOGS_DIR -type d -empty -delete

echo "Cleanup completed"

Als Cron-Job:

0 2 * * 0 /path/to/workspace/cleanup-old-logs.sh

2. Logs fĂĽr Compliance

Transaction-Logs sind perfekt fĂĽr Compliance und Auditing:

# compliance-report.sh
#!/bin/bash

START_DATE="2025-01-01"
END_DATE="2025-01-31"

echo "=== Compliance Report: $START_DATE to $END_DATE ==="

find .logs/ -name "*.yml" -newermt "$START_DATE" ! -newermt "$END_DATE" | \
  while read log; do
    echo "---"
    yq '{
      "timestamp": .transaction.start_time,
      "user": .user.email,
      "action": (.block.name + "/" + .action.name),
      "status": .transaction.status,
      "changes": .workspace.git.commit_sha
    }' "$log"
  done

3. Transaction-IDs in Alerts

Nutzen Sie Transaction-IDs in Alerting-Systemen:

# Webhook-Handler fĂĽr Slack/Mattermost
actions:
  - name: notify-on-failure
    script:
      - |
        if [ $? -ne 0 ]; then
          curl -X POST https://hooks.slack.com/services/XXX \
            -H 'Content-Type: application/json' \
            -d "{
              \"text\": \"❌ Deployment Failed\",
              \"blocks\": [{
                \"type\": \"section\",
                \"text\": {
                  \"type\": \"mrkdwn\",
                  \"text\": \"*Transaction:* \`{{ .transaction.id }}\`\n*Block:* {{ .block.name }}\n*Action:* {{ .action.name }}\n*User:* {{ .user.name }}\"
                }
              }]
            }"
        fi

4. Transaction-Replay

Reproduzieren Sie eine Transaction mit dem gespeicherten Snapshot:

# replay-transaction.sh
#!/bin/bash

TX_ID="$1"
LOG_FILE=$(find .logs/ -name "${TX_ID}.yml")

if [ -z "$LOG_FILE" ]; then
  echo "Transaction $TX_ID not found"
  exit 1
fi

# Extrahiere Befehl
COMMAND=$(yq ".transaction.command" "$LOG_FILE")

echo "Replaying transaction $TX_ID"
echo "Original command: $COMMAND"
echo ""

# FĂĽhre aus (mit Warnung)
read -p "Execute? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
  eval "$COMMAND"
fi

Troubleshooting

Transaction-Logs sind leer

PrĂĽfen Sie Berechtigungen:

ls -la .logs/
# Sollte schreibbar sein fĂĽr aktuellen User

Container wurde nicht aufgeräumt

# Hängende Container finden
docker ps -a | grep polycrate

# Aufräumen
docker ps -a | grep polycrate | awk '{print $1}' | xargs docker rm -f

Zu viele Logs

Implementieren Sie Log-Rotation (siehe Best Practices oben).

Zusammenhang mit anderen Konzepten

  • Snapshot: Jede Transaction erstellt einen Snapshot
  • Events: Transactions generieren Events
  • Artefakte: Ă„nderungen an Artifacts werden in Transaction-Logs dokumentiert
  • Container: Container wird mit Transaction-ID benannt
  • Workflows: Jeder Workflow-Step ist eine eigene Transaction

Transaction-basiertes Debugging

Bei Problemen: Finden Sie die Transaction-ID in der Ausgabe und öffnen Sie den Log. Er enthält ALLES: Snapshot, Ausgabe, Fehler, Umgebung.