Skip to content

Commit

Permalink
Release version 1.0.0 of the plugin (#1)
Browse files Browse the repository at this point in the history
* [prepare-release] Implemented easer & cacher functionality, and covered with tests

* [prepare-release] Implemented and set in use the SetPointedValue instead of full reflection
  • Loading branch information
ktsivkov authored Apr 18, 2023
1 parent 42b716e commit a3c35d2
Show file tree
Hide file tree
Showing 18 changed files with 1,440 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea
233 changes: 231 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,231 @@
# caches
Caches Plugin
# Gorm Caches

Gorm Caches plugin using database request reductions (easer), and response caching mechanism provide you an easy way to optimize database performance.

## Features

- Database request reduction. If three identical requests are running at the same time, only the first one is going to be executed, and its response will be returned for all.
- Database response caching. By implementing the Cacher interface, you can easily setup a caching mechanism for your database queries.
- Supports all databases that are supported by gorm itself.

## Install

```bash
go get -u github.com/go-gorm/caches
```

## Usage

Configure the `easer`, and the `cacher`, and then load the plugin to gorm.

```go
package main

import (
"fmt"
"sync"

"github.com/go-gorm/caches"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

func main() {
db, _ := gorm.Open(
mysql.Open("DATABASE_DSN"),
&gorm.Config{},
)
cachesPlugin := &caches.Caches{Conf: &caches.Config{
Easer: true,
Cacher: &yourCacherImplementation{},
}}
_ = db.Use(cachesPlugin)
}
```

## Easer Example

```go
package main

import (
"fmt"
"sync"
"time"

"github.com/go-gorm/caches"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

type UserRoleModel struct {
gorm.Model
Name string `gorm:"unique"`
}

type UserModel struct {
gorm.Model
Name string
RoleId uint
Role *UserRoleModel `gorm:"foreignKey:role_id;references:id"`
}

func main() {
db, _ := gorm.Open(
mysql.Open("DATABASE_DSN"),
&gorm.Config{},
)

cachesPlugin := &caches.Caches{Conf: &caches.Config{
Easer: true,
}}

_ = db.Use(cachesPlugin)

_ = db.AutoMigrate(&UserRoleModel{})

_ = db.AutoMigrate(&UserModel{})

adminRole := &UserRoleModel{
Name: "Admin",
}
db.FirstOrCreate(adminRole, "Name = ?", "Admin")

guestRole := &UserRoleModel{
Name: "Guest",
}
db.FirstOrCreate(guestRole, "Name = ?", "Guest")

db.Save(&UserModel{
Name: "ktsivkov",
Role: adminRole,
})
db.Save(&UserModel{
Name: "anonymous",
Role: guestRole,
})

var (
q1Users []UserModel
q2Users []UserModel
)
wg := &sync.WaitGroup{}
wg.Add(2)
go func() {
db.Model(&UserModel{}).Joins("Role").Find(&q1Users, "Role.Name = ? AND Sleep(1) = false", "Admin")
wg.Done()
}()
go func() {
time.Sleep(500 * time.Millisecond)
db.Model(&UserModel{}).Joins("Role").Find(&q2Users, "Role.Name = ? AND Sleep(1) = false", "Admin")
wg.Done()
}()
wg.Wait()

fmt.Println(fmt.Sprintf("%+v", q1Users))
fmt.Println(fmt.Sprintf("%+v", q2Users))
}
```

## Cacher Example

```go
package main

import (
"fmt"
"sync"

"github.com/go-gorm/caches"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

type UserRoleModel struct {
gorm.Model
Name string `gorm:"unique"`
}

type UserModel struct {
gorm.Model
Name string
RoleId uint
Role *UserRoleModel `gorm:"foreignKey:role_id;references:id"`
}

type dummyCacher struct {
store *sync.Map
}

func (c *dummyCacher) init() {
if c.store == nil {
c.store = &sync.Map{}
}
}

func (c *dummyCacher) Get(key string) interface{} {
c.init()
val, _ := c.store.Load(key)
return val
}

func (c *dummyCacher) Store(key string, val interface{}) error {
c.init()
c.store.Store(key, val)
return nil
}

func main() {
db, _ := gorm.Open(
mysql.Open("DATABASE_DSN"),
&gorm.Config{},
)

cachesPlugin := &caches.Caches{Conf: &caches.Config{
Cacher: &dummyCacher{},
}}

_ = db.Use(cachesPlugin)

_ = db.AutoMigrate(&UserRoleModel{})

_ = db.AutoMigrate(&UserModel{})

adminRole := &UserRoleModel{
Name: "Admin",
}
db.FirstOrCreate(adminRole, "Name = ?", "Admin")

guestRole := &UserRoleModel{
Name: "Guest",
}
db.FirstOrCreate(guestRole, "Name = ?", "Guest")

db.Save(&UserModel{
Name: "ktsivkov",
Role: adminRole,
})
db.Save(&UserModel{
Name: "anonymous",
Role: guestRole,
})

var (
q1Users []UserModel
q2Users []UserModel
)

db.Model(&UserModel{}).Joins("Role").Find(&q1Users, "Role.Name = ? AND Sleep(1) = false", "Admin")
fmt.Println(fmt.Sprintf("%+v", q1Users))

db.Model(&UserModel{}).Joins("Role").Find(&q2Users, "Role.Name = ? AND Sleep(1) = false", "Admin")
fmt.Println(fmt.Sprintf("%+v", q2Users))
}
```

## License

MIT license.

## Easer
The easer is an adjusted version of the [ServantGo](https://github.com/ktsivkov/servantgo) library to fit the needs of this plugin.
6 changes: 6 additions & 0 deletions cacher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package caches

type Cacher interface {
Get(key string) interface{}
Store(key string, val interface{}) error
}
48 changes: 48 additions & 0 deletions cacher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package caches

import (
"errors"
"sync"
)

type cacherMock struct {
store *sync.Map
}

func (c *cacherMock) init() {
if c.store == nil {
c.store = &sync.Map{}
}
}

func (c *cacherMock) Get(key string) interface{} {
c.init()
val, _ := c.store.Load(key)
return val
}

func (c *cacherMock) Store(key string, val interface{}) error {
c.init()
c.store.Store(key, val)
return nil
}

type cacherStoreErrorMock struct {
store *sync.Map
}

func (c *cacherStoreErrorMock) init() {
if c.store == nil {
c.store = &sync.Map{}
}
}

func (c *cacherStoreErrorMock) Get(key string) interface{} {
c.init()
val, _ := c.store.Load(key)
return val
}

func (c *cacherStoreErrorMock) Store(key string, val interface{}) error {
return errors.New("store-error")
}
Loading

0 comments on commit a3c35d2

Please sign in to comment.