{"id":36549968,"url":"https://github.com/juliaqiuxy/wfcache","last_synced_at":"2026-01-12T06:32:32.428Z","repository":{"id":57588919,"uuid":"372658938","full_name":"juliaqiuxy/wfcache","owner":"juliaqiuxy","description":"Multi-layered cache with waterfall hit propagation and built-in storage adapters for DynamoDB, Redis, BigCache, GoLRU etc.","archived":false,"fork":false,"pushed_at":"2021-11-22T18:19:24.000Z","size":80,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-06-20T12:07:09.336Z","etag":null,"topics":["bigcache","cache","dynamodb","go","redis"],"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/juliaqiuxy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-06-01T00:25:38.000Z","updated_at":"2021-11-22T18:26:02.000Z","dependencies_parsed_at":"2022-09-13T12:40:35.861Z","dependency_job_id":null,"html_url":"https://github.com/juliaqiuxy/wfcache","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/juliaqiuxy/wfcache","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliaqiuxy%2Fwfcache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliaqiuxy%2Fwfcache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliaqiuxy%2Fwfcache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliaqiuxy%2Fwfcache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/juliaqiuxy","download_url":"https://codeload.github.com/juliaqiuxy/wfcache/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/juliaqiuxy%2Fwfcache/sbom","scorecard":{"id":542008,"data":{"date":"2025-08-11","repo":{"name":"github.com/juliaqiuxy/wfcache","commit":"6d628828702de59f8899bd8d7a6df9362e460af4"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.2,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:31: update your workflow using https://app.stepsecurity.io/secureworkflow/juliaqiuxy/wfcache/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:33: update your workflow using https://app.stepsecurity.io/secureworkflow/juliaqiuxy/wfcache/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:37: update your workflow using https://app.stepsecurity.io/secureworkflow/juliaqiuxy/wfcache/ci.yml/main?enable=pin","Warn: containerImage not pinned by hash: Dockerfile:3","Warn: goCommand not pinned by hash: Dockerfile:7","Warn: downloadThenRun not pinned by hash: Dockerfile:8","Info:   0 out of   3 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 containerImage dependencies pinned","Info:   0 out of   1 goCommand dependencies pinned","Info:   0 out of   1 downloadThenRun dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":7,"reason":"3 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2022-0635","Warn: Project is vulnerable to: GO-2022-0646","Warn: Project is vulnerable to: GO-2022-0603 / GHSA-hp87-p4gw-j4gq"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T08:25:31.374Z","repository_id":57588919,"created_at":"2025-08-20T08:25:31.374Z","updated_at":"2025-08-20T08:25:31.374Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28336316,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T06:09:07.588Z","status":"ssl_error","status_checked_at":"2026-01-12T06:05:18.301Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["bigcache","cache","dynamodb","go","redis"],"created_at":"2026-01-12T06:32:31.619Z","updated_at":"2026-01-12T06:32:32.422Z","avatar_url":"https://github.com/juliaqiuxy.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Waterfall Cache\n\n[![GoDoc](https://godoc.org/github.com/juliaqiuxy/wfcache?status.svg)](https://godoc.org/github.com/juliaqiuxy/wfcache) [![wfcache CI](https://github.com/juliaqiuxy/wfcache/actions/workflows/ci.yml/badge.svg)](https://github.com/juliaqiuxy/wfcache/actions/workflows/ci.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/juliaqiuxy/wfcache)](https://goreportcard.com/report/github.com/juliaqiuxy/wfcache) [![npm](https://img.shields.io/github/license/juliaqiuxy/wfcache.svg?style=flat-square)](https://github.com/juliaqiuxy/wfcache/blob/master/LICENSE.md)\n\nwfcache is a multi-layered cache with waterfall hit propagation and built-in storage adapters for DynamoDB, Redis, BigCache (in-memory), and GoLRU (in-memory)\n\nwfcache is effective for read-heavy workloads and it can be used both as a side-cache or a read-through/write-through cache.\n\nThis libary is used in production by an application that receives 8 Million hits a month.\n\n## Built-in Storage Adapters\n\n| Package | Description | Eviction strategy\n| --- | --- | --- |\n| [DynamoDB](https://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb) | DynamoDB | [TTL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/howitworks-ttl.html) |\n| [Redis](https://github.com/go-redis/redis) | Redis | TTL/[LRU](https://redis.io/topics/lru-cache) |\n| [BigCache](https://github.com/allegro/bigcache) | Performant on heap storage with [minimal GC](https://github.com/allegro/bigcache#gc-pause-time) | TTL ([enforced on add](https://github.com/allegro/bigcache/issues/123#issuecomment-468902638)) |\n| [GoLRU](https://github.com/manucorporat/golru) | In-memory storage with approximated LRU similar to Redis | TTL/LRU |\n| [Basic](basic/basic.go) | Basic in-memory storage (not recommended) | TTL (enforced on get) |\n\n## Installation\n\nTo retrieve wfcache, run:\n\n```sh\n$ go get github.com/juliaqiuxy/wfcache\n```\n\n### Usage\n\n```go\nimport (\n  \"github.com/juliaqiuxy/wfcache\"\n  basic \"github.com/juliaqiuxy/wfcache/basic\"\n  bigcache \"github.com/juliaqiuxy/wfcache/bigcache\"\n  dynamodb \"github.com/juliaqiuxy/wfcache/dynamodb\"\n)\n\nc, err := wfcache.New(\n  basic.Create(5 * time.Minute),\n  bigcache.Create(2 * time.Hour),\n  dynamodb.Create(dynamodbClient, \"my-cache-table\", 24 * time.Hour),\n)\n\nitems, err := c.BatchGet(keys)\nif err == wfcache.ErrPartiallyFulfilled {\n  fmt.Println(\"Somethings are missing\")\n}\n```\n\n## Usage with hooks\n\nYou can configure wfcache to notify you when each storage operation starts and finishes. This is useful when you want to do performance logging, tracing etc.\n\n```go\nimport (\n  \"context\"\n  \"github.com/juliaqiuxy/wfcache\"\n  \"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer\"\n  basic \"github.com/juliaqiuxy/wfcache/basic\"\n)\n\nfunc onStartStorageOp(ctx context.Context, opName string) interface{} {\n  span, _ := tracer.StartSpanFromContext(ctx, opName)\n  return span\n}\n\nfunc onFinishStorageOp(span interface{}) {\n  span.(ddtrace.Span).Finish()\n}\n\nwfcache.NewWithHooks(\n  onStartStorageOp,\n  onFinishStorageOp,\n  basic.Create(5 * time.Minute),\n)\n```\n\n## How it works\n\nThe following steps outline how reads from wfcache work:\n\n- When getting a value, wfcache tries to read it from the first storage layer (e.g. BigCache).\n- If the storage layer is not populated with the requested key-value pair (cache miss), transparent to the application, wfcache notes the missing key and moves on to the next layer. This continues until all configured storage options are exhausted.\n- When there is a cache hit, wfcache then primes each storage layer with a previously reported cache miss to make the data available for any subsequent reads.\n- wfcache returns the key-value pair back to the application\n\nIf you want to use wfcache as read-through cache, you can implement a [custom adapter](#implementing-custom-adapters) for your source database and configure it as the last storage layer. In this setup, a cache miss only ever happens in intermediate storage layers (which are then primed as your source storage resolves values) but wfcache would always yield data.\n\nWhen mutating wfcache, key-value pairs are written and removed from all storage layers. To mutate a specific storage layer in isolation, you can ask wfcache to provide you with a reference to the underlying slice of storages by calling its `Storages()` method.\n\n### Cache eviction\n\nwfcache leaves it up to each storage layer to implement their eviction strategy. Built-in adapters use a combination of Time-to-Live (TTL) and Least Recently Used (LRU) algorithm to decide which items to evict. \n\nAlso note that the built-in Basic storage is not meant for production use as the TTL enforcement only happens if and when a \"stale\" item is requested form the storage layer.\n\n## Implementing Custom Adapters\n\nFor use cases where:\n\n- you require a stroge adapter which is not [included](#built-in-storage-adapters) in wfcache, or\n- you want to use wfcache as a read-through/write-through cache\n\nit is trivial to extend wfcache by implementing the following adapter interface:\n\n```go\ntype Storage interface {\n  Get(ctx context.Context, key string) *CacheItem\n  BatchGet(ctx context.Context, keys []string) []*CacheItem\n  Set(ctx context.Context, key string, value []byte) error\n  BatchSet(ctx context.Context, pairs map[string][]byte) error\n  Del(ctx context.Context, key string) error\n  BatchDel(ctx context.Context, keys []string) error\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliaqiuxy%2Fwfcache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuliaqiuxy%2Fwfcache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliaqiuxy%2Fwfcache/lists"}