{"id":13590299,"url":"https://github.com/go-gorm/caches","last_synced_at":"2025-04-13T03:31:22.474Z","repository":{"id":153532853,"uuid":"626245806","full_name":"go-gorm/caches","owner":"go-gorm","description":"Caches Dialector","archived":false,"fork":false,"pushed_at":"2024-04-25T11:36:42.000Z","size":24,"stargazers_count":120,"open_issues_count":4,"forks_count":11,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-26T21:12:54.069Z","etag":null,"topics":["caching","performance","performance-optimization"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/go-gorm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["jinzhu"],"patreon":"jinzhu","open_collective":"gorm"}},"created_at":"2023-04-11T04:52:21.000Z","updated_at":"2025-03-24T17:14:42.000Z","dependencies_parsed_at":"2024-01-14T04:08:29.347Z","dependency_job_id":"129b1793-c12b-49e6-a360-a7083ea9d940","html_url":"https://github.com/go-gorm/caches","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-gorm%2Fcaches","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-gorm%2Fcaches/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-gorm%2Fcaches/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-gorm%2Fcaches/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/go-gorm","download_url":"https://codeload.github.com/go-gorm/caches/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248659642,"owners_count":21141152,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["caching","performance","performance-optimization"],"created_at":"2024-08-01T16:00:43.076Z","updated_at":"2025-04-13T03:31:22.117Z","avatar_url":"https://github.com/go-gorm.png","language":"Go","funding_links":["https://github.com/sponsors/jinzhu","https://patreon.com/jinzhu","https://opencollective.com/gorm"],"categories":["Go"],"sub_categories":[],"readme":"# Gorm Caches\n\nGorm Caches plugin using database request reductions (easer), and response caching mechanism provide you an easy way to optimize database performance.\n\n## Features\n\n- 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.\n- Database response caching. By implementing the Cacher interface, you can easily setup a caching mechanism for your database queries.\n- Supports all databases that are supported by gorm itself.\n\n## Install\n\n```bash\ngo get -u github.com/go-gorm/caches/v4\n```\n\n## Usage\n\nConfigure the `easer`, and the `cacher`, and then load the plugin to gorm.\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/go-gorm/caches/v4\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/gorm\"\n)\n\nfunc main() {\n\tdb, _ := gorm.Open(\n\t\tmysql.Open(\"DATABASE_DSN\"),\n\t\t\u0026gorm.Config{},\n\t)\n\tcachesPlugin := \u0026caches.Caches{Conf: \u0026caches.Config{\n\t\tEaser: true,\n\t\tCacher: \u0026yourCacherImplementation{},\n\t}}\n\t_ = db.Use(cachesPlugin)\n}\n```\n\n## Easer Example\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-gorm/caches/v4\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/gorm\"\n)\n\ntype UserRoleModel struct {\n\tgorm.Model\n\tName string `gorm:\"unique\"`\n}\n\ntype UserModel struct {\n\tgorm.Model\n\tName   string\n\tRoleId uint\n\tRole   *UserRoleModel `gorm:\"foreignKey:role_id;references:id\"`\n}\n\nfunc main() {\n\tdb, _ := gorm.Open(\n\t\tmysql.Open(\"DATABASE_DSN\"),\n\t\t\u0026gorm.Config{},\n\t)\n\n\tcachesPlugin := \u0026caches.Caches{Conf: \u0026caches.Config{\n\t\tEaser: true,\n\t}}\n\n\t_ = db.Use(cachesPlugin)\n\n\t_ = db.AutoMigrate(\u0026UserRoleModel{})\n\n\t_ = db.AutoMigrate(\u0026UserModel{})\n\n\tadminRole := \u0026UserRoleModel{\n\t\tName: \"Admin\",\n\t}\n\tdb.FirstOrCreate(adminRole, \"Name = ?\", \"Admin\")\n\n\tguestRole := \u0026UserRoleModel{\n\t\tName: \"Guest\",\n\t}\n\tdb.FirstOrCreate(guestRole, \"Name = ?\", \"Guest\")\n\n\tdb.Save(\u0026UserModel{\n\t\tName: \"ktsivkov\",\n\t\tRole: adminRole,\n\t})\n\tdb.Save(\u0026UserModel{\n\t\tName: \"anonymous\",\n\t\tRole: guestRole,\n\t})\n\n\tvar (\n\t\tq1Users []UserModel\n\t\tq2Users []UserModel\n\t)\n\twg := \u0026sync.WaitGroup{}\n\twg.Add(2)\n\tgo func() {\n\t\tdb.Model(\u0026UserModel{}).Joins(\"Role\").Find(\u0026q1Users, \"Role.Name = ? AND Sleep(1) = false\", \"Admin\")\n\t\twg.Done()\n\t}()\n\tgo func() {\n\t\ttime.Sleep(500 * time.Millisecond)\n\t\tdb.Model(\u0026UserModel{}).Joins(\"Role\").Find(\u0026q2Users, \"Role.Name = ? AND Sleep(1) = false\", \"Admin\")\n\t\twg.Done()\n\t}()\n\twg.Wait()\n\n\tfmt.Println(fmt.Sprintf(\"%+v\", q1Users))\n\tfmt.Println(fmt.Sprintf(\"%+v\", q2Users))\n}\n```\n\n## Cacher Example (Redis)\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/go-gorm/caches/v4\"\n\t\"github.com/redis/go-redis/v9\"\n\t\"gorm.io/driver/sqlite\"\n\t\"gorm.io/gorm\"\n)\n\ntype UserRoleModel struct {\n\tgorm.Model\n\tName string `gorm:\"unique\"`\n}\n\ntype UserModel struct {\n\tgorm.Model\n\tName   string\n\tRoleId uint\n\tRole   *UserRoleModel `gorm:\"foreignKey:role_id;references:id\"`\n}\n\ntype redisCacher struct {\n\trdb *redis.Client\n}\n\nfunc (c *redisCacher) Get(ctx context.Context, key string, q *caches.Query[any]) (*caches.Query[any], error) {\n\tres, err := c.rdb.Get(ctx, key).Result()\n\tif err == redis.Nil {\n\t\treturn nil, nil\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := q.Unmarshal([]byte(res)); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn q, nil\n}\n\nfunc (c *redisCacher) Store(ctx context.Context, key string, val *caches.Query[any]) error {\n\tres, err := val.Marshal()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.rdb.Set(ctx, key, res, 300*time.Second) // Set proper cache time\n\treturn nil\n}\n\nfunc (c *redisCacher) Invalidate(ctx context.Context) error {\n\tvar (\n\t\tcursor uint64\n\t\tkeys   []string\n\t)\n\tfor {\n\t\tvar (\n\t\t\tk   []string\n\t\t\terr error\n\t\t)\n\t\tk, cursor, err = c.rdb.Scan(ctx, cursor, fmt.Sprintf(\"%s*\", caches.IdentifierPrefix), 0).Result()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tkeys = append(keys, k...)\n\t\tif cursor == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif len(keys) \u003e 0 {\n\t\tif _, err := c.rdb.Del(ctx, keys...).Result(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc main() {\n\tdb, _ := gorm.Open(sqlite.Open(\"gorm.db\"), \u0026gorm.Config{\n\t\tAllowGlobalUpdate: true,\n\t})\n\n\tcachesPlugin := \u0026caches.Caches{Conf: \u0026caches.Config{\n\t\tCacher: \u0026redisCacher{\n\t\t\trdb: redis.NewClient(\u0026redis.Options{\n\t\t\t\tAddr:     \"localhost:6379\",\n\t\t\t\tPassword: \"\",\n\t\t\t\tDB:       0,\n\t\t\t}),\n\t\t},\n\t}}\n\n\t_ = db.Use(cachesPlugin)\n\n\t_ = db.AutoMigrate(\u0026UserRoleModel{})\n\t_ = db.AutoMigrate(\u0026UserModel{})\n\n\tdb.Delete(\u0026UserRoleModel{})\n\tdb.Delete(\u0026UserModel{})\n\n\tadminRole := \u0026UserRoleModel{\n\t\tName: \"Admin\",\n\t}\n\tdb.Save(adminRole)\n\n\tguestRole := \u0026UserRoleModel{\n\t\tName: \"Guest\",\n\t}\n\tdb.Save(guestRole)\n\n\tdb.Save(\u0026UserModel{\n\t\tName: \"ktsivkov\",\n\t\tRole: adminRole,\n\t})\n\n\tdb.Save(\u0026UserModel{\n\t\tName: \"anonymous\",\n\t\tRole: guestRole,\n\t})\n\n\tq1User := \u0026UserModel{}\n\tdb.WithContext(context.Background()).Find(q1User, \"Name = ?\", \"ktsivkov\")\n\tq2User := \u0026UserModel{}\n\tdb.WithContext(context.Background()).Find(q2User, \"Name = ?\", \"ktsivkov\")\n\n\tfmt.Println(fmt.Sprintf(\"%+v\", q1User))\n\tfmt.Println(fmt.Sprintf(\"%+v\", q2User))\n}\n```\n\n## Cacher Example (Memory)\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/go-gorm/caches/v4\"\n\t\"gorm.io/driver/sqlite\"\n\t\"gorm.io/gorm\"\n)\n\ntype UserRoleModel struct {\n\tgorm.Model\n\tName string `gorm:\"unique\"`\n}\n\ntype UserModel struct {\n\tgorm.Model\n\tName   string\n\tRoleId uint\n\tRole   *UserRoleModel `gorm:\"foreignKey:role_id;references:id\"`\n}\n\ntype memoryCacher struct {\n\tstore *sync.Map\n}\n\nfunc (c *memoryCacher) init() {\n\tif c.store == nil {\n\t\tc.store = \u0026sync.Map{}\n\t}\n}\n\nfunc (c *memoryCacher) Get(ctx context.Context, key string, q *caches.Query[any]) (*caches.Query[any], error) {\n\tc.init()\n\tval, ok := c.store.Load(key)\n\tif !ok {\n\t\treturn nil, nil\n\t}\n\n\tif err := q.Unmarshal(val.([]byte)); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn q, nil\n}\n\nfunc (c *memoryCacher) Store(ctx context.Context, key string, val *caches.Query[any]) error {\n\tc.init()\n\tres, err := val.Marshal()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.store.Store(key, res)\n\treturn nil\n}\n\nfunc (c *memoryCacher) Invalidate(ctx context.Context) error {\n\tc.store = \u0026sync.Map{}\n\treturn nil\n}\n\nfunc main() {\n\tdb, _ := gorm.Open(sqlite.Open(\"gorm.db\"), \u0026gorm.Config{\n\t\tAllowGlobalUpdate: true,\n\t})\n\n\tcachesPlugin := \u0026caches.Caches{Conf: \u0026caches.Config{\n\t\tCacher: \u0026memoryCacher{},\n\t}}\n\n\t_ = db.Use(cachesPlugin)\n\n\t_ = db.AutoMigrate(\u0026UserRoleModel{})\n\t_ = db.AutoMigrate(\u0026UserModel{})\n\n\tdb.Delete(\u0026UserRoleModel{})\n\tdb.Delete(\u0026UserModel{})\n\n\tadminRole := \u0026UserRoleModel{\n\t\tName: \"Admin\",\n\t}\n\tdb.Save(adminRole)\n\n\tguestRole := \u0026UserRoleModel{\n\t\tName: \"Guest\",\n\t}\n\tdb.Save(guestRole)\n\n\tdb.Save(\u0026UserModel{\n\t\tName: \"ktsivkov\",\n\t\tRole: adminRole,\n\t})\n\n\tdb.Save(\u0026UserModel{\n\t\tName: \"anonymous\",\n\t\tRole: guestRole,\n\t})\n\n\tq1User := \u0026UserModel{}\n\tdb.WithContext(context.Background()).Find(q1User, \"Name = ?\", \"ktsivkov\")\n\tq2User := \u0026UserModel{}\n\tdb.WithContext(context.Background()).Find(q2User, \"Name = ?\", \"ktsivkov\")\n\n\tfmt.Println(fmt.Sprintf(\"%+v\", q1User))\n\tfmt.Println(fmt.Sprintf(\"%+v\", q2User))\n}\n```\n\n## License\n\nMIT license.\n\n## Easer\nThe easer is an adjusted version of the [ServantGo](https://github.com/ktsivkov/servantgo) library to fit the needs of this plugin.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgo-gorm%2Fcaches","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgo-gorm%2Fcaches","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgo-gorm%2Fcaches/lists"}