{"id":37871309,"url":"https://github.com/mailsac/dracula","last_synced_at":"2026-01-16T16:41:08.025Z","repository":{"id":45351587,"uuid":"418580272","full_name":"mailsac/dracula","owner":"mailsac","description":"Count Dracula is a fast, lightweight metrics server that counts entries while automatically expiring old ones","archived":false,"fork":false,"pushed_at":"2024-06-24T23:08:40.000Z","size":206,"stargazers_count":52,"open_issues_count":2,"forks_count":3,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-06-24T23:48:32.903Z","etag":null,"topics":["metrics-server","session-store","throttling-server"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mailsac.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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":"2021-10-18T16:22:08.000Z","updated_at":"2024-06-24T23:47:25.000Z","dependencies_parsed_at":"2024-06-19T06:11:50.224Z","dependency_job_id":"d60d85cd-2213-43dc-b844-c6cc2b653e23","html_url":"https://github.com/mailsac/dracula","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/mailsac/dracula","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mailsac%2Fdracula","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mailsac%2Fdracula/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mailsac%2Fdracula/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mailsac%2Fdracula/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mailsac","download_url":"https://codeload.github.com/mailsac/dracula/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mailsac%2Fdracula/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28480064,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T11:59:17.896Z","status":"ssl_error","status_checked_at":"2026-01-16T11:55:55.838Z","response_time":107,"last_error":"SSL_read: 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":["metrics-server","session-store","throttling-server"],"created_at":"2026-01-16T16:41:07.909Z","updated_at":"2026-01-16T16:41:08.008Z","avatar_url":"https://github.com/mailsac.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# In-Memory Expirable Key Counter\n\nThis is a fast metrics server, ideal for tracking throttling. Put values to the server, and then count them. Values\nexpire according to the TTL seconds you set.\n\nThis repo provides both the client and server.\n\n## Why\n\nOne would think Redis is a natural choice for this type of service. When you need to count short-lived, namespaced\nkeys, Redis does not exactly meet this use case. There are solutions but significant application level code is\nrequired, or Redis needs to be wrapped in another service.\n\nFor example, one solution in Redis is to put everything at the top level like `namespace:key:entry-id` and\nrun `SCAN 0 MATCH namespace:key:*`. But it *returns all the keys*, which you can then count. It also requires that you\nimplement some sort of unique ID generation. Redis does support `KEEPTTL` during `SET` to each key.\n\nAnother approach in Redis is to store entries in a sorted set or other data structure, at a key like `namespace:key` and\nomit or expire entries in application logic. We also considered adding a service in front of Redis to handle that, but\nthat seemed about as much overhead as just writing exactly what we needed into this dracula project.\n\n#### Design Requirements\n\nAt the outset, we wanted to meet the following needs.\n\n- every key entry has the same expiry time\n- every key is in a namespace\n- support write-heavy operations\n- support tens of thousands of simultaneous reads and writes on low-end hardware\n- AWS graviton / ARM support\n- easy deploy\n\nWe were able to achieve these goals in a server that uses about 1.2MB of RAM on startup.\n\n## Usage\n\n### Download executable\n\nPre-built binaries of `dracula-server` and `dracula-cli` are [available in the Releases tab](https://github.com/mailsac/dracula/releases).\n\n### Run from docker\n\ndracula container images are published to:\n- https://github.com/mailsac/dracula/pkgs/container/dracula\n\nExample running the server:\n\n```bash\ndocker run -d --rm -p \"3509:3509/udp\" --name dracula-server-test \"ghcr.io/mailsac/dracula\" /app/dracula-server -v\n```\n\nThe dracula CLI is also included in the docker image. Dracula works from IP addresses, so to use docker you must\nfirst get the dracula server's container IP:\n\n```bash\nDOCKER_DRACULA_IP=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' dracula-server-test)\necho ${DOCKER_DRACULA_IP}\n```\n\nthen it is possible to use the docker `dracula-cli` and make a request to count some records:  \n```bash\ndocker run --rm \"ghcr.io/mailsac/dracula\" /app/dracula-cli -i ${DOCKER_DRACULA_IP} -put -k testing_key\ndocker run --rm \"ghcr.io/mailsac/dracula\" /app/dracula-cli -i ${DOCKER_DRACULA_IP} -count -k testing_key\n# 1\n```\n\n### Build from source\n\nAlternatively, build it (Golang 1.16+ recommended):\n\n```\nmake build-server\n```\n\n### First Run and Test\n\nOptionally set a pre-shared signing secret via environment variable:\n\n```\nexport DRACULA_SECRET=very-secure3;\n```\n\nthen run the server with default settings and verbose logging:\n\n```\n./dracula-server -v\n```\n\nAvailable server options:\n```\n./dracula-server -h\n```\n\n```text\nUsage of ./dracula-server:\n  -c 192.168.0.1:3509,192.168.0.2:3555\n        Enable cluster replication. Peers must be comma-separated ip:port like 192.168.0.1:3509,192.168.0.2:3555.\n  -h    Print this help\n  -i string\n        Self peer IP and host like 192.168.0.1:3509 to identify self in the cluster\n  -p int\n        UDP this server will run on (default 3509)\n  -prom string\n        Enable prometheus metrics. May cause pauses. Example: '0.0.0.0:9090'\n  -s string\n        Optional pre-shared auth secret if not using env var DRACULA_SECRET\n  -t int\n        TTL secs - entries will expire after this many seconds (default 60)\n  -tcp int\n        TCP port this server will run on (default 3509)\n  -v    Verbose logging\n  -version\n\n```\n\nThen use the cli for testing. Put keys to the server:\n\n```\nmake build-cli\n\n./dracula-cli -put -k asdf\n./dracula-cli -put -k asdf\n./dracula-cli -put -k asdfjkl\n./dracula-cli -put -k jkl\n\n./dracula-cli -count -k asdf\n# \u003e 2\n\n./dracula-cli -keys -k \"a*\"\n# \u003e 1) asdf\n# \u003e 2) asdfjkl\n\n./dracula-cli -keys -k \"a*\"\n# \u003e 1) asdf\n# \u003e 2) asdfjkl\n# \u003e 3) jkl\n```\n\nKeys can be namespaced with the `-n` flag.\n\n\nDracula server can be embedded in your Go application:\n\n```go\npackage main\n\nimport (\n\t\"github.com/mailsac/dracula/server\"\n)\n\nfunc main() {\n\tvar expireAfterSeconds int64 = 60\n\tpreSharedSecret := \"supersecret\"\n\ts := server.NewServer(expireAfterSeconds, preSharedSecret)\n\terr := s.Listen(3509, 3509)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\n```\n\nthen use the Go client to put values and count them:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/mailsac/dracula/client\"\n\t\"time\"\n)\n\nconst (\n\t// include just one server or comma-separated pool\n    serverIPPortPool = \"127.0.0.1:3509,192.168.0.1:3509\"\n\tnamespace  = \"default\"\n)\n\nvar (\n\tclientResponseTimeout = time.Second * 6\n)\n\nfunc main() {\n\tc := client.NewClient(client.Config{\n\t\tRemoteUDPIPPortList: serverIPPortPool, \n\t\tRemoteTCPIPPortList: serverIPPortPool, \n\t\tTimeout: clientResponseTimeout, \n\t\tPreSharedSecret: \"very-secure3\",\n    })\n\tc.Listen(9001)\n\n\t// seed some entries\n\tc.Put(namespace, \"192.168.0.50\")\n\tc.Put(namespace, \"192.168.0.50\")\n\tc.Put(namespace, \"mailsac.com\")\n\tc.Put(namespace, \"hello.msdc.co\")\n\n\ttotal, _ := c.Count(namespace, \"192.168.0.50\")\n\tfmt.Println(\"192.168.0.50\", total) // 2\n\n\t// wait a while\n\ttime.Sleep(time.Second * 60)\n\n\t// now entries will be expired\n\ttotal, _ = c.Count(namespace, \"mailsac.com\")\n\tfmt.Println(\"mailsac.com\", total) // 0\n}\n\n```\n\nEntries are grouped in a `namespace`.\n\nSee `server/server_test.go` for examples.\n\n## Prometheus metrics\n\nBasic garbage collection metrics are exposed when using the server flag `--prom=0.0.0.0:9090` flag (you can use a custom host and port).\n\n```text\n# HELP dracula_key_sum_in_gc_namespaces Count of key values in last garbed collected namespace valid keys\n# TYPE dracula_key_sum_in_gc_namespaces gauge\ndracula_key_sum_in_gc_namespaces 0\n# HELP dracula_keys_in_gc_namespaces Count of unexpired keys in last garbage collected namespaces\n# TYPE dracula_keys_in_gc_namespaces gauge\ndracula_keys_in_gc_namespaces 0\n# HELP dracula_max_namespaces_denom Denominator/portion of namespaces to be garbage collected each cleanup run\n# TYPE dracula_max_namespaces_denom gauge\ndracula_max_namespaces_denom 3\n# HELP dracula_namespaces_count Number of top level key namespaces\n# TYPE dracula_namespaces_count gauge\ndracula_namespaces_count 3\n# HELP dracula_namespaces_gc_count Number of namespaces which had keys garbage collected during last cleanup run\n# TYPE dracula_namespaces_gc_count gauge\ndracula_namespaces_gc_count 1\n```\n\n## High Availability / Failover\n\nRudimentary and experimental HA is possible via replication by using the `-p` peers list and `-i` self `IP:host` pair flags such as:\n```\ndracula-server -p \"127.0.0.1:3509,127.0.0.1:3519,127.0.0.1:3529\" -i 127.0.0.1:3529\n```\n\nwhere clients can connect to the pool and maintain a list of `-i` servers:\n```\ndracula-cli -i \"127.0.0.1:3509,127.0.0.1:3519,127.0.0.1:3529\" [...more flags]\n```\n\nAll peers in the cluster are listed, as well as the self IP and host in the cluster. These flags tell the dracula server to replicate all PUT messages to peers.\n\nIn practice, replication only meets the use case of short-lived, imperfectly consistent metrics.\n\nIf you require exact replication across peers, this feature will not be tolerant to network partitioning and will not meet your needs.\n\n## Limitations\n\nMessages are sent over UDP and not reliable. The trade-off desired is speed. This project was initially implemented to\nbe a throttling server, so missing a few messages wasn't a big deal. Also, UDP can cause TCP traffic on the same box to\nbe slower under heavy load. This was ideal for our use-case, but may not be for yours.\n\nA UDP message is limited to 1500 bytes. See `protocol/` for exactly how messages are parsed.\n\nThe namespace can be 64 bytes and the data value can be 1419 bytes.\n\nThe maximum entries in a key is the highest value of uint32.\n\nAuthentication is just strong enough to make sure you aren't sending messages to the wrong server. It is assumed dracula\nis running in a trusted environment.\n\n## Roadmap\n\n- Persistence and value storage\n- Better support for high availability under network partitions \n- Clients in other languages\n- Retries\n\nPlease open an Issue to request a feature.\n\n## License\n\nSee dependencies listed in go.mod for copyright notices and licenses.\n\n----\n\nCopyright (c) 2021-2023 Forking Software LLC\n\n| Project           | License SPDX Identifier |\n|-------------------|-------------------------|\n| Dracula CLI       | LGPL-3.0 |\n| Dracula Go Client | LGPL-3.0 |\n| Dracula Server    | GPL-2.0 |\n\nSee the files LICENSE.clients.txt and LICENSE.server.txt at the root of this repo.\n\nDual licensing is available. Contact Forking Software LLC.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmailsac%2Fdracula","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmailsac%2Fdracula","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmailsac%2Fdracula/lists"}