{"id":26590537,"url":"https://github.com/emcfarlane/observer","last_synced_at":"2025-03-23T13:52:39.415Z","repository":{"id":57576732,"uuid":"192176611","full_name":"emcfarlane/observer","owner":"emcfarlane","description":"Observer pattern","archived":false,"fork":false,"pushed_at":"2023-04-22T14:29:41.000Z","size":69,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-06-20T10:16:28.115Z","etag":null,"topics":["go"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/emcfarlane.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":"2019-06-16T10:12:49.000Z","updated_at":"2022-08-06T03:17:11.000Z","dependencies_parsed_at":"2024-06-20T09:24:56.389Z","dependency_job_id":"5e44ed52-82dd-4659-9ce8-7cbe457f18bf","html_url":"https://github.com/emcfarlane/observer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emcfarlane%2Fobserver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emcfarlane%2Fobserver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emcfarlane%2Fobserver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emcfarlane%2Fobserver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emcfarlane","download_url":"https://codeload.github.com/emcfarlane/observer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245111929,"owners_count":20562511,"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":["go"],"created_at":"2025-03-23T13:52:38.559Z","updated_at":"2025-03-23T13:52:39.382Z","avatar_url":"https://github.com/emcfarlane.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# observer\n\n[![GoDoc](https://godoc.org/github.com/emcfarlane/observer?status.svg)](https://godoc.org/github.com/emcfarlane/observer)\n\nExperimental concurrency primatives!\n\n## Subject[T]\n\nSubject is a generic unbounded concurrent observer.\nIt's based on an atomic counter per fixed length array. Adding elements increments \nthe counter to select the insert position. If the counter is greater than the \nlist size a new array is allocated and the process is repeated for the new array.\nReaders hold a pointer to a position in the array. Replaces a list of channels.\n\n```\nvar s observer.Subject[string]\ngo s.Set(\"hello\")\n\ngo func() {\n\tfor v := s.View(); ; v = v.Next() {\n\t\tfmt.Println(v.Value())\n\t}\n}()\n```\n\n### Benchmark\n\nObserver allocations are amortize over the array length.\nFaster than a single channel and cost is linear when increase the number of readers unlike a an array of channels.\nTrade off is in the queue becoming unbounded.\n\n```\nBenchmarkObserver/1-8    35960995  34.22 ns/op  9 B/op  0 allocs/op\nBenchmarkObserver/8-8     7084509  145.5 ns/op  9 B/op  0 allocs/op\nBenchmarkObserver/32-8   14361388  94.58 ns/op  9 B/op  0 allocs/op\nBenchmarkChannel/1-8     20000000   74.2 ns/op  0 B/op  0 allocs/op\nBenchmarkChannel/8-8      1000000   1353 ns/op  0 B/op  0 allocs/op\nBenchmarkChannel/32-8      200000   6261 ns/op  0 B/op  0 allocs/op\n```\n\n##  Map\n\nConcurrent map is similar to the builtin `sync.Map` but with a different locking stratergy.\nThe map is duplicated into a read/write pair that are switched based on three atomic counters.\nA counter shared between the two maps and a counter per map.\nWhen loading the maps mutual counter is incremented.\nThis flags which map to read from.\nOnce the read is finished the maps counter is incremented.\nWriters wait for all readers of the map to leave, then swap the read/write map flag so the next write can be processed.\n\nThis has some benifits/trade offs in read/write workloads. See the benchmarks.\nThere is no concurrent process, like compaction, running alongside the map which has the benifit of maintaing throughput under sustained load.\n\nRange operation isn't support as this would lock the entire map, unlike `sync.Map`.\nHowever we gain a `Tx` method that supports a transaction like read/write`.\nUseful for counters and optional updates.\n\n```\nvar m observer.Map\nm.Set(\"key\", 1)\n\nm.Tx(\"key\", func(val interface{}, ok bool) {\n  if ok {\n    return val.(int) + 1, ok\n  }\n  return nil, false\n})\nval, ok := m.Get(key)\n```\n\n### Benchmark\n\nBenchmark is based off of Dgraphs [cache testing](https://github.com/dgraph-io/benchmarks/blob/master/cachebench/cache_bench_test.go).\n\nkey: Map is this Map, SyncMap is `sync.Map` and RWMap is a Go map with a `sync.Mutex` to protect it.\n\n```\nBenchmarkCaches/MapZipfRead\nBenchmarkCaches/MapZipfRead-8           21317090                54.80 ns/op            0 B/op          0 allocs/op\nBenchmarkCaches/SyncMapZipfRead\nBenchmarkCaches/SyncMapZipfRead-8       22807447                54.58 ns/op            0 B/op          0 allocs/op\nBenchmarkCaches/RWMapZipfRead\nBenchmarkCaches/RWMapZipfRead-8         27089253                45.28 ns/op            0 B/op          0 allocs/op\nBenchmarkCaches/MapOneKeyRead\nBenchmarkCaches/MapOneKeyRead-8         31896468                38.77 ns/op            0 B/op          0 allocs/op\nBenchmarkCaches/SyncMapOneKeyRead\nBenchmarkCaches/SyncMapOneKeyRead-8     100000000               10.91 ns/op            0 B/op          0 allocs/op\nBenchmarkCaches/RWMapOneKeyRead\nBenchmarkCaches/RWMapOneKeyRead-8       28684920                41.93 ns/op            0 B/op          0 allocs/op\nBenchmarkCaches/MapZipIfWrite\nBenchmarkCaches/MapZipIfWrite-8          2000820               610.6 ns/op            55 B/op          3 allocs/op\nBenchmarkCaches/SyncMapZipfWrite\nBenchmarkCaches/SyncMapZipfWrite-8       2193643               550.7 ns/op            71 B/op          5 allocs/op\nBenchmarkCaches/RWMapZipfWrite\nBenchmarkCaches/RWMapZipfWrite-8         4045146               298.6 ns/op            15 B/op          1 allocs/op\nBenchmarkCaches/MapOneIfWrite\nBenchmarkCaches/MapOneIfWrite-8          2679439               446.5 ns/op            56 B/op          4 allocs/op\nBenchmarkCaches/SyncMapOneKeyWrite\nBenchmarkCaches/SyncMapOneKeyWrite-8     3835924               312.0 ns/op            72 B/op          5 allocs/op\nBenchmarkCaches/RWMapOneKeyWrite\nBenchmarkCaches/RWMapOneKeyWrite-8       5000916               240.5 ns/op            16 B/op          2 allocs/op\nBenchmarkCaches/MapZipfMixed\nBenchmarkCaches/MapZipfMixed-8          16913481                68.68 ns/op            2 B/op          0 allocs/op\nBenchmarkCaches/SyncMapZipfMixed\nBenchmarkCaches/SyncMapZipfMixed-8      19314884                58.52 ns/op           14 B/op          0 allocs/op\nBenchmarkCaches/RWMapZipfMixed\nBenchmarkCaches/RWMapZipfMixed-8         2572059               443.6 ns/op             1 B/op          0 allocs/op\nBenchmarkCaches/MapOneKeyMixed\nBenchmarkCaches/MapOneKeyMixed-8        26252911                45.45 ns/op            2 B/op          0 allocs/op\nBenchmarkCaches/SyncMapOneKeyMixed\nBenchmarkCaches/SyncMapOneKeyMixed-8    64579701                18.14 ns/op            6 B/op          0 allocs/op\nBenchmarkCaches/RWMapOneKeyMixed\nBenchmarkCaches/RWMapOneKeyMixed-8       8419248               141.7 ns/op             0 B/op          0 allocs/op\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femcfarlane%2Fobserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femcfarlane%2Fobserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femcfarlane%2Fobserver/lists"}