Skip to content

Commit d66e65b

Browse files
authored
feat: Compress data backup using zstd (salesforce#264)
The backup turns out to be highly compressible! I chose zstd. Although gzip is standard, zstd is faster, and compresses better. Example: 4.6M sloop-cluster-0.bak.zst 9.5M sloop-cluster-0.bak.gz 155M sloop-cluster-0.bak
1 parent 6438d5d commit d66e65b

File tree

4 files changed

+39
-3
lines changed

4 files changed

+39
-3
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/golang/glog v1.0.0
1111
github.com/golang/protobuf v1.5.2
1212
github.com/jteeuwen/go-bindata v3.0.7+incompatible
13+
github.com/klauspost/compress v1.16.5
1314
github.com/nsf/jsondiff v0.0.0-20190712045011-8443391ee9b6
1415
github.com/pkg/errors v0.9.1
1516
github.com/prometheus/client_golang v1.14.0

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
205205
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
206206
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
207207
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
208+
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
209+
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
208210
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
209211
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
210212
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=

pkg/sloop/webserver/webserver.go

+18-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"syscall"
2424
"time"
2525

26+
"github.com/klauspost/compress/zstd"
2627
"github.com/salesforce/sloop/pkg/sloop/common"
2728
"github.com/spf13/afero"
2829

@@ -116,11 +117,25 @@ func backupHandler(db badgerwrap.DB, currentContext string) http.HandlerFunc {
116117
return
117118
}
118119

119-
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=sloop-%s-%d.bak", currentContext, since))
120-
w.Header().Set("Content-Type", "application/octet-stream")
120+
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=sloop-%s-%d.bak.zst", currentContext, since))
121+
// The 'Content-Length' header is not set, because we do not know the size of the backup before we write it to the body.
122+
w.Header().Set("Content-Encoding", "zstd")
123+
w.Header().Set("Content-Type", "application/zstd")
121124
w.Header().Set("Transfer-Encoding", "chunked")
122125

123-
_, err = db.Backup(w, since)
126+
zw, err := zstd.NewWriter(w)
127+
if err != nil {
128+
logWebError(err, "Error configuring compression", r, w)
129+
return
130+
}
131+
132+
_, err = db.Backup(zw, since)
133+
if err != nil {
134+
logWebError(err, "Error writing backup", r, w)
135+
return
136+
}
137+
138+
err = zw.Close()
124139
if err != nil {
125140
logWebError(err, "Error writing backup", r, w)
126141
return

pkg/sloop/webserver/webserver_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"net/http/httptest"
66
"testing"
77

8+
badger "github.com/dgraph-io/badger/v2"
9+
"github.com/salesforce/sloop/pkg/sloop/store/untyped/badgerwrap"
810
"github.com/stretchr/testify/assert"
911
)
1012

@@ -55,3 +57,19 @@ func TestWebFileHandler(t *testing.T) {
5557
assert.Equal(t, http.StatusOK, rr.Code)
5658
assert.NotNil(t, rr.Body.String())
5759
}
60+
61+
func TestBackupHandler(t *testing.T) {
62+
req, err := http.NewRequest("GET", "/clusterContext/data/backup", nil)
63+
assert.Nil(t, err)
64+
65+
db, err := (&badgerwrap.MockFactory{}).Open(badger.DefaultOptions(""))
66+
assert.Nil(t, err)
67+
68+
// Create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
69+
rr := httptest.NewRecorder()
70+
handler := http.HandlerFunc(backupHandler(db, "clusterContext"))
71+
handler.ServeHTTP(rr, req)
72+
73+
assert.Equal(t, http.StatusOK, rr.Code)
74+
assert.NotNil(t, rr.Body.String())
75+
}

0 commit comments

Comments
 (0)