1
0
Fork 0
mirror of https://github.com/imjasonh/terraform-playground synced 2026-07-08 07:44:57 +00:00

only update count not version, handle startup better

Signed-off-by: Jason Hall <jason@chainguard.dev>
This commit is contained in:
Jason Hall 2024-06-26 14:24:03 -04:00
parent 60c192aab8
commit 05304f8146
Failed to extract signature
4 changed files with 42 additions and 28 deletions

View file

@ -50,3 +50,7 @@ These costs are shown in monthly units, but are billed per 100 milliseconds of a
Since it's just SQLite, this also supports standard SQL operations and semantics. It can be trivially tested locally without access to a cloud instance, or moved to other Litestream-compatible backends like S3.
There are also potentially tenancy benefits to using a separate database for each user, which can be easily achieved with multiple SQLite instances.
### Running locally
Since the database is just SQLite, it's very easy to run this locally. Simply `go run ./` and the service will create `db.sqlite` in the current directory. You can browse to http://localhost:8080/ to see the service running, and interact with the database using standard tools.

View file

@ -3,6 +3,7 @@ module github.com/imjasonh/terraform-playground/litestream
go 1.21.5
require (
cloud.google.com/go/compute/metadata v0.3.0
github.com/chainguard-dev/clog v1.3.1
github.com/glebarez/go-sqlite v1.22.0
)

View file

@ -1,3 +1,5 @@
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/chainguard-dev/clog v1.3.1 h1:CDNCty5WKQhJzoOPubk0GdXt+bPQyargmfClqebrpaQ=
github.com/chainguard-dev/clog v1.3.1/go.mod h1:cV516KZWqYc/phZsCNwF36u/KMGS+Gj5Uqeb8Hlp95Y=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=

View file

@ -2,7 +2,6 @@ package main
import (
"database/sql"
"errors"
"flag"
"fmt"
"html/template"
@ -12,44 +11,51 @@ import (
"os/exec"
"time"
"cloud.google.com/go/compute/metadata"
"github.com/chainguard-dev/clog"
_ "github.com/chainguard-dev/clog/gcp/init"
_ "github.com/glebarez/go-sqlite"
)
var dbfile = flag.String("file", "/data/db.sqlite", "path to database file")
var dbfile = flag.String("file", "db.sqlite", "path to database file")
func main() {
flag.Parse()
if _, err := os.Stat(*dbfile); errors.Is(err, os.ErrNotExist) {
if os.Getenv("BUCKET") != "" {
// Before serving requests, restore the database from the latest replica.
start := time.Now()
cmd := exec.Command("litestream", "restore",
"-o", *dbfile,
fmt.Sprintf("gcs://%s/litestream", os.Getenv("BUCKET")))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
clog.Fatalf("failed to restore database: %v", err)
}
clog.Infof("restoring database took %s", time.Since(start))
} else {
clog.Infof("creating database file: %s", *dbfile)
if _, err := os.Create(*dbfile); err != nil {
dbfile := *dbfile
if metadata.OnGCE() {
if os.Getenv("BUCKET") == "" {
clog.Fatal("BUCKET environment variable is required on GCE")
}
dbfile = "/data/" + dbfile
// Before serving requests, restore the database from the latest replica.
start := time.Now()
cmd := exec.Command("litestream", "restore",
"-o", dbfile,
fmt.Sprintf("gcs://%s/litestream", os.Getenv("BUCKET")))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
clog.Fatalf("failed to restore database: %v", err)
}
clog.Infof("restoring database took %s", time.Since(start))
} else {
if _, err := os.Stat(dbfile); os.IsNotExist(err) {
clog.Infof("creating database file: %s", dbfile)
if _, err := os.Create(dbfile); err != nil {
clog.Fatalf("failed to create database file: %v", err)
}
}
}
fi, err := os.Stat(*dbfile)
fi, err := os.Stat(dbfile)
if err != nil {
clog.Fatalf("failed to stat database: %v", err)
}
clog.Infof("database size: %d bytes", fi.Size())
db, err := sql.Open("sqlite", *dbfile)
db, err := sql.Open("sqlite", dbfile)
if err != nil {
clog.Fatalf("failed to open database: %v", err)
}
@ -65,7 +71,7 @@ func main() {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
data, err := getData(db)
data, err := getData(db, true)
if err != nil {
clog.Fatalf("failed to get data: %v", err)
}
@ -78,7 +84,7 @@ func main() {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
data, err := getData(db)
data, err := getData(db, false)
if err != nil {
clog.Fatalf("failed to get data: %v", err)
}
@ -89,11 +95,13 @@ func main() {
log.Fatal(http.ListenAndServe(":8080", nil))
}
func getData(db *sql.DB) (data, error) {
row := db.QueryRow("select sqlite_version()")
func getData(db *sql.DB, getVersion bool) (data, error) {
var version string
if err := row.Scan(&version); err != nil {
return data{}, fmt.Errorf("failed to query database version: %w", err)
if getVersion {
row := db.QueryRow("select sqlite_version()")
if err := row.Scan(&version); err != nil {
return data{}, fmt.Errorf("failed to query database version: %w", err)
}
}
if _, err := db.Exec("insert into test3 (time) values (unixepoch('now','subsec'))"); err != nil {
@ -128,8 +136,8 @@ var (
</head>
<body>
<h1>litestream</h1>
<p>sqlite version: {{.Version}}</p>
<div id="data" hx-swap="outerHTML">
<p>sqlite version: {{.Version}}</p>
<p>count: {{.Count}}</p>
<button hx-post="/click" hx-trigger="click" hx-target="#data">Refresh</button>
</div>
@ -139,7 +147,6 @@ var (
div = template.Must(template.New("").Parse(`
<div id="data" hx-swap="outerHTML">
<p>sqlite version: {{.Version}}</p>
<p>count: {{.Count}}</p>
<button hx-post="/click" hx-trigger="click" hx-target="#data">Refresh</button>
</div>