Skip to content

Commit

Permalink
Initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
ndthuan committed Jun 10, 2024
1 parent d84ff82 commit 598659b
Show file tree
Hide file tree
Showing 13 changed files with 967 additions and 0 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Go

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:

build:
name: Build
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [1.18, 1.19, 1.20, 1.21, 1.22]
steps:

- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
id: go

- uses: actions/checkout@v4
with:
fetch-depth: 10

- name: Get dependencies
run: go get -v -t -d ./...

- uses: gwatts/go-coverage-action@v2
id: coverage
with:
# Optional coverage threshold
# use fail-coverage to determine what should happen below this threshold
coverage-threshold: 100

# collect coverage for all packages beyond the one under test
cover-pkg: ./...
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/kvsync.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

139 changes: 139 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# KVSync

KVSync is a Go package that provides a simple and efficient way to synchronize your GORM models with a key-value store.

There can be multiple key definitions for each model. For example, you can have a key for fetching by ID, a key for fetching by UUID, and a composite key for fetching by both ID and UUID. For each key, the model data is replicated accordingly to the key-value store.

## Installation

To install KVSync, use the `go get` command:

```bash
go get github.com/ndthuan/kvsync
```

## Sync Setup

### Define Synced Models

Implement `kvsync.Syncable` and provide sync keys mapped by a name for fetching later. Each key is unique on the key-value store.

```go
type SyncedUser struct {
gorm.Model
UUID string
Username string
}

func (u SyncedUser) SyncKeys() map[string]string {
return map[string]string{
"id": fmt.Sprintf("user:id:%d", u.ID),
"uuid": fmt.Sprintf("user:uuid:%s", u.UUID),
"composite": fmt.Sprintf("user:composite:%d_%s", u.ID, u.UUID),
}
}

```

### Configure Key-Value Store

With Redis for example, you can use the provided `RedisStore`. Steps:
- Init GORM DB instance
- Init Redis client
- Create a new `RedisStore` instance
- Create a new `KVSync` instance
- Register GORM callbacks

```go
package main

import (
"context"
"fmt"
"github.com/ndthuan/kvsync"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
"time"
)

func main() {
db, err := gorm.Open(...)
if err != nil {
panic(fmt.Sprintf("Failed to connect to database: %v", err))
}

clusterClient := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{},
})

// Create a new RedisStore
store := &kvsync.RedisStore{
Client: clusterClient,
Expiration: time.Hour * 24 * 365, // Set the expiration time for the keys
Prefix: "kvsync:", // Optional, defaults to "kvsync:"
Marshaler: &kvsync.BSONMarshalingAdapter{}, // Optional, BSONMarshalingAdapter (using Mongo's BSON) is the default and recommended
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

kvSync := kvsync.NewKVSync(ctx, kvsync.Options{
Store: store,
Workers: 4,
ReportCallback: func(r kvsync.Report) {
if r.Err == nil {
actualDoneCount++
}
},
})

// Register the GORM callbacks for automated synchronization
db.Callback().Create().After("gorm:create").Register("kvsync:create", kvSync.GormCallback())
db.Callback().Update().After("gorm:update").Register("kvsync:update", kvSync.GormCallback())

}
```

### And create/update your model as usual

```go
// Create a new SyncedUser
db.Create(&SyncedUser{
UUID: "test-uuid",
Username: "test-username",
})
// The SyncedUser is automatically synchronized with the key-value store
```

## Fetching Synced Models

You can fetch the model by any of the keys you defined. You must provide a struct with non-zero values for the keys you want to fetch by.

By ID
```go
user := SyncedUser{
Model: gorm.Model{ID: 1},
}
kvSync.Fetch(&user, "id")
```

By UUID
```go
user := SyncedUser{
UUID: "test-uuid",
}
kvSync.Fetch(&user, "uuid")
```

By composite key
```go
user := SyncedUser{
Model: gorm.Model{ID: 1},
UUID: "test-uuid",
}
kvSync.Fetch(&user, "composite")
```

## License

KVSync is licensed under the MIT License. See the [LICENSE](LICENSE) file for more information.
25 changes: 25 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module github.com/ndthuan/kvsync

go 1.18

require (
github.com/alicebob/miniredis/v2 v2.33.0
github.com/redis/go-redis/v9 v9.5.3
github.com/stretchr/testify v1.9.0
go.mongodb.org/mongo-driver v1.15.0
gorm.io/driver/sqlite v1.5.5
gorm.io/gorm v1.25.10
)

require (
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/yuin/gopher-lua v1.1.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
37 changes: 37 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA=
github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU=
github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc=
go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
Loading

0 comments on commit 598659b

Please sign in to comment.