{"id":25473625,"url":"https://github.com/blastrain/rapidash","last_synced_at":"2025-09-01T18:45:07.426Z","repository":{"id":39417434,"uuid":"203494424","full_name":"blastrain/rapidash","owner":"blastrain","description":"Go package for database record or other data caching","archived":false,"fork":false,"pushed_at":"2023-01-07T08:58:44.000Z","size":1786,"stargazers_count":82,"open_issues_count":26,"forks_count":9,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-04-09T11:23:31.242Z","etag":null,"topics":["cache","go","golang","golang-library"],"latest_commit_sha":null,"homepage":null,"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/blastrain.png","metadata":{"files":{"readme":"README.ja.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}},"created_at":"2019-08-21T02:55:34.000Z","updated_at":"2024-12-05T09:07:21.000Z","dependencies_parsed_at":"2023-02-06T17:45:28.787Z","dependency_job_id":null,"html_url":"https://github.com/blastrain/rapidash","commit_stats":null,"previous_names":["knocknote/rapidash"],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/blastrain/rapidash","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blastrain%2Frapidash","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blastrain%2Frapidash/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blastrain%2Frapidash/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blastrain%2Frapidash/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/blastrain","download_url":"https://codeload.github.com/blastrain/rapidash/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blastrain%2Frapidash/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273175003,"owners_count":25058472,"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","status":"online","status_checked_at":"2025-09-01T02:00:09.058Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["cache","go","golang","golang-library"],"created_at":"2025-02-18T10:35:47.497Z","updated_at":"2025-09-01T18:45:07.389Z","avatar_url":"https://github.com/blastrain.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Rapidash [![README_EN](https://img.shields.io/badge/readme-EN-green.svg)](https://github.com/blastrain/rapidash/blob/master/README.md)\n\n\u003cimg width=\"300\" alt=\"2\" src=\"https://user-images.githubusercontent.com/209884/34214640-697191ce-e5e6-11e7-8a12-c1cd45af2320.png\"\u003e\n\n`Rapidash` はGo言語から利用できる高機能なキャッシュライブラリです。  \n`memcached` や `Redis` といった一般的なキャッシュサーバーを利用したキャッシュの利用はもちろん、  \nデータベース上の検索しか行わないテーブルのデータをアプリケーションのメモリ上に展開して高速にアクセスすることや、  \n読み書きをおこなうテーブルのデータをデータベースから読み込むタイミングでキャッシュサーバーに格納し、以降の同じ検索クエリをキャッシュサーバーから取得することによってクエリの応答性能を上げたりデータベースの負荷を下げることができる機能(いわゆる `Read-Through/Write-Through` )などがあります。  \n\n主要な機能を並べると以下のようになります。  \n\n# 1. 主な機能\n\n1. 検索しか行わないテーブルのデータをアプリケーション起動時にデータベースからすべて吸い上げ、インデックスの定義に従ってメモリ上にB＋Tree構造で展開する。検索時はクエリビルダーを通して行い、範囲検索も可能\n2. 読み書きを行うテーブルのレコードを `memcached` や `Redis` といったキャッシュサーバーに格納し、高速に検索したり検索クエリの負荷分散をおこなう\n3. `memcached` や `Redis` を利用した汎用的なキャッシュ操作\n4. 2 や 3 を行う際に擬似的なトランザクションを利用できる\n5. キャッシュサーバーが複数ある場合に、キーの種類によって格納するサーバーを制御する機能\n6. Consistent Hashing によるキャッシュサーバーの増減に対する効率的な分散手法\n7. `reflect` を使わない高速なエンコード・デコード処理\n8. `msgpack` による値の自動圧縮\n\n`Rapidash` の内部は、機能によって大きく3つのコンポーネントに分けられています。  \n1 で挙げた、検索しか行わないテーブルのレコードをキャッシュするためのコンポーネントを `FirstLevelCache`   \n2 で挙げた、読み書きを行うテーブルのレコードをキャッシュするためのコンポーネントを `SecondLevelCache`  \n3 で挙げた、汎用的なキャッシュ操作をおこなうためのコンポーネントを `LastLevelCache` と呼んでいます。  \n\n`Rapidash` が提供するインターフェースを利用する限りにおいて、これらのコンポーネントの存在を意識する必要はありませんが、\n以降では、上記で挙げた機能の詳細について説明する際にこれらの名前を利用します。\n\n# 2. インストール\n\n```\ngo get -u go.knocknote.io/rapidash\n```\n\n# 3. ドキュメント\n\nGoDoc は [こちら](https://godoc.org/go.knocknote.io/rapidash)\n\n# 4. 使い方\n\n## 4.1 検索のみのテーブルのデータを高速に検索する\n\nアプリケーションには、マスターデータのようにユーザの行動起因で変化しないデータセットが存在する場合があります。  \nそういった変化しないデータをアプリケーション起動時にキャッシュすることで、高速に\u000b検索するための仕組みを提供しています。  \n\n例えば以下のような `events` テーブルがあり、あらかじめイベント情報を登録していたとします。\n\n```sql\nCREATE TABLE events (\n  id bigint(20) unsigned NOT NULL,\n  event_id bigint(20) unsigned NOT NULL,\n  term enum('early_morning', 'morning', 'daytime', 'evening', 'night', 'midnight') NOT NULL,\n  start_week int(10) unsigned NOT NULL,\n  end_week int(10) unsigned NOT NULL,\n  created_at datetime NOT NULL,\n  updated_at datetime NOT NULL,\n  PRIMARY KEY (id),\n  UNIQUE KEY (event_id, start_week)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n```\n\nこの `events` テーブルの情報をすべてキャッシュするには、以下のように記述します。\n\n```go\npackage main\n\nimport (\n\t\"database/sql\"\n\t\"time\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"go.knocknote.io/rapidash\"\n)\n\n// スキーマに対応するGo側の型を用意する\ntype Event struct {\n\tID        int64\n\tEventID   int64\n\tTerm      string\n\tStartWeek uint8\n\tEndWeek   uint8\n\tCreatedAt time.Time\n\tUpdatedAt time.Time\n}\n\n// デコードのためのメソッドを定義する\nfunc (e *Event) DecodeRapidash(dec rapidash.Decoder) error {\n\te.ID = dec.Int64(\"id\")\n\te.EventID = dec.Int64(\"event_id\")\n\te.Term = dec.String(\"term\")\n\te.StartWeek = dec.Uint8(\"start_week\")\n\te.EndWeek = dec.Uint8(\"end_week\")\n\te.CreatedAt = dec.Time(\"created_at\")\n\te.UpdatedAt = dec.Time(\"updated_at\")\n\treturn dec.Error()\n}\n\n// events テーブルの各カラムとGoの型のマッピングを作成\nfunc (e *Event) Struct() *rapidash.Struct {\n\treturn rapidash.NewStruct(\"events\").\n\t\tFieldInt64(\"id\").\n\t\tFieldInt64(\"event_id\").\n\t\tFieldString(\"term\").\n\t\tFieldUint8(\"start_week\").\n\t\tFieldUint8(\"end_week\").\n\t\tFieldTime(\"created_at\").\n\t\tFieldTime(\"updated_at\")\n}\n\nfunc main() {\n\tconn, err := sql.Open(\"mysql\", \"root:@tcp(localhost:3306)/rapidash?parseTime=true\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Rapidash インスタンスの作成\n\tcache, err := rapidash.New()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t\n\t// events テーブルを参照できるコネクションをもった sql.DB と 型情報を利用して、メモリ上にキャッシュを構築\n\tif err := cache.WarmUp(conn, new(Event).Struct(), true); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Rapidash インスタンスから `*rapidash.Tx` インスタンスを作成\n\ttx, err := cache.Begin()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// SELECT * FROM events\n\t//   WHERE `event_id` = 1 AND\n\t//      `start_week` \u003c= 3 AND\n\t//      `end_week` \u003e= 3   AND\n\t//      `term` = daytime\n\tbuilder := rapidash.NewQueryBuilder(\"events\").\n\t\tEq(\"event_id\", uint64(1)).\n\t\tLte(\"start_week\", uint8(3)).\n\t\tGte(\"end_week\", uint8(3)).\n\t\tEq(\"term\", \"daytime\")\n\tvar event Event\n\tif err := tx.FindByQueryBuilder(builder, \u0026event); err != nil {\n\t\tpanic(err)\n\t}\n}\n```\n\n\n## 4.2 読み書きを行うテーブルのデータを高速に検索する\n\n```sql\nCREATE TABLE IF NOT EXISTS user_logins (\n  id bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  user_id bigint(20) unsigned NOT NULL,\n  user_session_id bigint(20) unsigned NOT NULL,\n  login_param_id bigint(20) unsigned NOT NULL,\n  name varchar(255) NOT NULL,\n  created_at datetime NOT NULL,\n  updated_at datetime NOT NULL,\n  PRIMARY KEY (id),\n  UNIQUE KEY (user_id, user_session_id),\n  KEY (user_id, login_param_id)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8\n```\n\n上記のようなスキーマのテーブルのデータをキャッシュサーバに適宜格納・削除しながら高速に検索するには\n以下のように記述します\n\n※ 事前に `memcached` が `11211` ポートで起動済みとします\n\n```go\npackage main\n\nimport (\n\t\"database/sql\"\n\t\"time\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"go.knocknote.io/rapidash\"\n)\n\n// スキーマに対応するGo側の型を用意する\ntype UserLogin struct {\n\tID            int64\n\tUserID        int64\n\tUserSessionID int64\n\tLoginParamID  int64\n\tName          string\n\tCreatedAt     time.Time\n\tUpdatedAt     time.Time\n}\n\n// エンコードするためのメソッドを定義する\nfunc (u *UserLogin) EncodeRapidash(enc Encoder) error {\n\tif u.ID != 0 {\n\t\tenc.\bInt64(\"id\", u.ID)\n\t}\n\tenc.Int64(\"user_id\", u.UserID)\n\tenc.Int64(\"user_session_id\", u.UserSessionID)\n\tenc.Int64(\"login_param_id\", u.LoginParamID)\n\tenc.String(\"name\", u.Name)\n\tenc.Time(\"created_at\", u.CreatedAt)\n\tenc.Time(\"updated_at\", u.UpdatedAt)\n\treturn enc.Error()\n}\n\n// デコードするためのメソッドを定義する\nfunc (u *UserLogin) DecodeRapidash(dec Decoder) error {\n\tu.ID = dec.Int64(\"id\")\n\tu.UserID = dec.Int64(\"user_id\")\n\tu.UserSessionID = dec.Int64(\"user_session_id\")\n\tu.LoginParamID = dec.Int64(\"login_param_id\")\n\tu.Name = dec.String(\"name\")\n\tu.CreatedAt = dec.Time(\"created_at\")\n\tu.UpdatedAt = dec.Time(\"updated_at\")\n\treturn dec.Error()\n}\n\n// user_logins テーブルの各カラムとGoの型のマッピングを作成\nfunc (u *UserLogin) Struct() *rapidash.Struct {\n\treturn rapidash.NewStruct(\"user_logins\").\n\t\tFieldInt64(\"id\").\n\t\tFieldInt64(\"user_id\").\n\t\tFieldInt64(\"user_session_id\").\n\t\tFieldInt64(\"login_param_id\").\n\t\tFieldString(\"name\").\n\t\tFieldTime(\"created_at\").\n\t\tFieldTime(\"updated_at\")\n}\n\nfunc main() {\n\tconn, err := sql.Open(\"mysql\", \"root:@tcp(localhost:3306)/rapidash?parseTime=true\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Rapidash インスタンスの作成\n\tcache, err := rapidash.New(rapidash.ServerAddrs(\"localhost:11211\"))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// user_logins テーブルを参照できるコネクションをもった sql.DB と 型情報を利用してキャッシュを利用する準備をする\n\tif err := cache.WarmUp(conn, new(UserLogin).Struct(), false); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// sql.Tx の作成\n\ttxConn, err := conn.Begin()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// Rapidash インスタンスから `*rapidash.Tx` インスタンスを作成\n\ttx, err := cache.Begin(txConn)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// SELECT * FROM user_logins\n\t//   WHERE `user_id` = 1 AND `user_session_id` = 1\n\tbuilder := rapidash.NewQueryBuilder(\"user_logins\").\n\t\tEq(\"user_id\", int64(1)).\n\t\tEq(\"user_session_id\", int64(1))\n\n\t// キャッシュから検索し、なければデータベースから検索する\n\tvar userLogin UserLogin\n\tif err := tx.FindByQueryBuilder(builder, \u0026userLogin); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// 作成したキャッシュをキャッシュサーバーに保存\n\tif err := tx.Commit(); err != nil {\n\t\tpanic(err)\n\t}\n}\n```\n\n## 4.3 任意の値のキャッシュの読み書きを行う\n\n※ 事前に `memcached` が `11211` ポートで起動済みとします\n\n```go\npackage main\n\nimport (\n\t\"go.knocknote.io/rapidash\"\n)\n\nfunc main() {\n\t// Rapidash インスタンスの作成\n\tcache, err := rapidash.New(rapidash.ServerAddrs(\"localhost:11211\"))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\ttx, err := cache.Begin()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t\n\t// int の値の読み書き\n\tif err := tx.Create(\"key\", rapidash.Int(1)); err != nil {\n\t\tpanic(err)\n\t}\n\tvar v int\n\tif err := tx.Find(\"key\", rapidash.IntPtr(\u0026v)); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// 作ったキャッシュを保存する\n\tif err := tx.Commit(); err != nil {\n\t\tpanic(err)\n\t}\n}\n```\n\n# 5. LICENSE\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblastrain%2Frapidash","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblastrain%2Frapidash","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblastrain%2Frapidash/lists"}