{"id":24314903,"url":"https://github.com/karrick/goswarm","last_synced_at":"2025-09-26T21:30:46.694Z","repository":{"id":57484308,"uuid":"76911544","full_name":"karrick/goswarm","owner":"karrick","description":"Go Stale While Asynchronously Revalidate Memoization","archived":false,"fork":false,"pushed_at":"2023-03-19T18:31:31.000Z","size":66,"stargazers_count":11,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-11T05:24:26.601Z","etag":null,"topics":["concurrency","concurrent-data-structure","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/karrick.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}},"created_at":"2016-12-20T01:28:13.000Z","updated_at":"2023-04-29T23:22:47.000Z","dependencies_parsed_at":"2024-06-21T05:50:05.337Z","dependency_job_id":null,"html_url":"https://github.com/karrick/goswarm","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karrick%2Fgoswarm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karrick%2Fgoswarm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karrick%2Fgoswarm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karrick%2Fgoswarm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karrick","download_url":"https://codeload.github.com/karrick/goswarm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234345365,"owners_count":18817558,"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":["concurrency","concurrent-data-structure","golang","golang-library"],"created_at":"2025-01-17T10:16:09.368Z","updated_at":"2025-09-26T21:30:41.452Z","avatar_url":"https://github.com/karrick.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# goswarm\n\nGo Stale While Asynchronously Revalidate Memoization\n\n## DESCRIPTION\n\ngoswarm is a library written in Go for storing the results of\nexpensive function calls and returning the cached result when the same\ninput key occurs again.\n\nIn addition to the examples provided below, documentation can be found\nat\n[![GoDoc](https://godoc.org/github.com/karrick/goswarm?status.svg)](https://godoc.org/github.com/karrick/goswarm).\n\n```Go\n    simple, err := goswarm.NewSimple(nil)\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    // you can store any Go type in a Swarm\n    simple.Store(\"someKeyString\", 42)\n    simple.Store(\"anotherKey\", struct{}{})\n    simple.Store(\"yetAnotherKey\", make(chan interface{}))\n\n    // but when you retrieve it, you are responsible to perform type assertions\n    key := \"yetAnotherKey\"\n    value, ok := simple.Load(key)\n    if !ok {\n        panic(fmt.Errorf(\"cannot find %q\", key))\n    }\n    value = value.(chan interface{})\n\n    simple.Delete(\"anotherKey\")\n```\n\nAs seen above, goswarm's API is similar to that of any associative\narray, also known as a map in Go terminology: It allows storing a\nvalue to be retrieved using a specified key, and overwriting any value\nthat might already be present in the map. It allows loading the value\nassociated with the specified key already stored in the map. It allows\ndeleting a specified key and its associated value stored in the map.\n\ngoswarm differs from most traditional associative array APIs in that\nit provides a method to load the value associated with a specified\nkey, and if that key is not found in the map, to invoke a specified\nlookup function to fetch the value for that key.\n\n```Go\n    simple, err := goswarm.NewSimple(\u0026goswarm.Config{\n        Lookup: func(key string) (interface{}, error) {\n            // TODO: do slow calculation or make a network call\n            result := key // example\n            return result, nil\n        },\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer func() { _ = simple.Close() }()\n\n    value, err := simple.Query(\"%version\")\n    if !err {\n        panic(fmt.Errorf(\"cannot retrieve value for key %q: %s\", key, err))\n    }\n    fmt.Printf(\"The value is: %v\\n\", value)\n```\n\n## Stale-While-Revalidate and Stale-If-Error\n\nIn addition, goswarm provides stale-while-revalidate and\nstale-if-error compatible features in its simple API.\n\n### Stale-While-Revalidate\n\nWhen the user requests the value associated with a particular key,\ngoswarm determines whether that key-value pair is present, and if so,\nwhether that value has become stale. If the value is stale, goswarm\nwill return the previously stored value to the client, but spawn an\nasynchronous routine to fetch a new value for that key and store that\nvalue in the map to be used for future queries.\n\nWhen the user requests the value associated with a particular key that\nhas expired, goswarm will not return that value, but rather\nsynchronously fetch an updated value to store in the map and return to\nthe user.\n\n### Stale-If-Error\n\nWhen fetching a new value to replace a value that has become stale,\nthe lookup callback funciton might return an error. Perhaps the remote\nnetwork resource used to fetch responses is offline. In these cases,\ngoswarm will not overwrite the stale value with the error, but\ncontinue to serve the stale value until the lookup callback function\nreturns a new value rather than an error, or the value expires, in\nwhich case, the error is returned to the user.\n\n## Periodic Removal Of Expired Keys\n\nIf `GCPeriodicty` configuration value is greater than the zero-value\nfor time.Duration, goswarm spawns a separate go-routine that invokes\nthe `GC` method periodically, removing all key-value pairs from the\ndata map that have an expired time. When this feature is used, the\n`Close` method must be invoked to stop and release that go-routine.\n\n```Go\n    simple, err := goswarm.NewSimple(\u0026goswarm.Config{\n        GoodExpiryDuration: 24 * time.Hour,\n        BadExpiryDuration:  5 * time.Minute,\n        GCPeriodicity:      time.Hour,\n        Lookup:             func(key string) (interface{}, error) {\n            // TODO: do slow calculation or make a network call\n            result := key // example\n            return result, nil\n        },\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer func() { _ = simple.Close() }()\n```\n\n## Note About Choosing Stale and Expiry Durations\n\nDifferent applications may require different logic, however if your\napplication needs to continue processing data and serving requests\neven when a downstream dependency is subject to frequent high latency\nperiods or faults, it is recommended to set the GoodStaleDuration\nperiod to a low enough value to ensure data is reasonably up-to-date,\nbut extend the GoodExpiryDuration to be long enough that your\napplication can still operate using possibly stale data. The goswarm\nlibrary will repeatedly attempt to fetch a new value from the\ndownstream service, but until a defined very long period of time\ntranspires, your service will be relatively insulated from these type\nof downstream faults and latencies.\n\n```Go\n    simple, err := goswarm.NewSimple(\u0026goswarm.Config{\n        GoodStaleDuration:  time.Minute,\n        GoodExpiryDuration: 24 * time.Hour,\n        BadStaleDuration:   time.Minute,\n        BadExpiryDuration:  5 * time.Minute,\n        GCPeriodicity:      time.Hour,\n        Lookup:             func(key string) (interface{}, error) {\n            // TODO: do slow calculation or make a network call\n            result := key // example\n            return result, nil\n        },\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer func() { _ = simple.Close() }()\n\n    value, err := simple.Query(\"%version\")\n    if !err {\n        panic(fmt.Errorf(\"cannot retrieve value for key %q: %s\", key, err))\n    }\n    fmt.Printf(\"The value is: %v\\n\", value)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarrick%2Fgoswarm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarrick%2Fgoswarm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarrick%2Fgoswarm/lists"}