-
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Release version 1.0.0 of the plugin (#1)
* [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
Showing
18 changed files
with
1,440 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
} |
Oops, something went wrong.