Skip to content

Commit

Permalink
snapshot create and restore + readme
Browse files Browse the repository at this point in the history
  • Loading branch information
BrendanCoughlan5 committed Dec 11, 2024
1 parent 8f4b3e9 commit afef155
Show file tree
Hide file tree
Showing 3 changed files with 293 additions and 4 deletions.
33 changes: 29 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,15 +234,40 @@ POSTGRES_DATA_PATH=<path to store postgres data> docker-compose up
# Boot from a snapshot
Get our latest snapshots
* Mainnet (not yet available)
* Testnet ([2024-11-22](https://eigenlayer-sidecar.s3.us-east-1.amazonaws.com/snapshots/testnet-holesky/sidecar-testnet-holesky-20241122.tar.gz))
* Testnet
* Latest archive snapshot (~1.5GB) https://eigenlayer-sidecar.s3.us-east-1.amazonaws.com/snapshots/holesky/archive/sidecar-snapshot-holesky-archive-latest.tar.gz
Example script
```bash
curl -LO https://eigenlayer-sidecar.s3.amazonaws.com/snapshots/testnet-holesky/sidecar-testnet-holesky-20241122.tar.gz
curl -LO https://eigenlayer-sidecar.s3.us-east-1.amazonaws.com/snapshots/holesky/archive/sidecar-snapshot-holesky-archive-latest.tar.gz
tar -xvf sidecar-snapshot-holesky-archive-latest.tar.gz
go run main.go snapshot-restore \
--snapshot-restore-from-path=sidecar-snapshot-holesky-archive-latest.dump \
--clean=true \
--database.host=localhost \
--database.user=sidecar \
--database.password=sidecar \
--database.port=5432 \
--database.db_name=sidecar
```
tar -xvf sidecar-testnet-2024-11-22.tar.gz
Browse our public snapshots here
```bash
aws s3 ls s3://eigenlayer-sidecar/snapshots/ --no-sign-request
```
See previous versions of an s3 object
```bash
aws s3api list-object-versions \
--bucket eigenlayer-sidecar \
--prefix snapshots/holesky/archive/sidecar-snapshot-holesky-archive-latest.tar.gz \
--no-sign-request
pg_restore --host <hostname> --port 5432 --username <username> --dbname <dbname> --no-owner sidecar-testnet-2024-11-22.dump
```
## RPC Routes
Expand Down
161 changes: 161 additions & 0 deletions cmd/snapshot-create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package cmd

import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"time"

"github.com/Layr-Labs/sidecar/internal/logger"
"github.com/spf13/cobra"
)

var (
snapshotCreateToPath string
snapshotFidelity string
s3Path string
compressSnapshot bool
)

var snapshotCreateCmd = &cobra.Command{
Use: "snapshot-create",
Short: "Create a snapshot of the database",
Long: `Create a snapshot of the database.
Currently available fidelity levels:
- archive (default): includes chain data, EigenModel state, rewards, and staker-operator table data.
`,
RunE: func(cmd *cobra.Command, args []string) error {
// Initialize logger (in a real scenario, you'd likely pass in a config)
l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: true})

// Retrieve database connection details from global flags
dbHost, _ := cmd.Flags().GetString("database.host")
dbName, _ := cmd.Flags().GetString("database.db_name")
dbUser, _ := cmd.Flags().GetString("database.user")
dbPassword, _ := cmd.Flags().GetString("database.password")
dbPort, _ := cmd.Flags().GetInt("database.port")
schemaName, _ := cmd.Flags().GetString("database.schema_name")

// Retrieve chain and fidelity
chain, _ := cmd.Flags().GetString("chain")

// Validate fidelity
if snapshotFidelity != "archive" {
l.Sugar().Warnw("Unsupported fidelity specified; falling back to 'archive'.",
"requested", snapshotFidelity,
"used", "archive",
)
snapshotFidelity = "archive"
}

// Log database connection details at info level
l.Sugar().Infow("Database connection details",
"host", dbHost,
"name", dbName,
"user", dbUser,
"port", dbPort,
"schema", schemaName,
)

// If no specific file name is provided, generate one
if snapshotCreateToPath == "" {
fileNameSuffix := time.Now().UTC().Format("2006-01-02-15-04-05")
snapshotCreateToPath = fmt.Sprintf("sidecar-snapshot-%s-%s-%s.dump", chain, snapshotFidelity, fileNameSuffix)
}

// Log the snapshot file path
l.Sugar().Infow("Snapshot will be created at this file", "file", snapshotCreateToPath)

// Set the password in an environment variable for pg_dump
if err := os.Setenv("PGPASSWORD", dbPassword); err != nil {
l.Sugar().Fatalw("Failed to set environment variable", "error", err)
return err
}

// Construct the pg_dump command with a context for timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()

pgDumpCmd := exec.CommandContext(ctx,
"pg_dump",
"--host", dbHost,
"--port", fmt.Sprintf("%d", dbPort),
"--username", dbUser,
"--format", "custom",
"--file", snapshotCreateToPath,
"--schema", schemaName,
dbName,
)

// Direct command output to stdout/stderr for visibility
pgDumpCmd.Stdout = os.Stdout
pgDumpCmd.Stderr = os.Stderr

l.Sugar().Infow("Running pg_dump command",
"command", pgDumpCmd.String(),
)

// Run the pg_dump command
if err := pgDumpCmd.Run(); err != nil {
if ctx.Err() == context.DeadlineExceeded {
l.Sugar().Errorw("pg_dump command timed out",
"error", err,
"command", pgDumpCmd.String(),
)
return fmt.Errorf("pg_dump command timed out: %w", err)
}
l.Sugar().Errorw("Failed to run pg_dump",
"error", err,
"command", pgDumpCmd.String(),
)
return fmt.Errorf("failed to run pg_dump: %w", err)
}

l.Sugar().Infow("Successfully created snapshot", "file", snapshotCreateToPath)

// Compress the snapshot if the flag is set
if compressSnapshot {
compressedSnapshot := snapshotCreateToPath + ".tar.gz"
l.Sugar().Infow("Compressing snapshot", "file", compressedSnapshot)
tarCmd := exec.Command("tar", "-czf", compressedSnapshot, "-C", filepath.Dir(snapshotCreateToPath), filepath.Base(snapshotCreateToPath))
tarCmd.Stdout = os.Stdout
tarCmd.Stderr = os.Stderr

if err := tarCmd.Run(); err != nil {
l.Sugar().Errorw("Snapshot compression failed", "error", err)
return fmt.Errorf("snapshot compression failed: %w", err)
}

l.Sugar().Infow("Successfully compressed snapshot", "file", compressedSnapshot)
snapshotCreateToPath = compressedSnapshot
}

// Upload to S3 if s3Path is specified
if s3Path != "" {
l.Sugar().Infow("Uploading to S3", "path", s3Path)
s3Cmd := exec.Command("aws", "s3", "cp", snapshotCreateToPath, s3Path)
s3Cmd.Stdout = os.Stdout
s3Cmd.Stderr = os.Stderr

if err := s3Cmd.Run(); err != nil {
l.Sugar().Errorw("Upload to S3 failed", "error", err)
return fmt.Errorf("upload to S3 failed: %w", err)
}

l.Sugar().Infow("Successfully uploaded snapshot to S3", "file", snapshotCreateToPath)
}

return nil
},
}

func init() {
rootCmd.AddCommand(snapshotCreateCmd)
snapshotCreateCmd.Flags().StringVarP(&snapshotCreateToPath, "snapshot-create-to-path", "f", "", "Path to save the snapshot file to (default is generated based on timestamp and chain)")
snapshotCreateCmd.Flags().StringVar(&snapshotFidelity, "fidelity", "archive", "The fidelity level of the snapshot: 'archive' only currently supported.")
snapshotCreateCmd.Flags().StringVar(&s3Path, "s3-path", "", "S3 path to upload the snapshot to (e.g., s3://bucket/folder/), Default doesn't upload. It will only upload the compressed snapshot if --compress is set")
snapshotCreateCmd.Flags().BoolVar(&compressSnapshot, "compress", false, "Compress the snapshot and save as well as the original snapshot with .tar.gz extension")
}
103 changes: 103 additions & 0 deletions cmd/snapshot-restore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package cmd

import (
"context"
"fmt"
"os"
"os/exec"
"time"

"github.com/Layr-Labs/sidecar/internal/logger"
"github.com/spf13/cobra"
)

var snapshotRestoreFromPath string
var cleanRestore bool

var snapshotRestoreCmd = &cobra.Command{
Use: "snapshot-restore",
Short: "Restore from a snapshot",
RunE: func(cmd *cobra.Command, args []string) error {
// Initialize logger (in a real scenario, you'd likely pass in a config)
l, _ := logger.NewLogger(&logger.LoggerConfig{Debug: true}) // or false depending on your need

// Retrieve database connection details from global flags
dbHost, _ := cmd.Flags().GetString("database.host")
dbName, _ := cmd.Flags().GetString("database.db_name")
dbUser, _ := cmd.Flags().GetString("database.user")
dbPassword, _ := cmd.Flags().GetString("database.password")
dbPort, _ := cmd.Flags().GetInt("database.port")

// Log database connection details at info level
l.Sugar().Infow("Database connection details",
"host", dbHost,
"name", dbName,
"user", dbUser,
"port", dbPort,
)

// Log the snapshot path
l.Sugar().Infow("Snapshot path", "path", snapshotRestoreFromPath)

// Set the password in an environment variable for pg_restore
if err := os.Setenv("PGPASSWORD", dbPassword); err != nil {
l.Sugar().Fatalw("Failed to set environment variable", "error", err)
return err
}

// Construct the pg_restore command with a context for timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()

pgRestoreCmd := exec.CommandContext(ctx,
"pg_restore",
"--host", dbHost,
"--port", fmt.Sprintf("%d", dbPort),
"--username", dbUser,
"--dbname", dbName,
"--no-owner",
)

// Add the clean option if specified
if cleanRestore {
pgRestoreCmd.Args = append(pgRestoreCmd.Args, "--clean")
}

// Add the snapshot path
pgRestoreCmd.Args = append(pgRestoreCmd.Args, snapshotRestoreFromPath)

// Direct command output to stdout/stderr
pgRestoreCmd.Stdout = os.Stdout
pgRestoreCmd.Stderr = os.Stderr

l.Sugar().Infow("Running pg_restore command",
"command", pgRestoreCmd.String(),
)

// Run the pg_restore command
if err := pgRestoreCmd.Run(); err != nil {
if ctx.Err() == context.DeadlineExceeded {
l.Sugar().Errorw("pg_restore command timed out",
"error", err,
"command", pgRestoreCmd.String(),
)
return fmt.Errorf("pg_restore command timed out: %w", err)
}
l.Sugar().Errorw("Failed to run pg_restore",
"error", err,
"command", pgRestoreCmd.String(),
)
return fmt.Errorf("failed to run pg_restore: %w", err)
}

l.Sugar().Infow("Successfully restored from snapshot")
return nil
},
}

func init() {
rootCmd.AddCommand(snapshotRestoreCmd)
snapshotRestoreCmd.Flags().StringVarP(&snapshotRestoreFromPath, "snapshot-restore-from-path", "p", "", "Path to snapshot file (required)")
snapshotRestoreCmd.Flags().BoolVar(&cleanRestore, "clean", false, "Clean restore (default false)")
snapshotRestoreCmd.MarkFlagRequired("snapshot-restore-from-path")
}

0 comments on commit afef155

Please sign in to comment.