{"id":34288417,"url":"https://github.com/mdevilliers/redishappy","last_synced_at":"2026-03-11T09:32:32.285Z","repository":{"id":20249258,"uuid":"23521816","full_name":"mdevilliers/redishappy","owner":"mdevilliers","description":"Redis Sentinel high availabillity daemon","archived":false,"fork":false,"pushed_at":"2017-01-03T09:53:55.000Z","size":11428,"stargazers_count":114,"open_issues_count":4,"forks_count":22,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-12-20T13:26:12.646Z","etag":null,"topics":["deb","go","haproxy","haproxy-configuration","redis","redis-master-promotion","redis-sentinels","rpm"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mdevilliers.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}},"created_at":"2014-08-31T19:56:19.000Z","updated_at":"2023-07-25T15:58:14.000Z","dependencies_parsed_at":"2022-09-03T19:10:36.172Z","dependency_job_id":null,"html_url":"https://github.com/mdevilliers/redishappy","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mdevilliers/redishappy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdevilliers%2Fredishappy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdevilliers%2Fredishappy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdevilliers%2Fredishappy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdevilliers%2Fredishappy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mdevilliers","download_url":"https://codeload.github.com/mdevilliers/redishappy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdevilliers%2Fredishappy/sbom","scorecard":{"id":633027,"data":{"date":"2025-08-11","repo":{"name":"github.com/mdevilliers/redishappy","commit":"a22f26f3005039d5427aaf0c77208a13e9837dc4"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.6,"checks":[{"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":4,"reason":"Found 7/17 approved changesets -- score normalized to 4","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":"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":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"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":"Dangerous-Workflow","score":-1,"reason":"no workflows found","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":"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":"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":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"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":"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":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"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":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"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":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 26 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-21T08:25:01.681Z","repository_id":20249258,"created_at":"2025-08-21T08:25:01.681Z","updated_at":"2025-08-21T08:25:01.681Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30377278,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-11T06:09:32.197Z","status":"ssl_error","status_checked_at":"2026-03-11T06:09:17.086Z","response_time":84,"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":["deb","go","haproxy","haproxy-configuration","redis","redis-master-promotion","redis-sentinels","rpm"],"created_at":"2025-12-17T01:18:07.777Z","updated_at":"2026-03-11T09:32:32.269Z","avatar_url":"https://github.com/mdevilliers.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"### redishappy\n\n[![Build Status](https://travis-ci.org/mdevilliers/redishappy.svg?branch=master)](https://travis-ci.org/mdevilliers/redishappy)\n[![Coverage Status](https://coveralls.io/repos/mdevilliers/redishappy/badge.png)](https://coveralls.io/r/mdevilliers/redishappy)\n\nOne method of providing a highly available Redis service is to deploy using [Redis Sentinel](http://redis.io/topics/sentinel).\n\nRedis Sentinel monitors your Redis cluster and on detecting failure, promotes a slave to become the new master. RedisHappy provides a daemon to monitor for this promotion and to tell the outside world that this has happened.\n\nCurrently we support [HAProxy](http://www.haproxy.org/) with redishappy-haproxy and [Consul](https://www.consul.io/) with redishappy-consul.\n\nFeatures\n\n* Automatic discovery and healthchecking of Redis Sentinels.\n* Extensible to support various service discovery mechanisims\n* Developed in Golang, clean deployment with no additional dependencies.\n* Read-only RestAPI.\n* Syslog integration.\n* RPM and Deb packages available.\n* Support for Linux, Solaris and Windows\n* [Puppet module available](https://github.com/lesaux/puppet-redishappy).\n\n### Deployment\n\nRedishappy ships in two forms redishappy-haproxy and redishappy-consul.\n\n#### redishappy-haproxy\n\nredishappy-haproxy updates HAProxy's configuration file on Redis master promotion and then reloads the HAProxy configuration file. The reload maintains current connections.\n\n![redishappy_haproxy]( docs/redishappy-haproxy.png)\n\nThe redishappy daemon is installed on the same machine as HAProxy and runs with correct user rights to interact with HAProxy. Multiple instance of HAProxy/redishappy-haproxy can be deployed and operate seperatly.\n\n#### redishappy-consul\n\nredishappy-consul updates entries in a Consul instance on Redis master promotion.\n\n![redishappy_consul]( docs/redishappy-consul.png)\n\n\n### FAQ\n\nQ. Why - I thought in the modern age Redis clients should be Sentinel aware? They should connect to the correct Redis instance on failover.\n\nA. Some do, some don't. Some it seems to be an eternal 'work in progress'. Rather than fixing all of the clients we needed to work correctly with Sentinel, RedisHappy was built upon the fact that all of the clients I have tested are great at connecting to a single address.\n\nOperations teams also need to support legacy applications and libraries - adding redishappy, Sentinels and HAProxy can help provide a HA enviroment for Redis backed applications.\n\nQ. Why - This [article](http://blog.haproxy.com/2014/01/02/haproxy-advanced-redis-health-check/) suggests that HAProxy can healthcheck Redis instances quite fine by itself.\n\nA. Yes. It can do. But not reliably... I'll explain.\n\nSuppose we have this setup. R1 and R2 are redis instances, S1,S2,S3 are Sentinel instances, H1 and H2 are HAProxy instances.\n\n\u003cpre\u003e\n    R1,R2\n    S1, S2, S3\n    H1, H2\n\u003c/pre\u003e\n\n- Life is good - R1 and R2 are in a master slave configuration, H1 and H2 correctly identify R1 as the master\n\n\u003cpre\u003e\n    R1      R2\n    M  ---- S\n    ^\n    |\n    ---------\n    |       |\n    H1      H2\n\u003c/pre\u003e\n\n- Disaster! - R1 dies or is partitioned but don't fear R2 is now the \"master\". Day saved!\n\n\u003cpre\u003e\n    *       R2\n            M\n            ^\n            |\n    ---------\n    |       |\n    H1      H2\n\u003c/pre\u003e\n\n- Disaster! - R1 comes back online and announces itself as a \"master\". Both R1 and R2 are now accepting writes, as HAProxy's healthcheck identifies both as online.\n\n\u003cpre\u003e\n    R1        R2\n    M       M\n    ^        ^\n    |       |\n    ---------\n    |       |\n    H1      H2\n\u003c/pre\u003e\n\n- R1 is made the \"slave\" of R2. Everything is ok now, except for the writes that R1 accepted which are lost forever.\n\n\u003cpre\u003e\n    R1      R2\n    S ----- M\n            ^\n            |\n    ---------\n    |       |\n    H1      H2\n\u003c/pre\u003e\n\nWhen a Redis instance is started and stopped it initially announces itself as a \"master\". It will some time later be made a \"slave\" but in the meantime accept writes which will be lost when it is correctly made a slave.\n\nRedisHappy attempts to avoid this failure mode by only presenting the correct server to HAProxy or any other service once it is confirmed as a \"master\". We assume clients will either block or fail until the master is online again.\n\n### Building\n\nUsing Vagrant\n\nThe provided vagrant file creates a virtual machine with all of the dependancies to build redishappy, smoke test it, and build the deb and rpm packages.\n\n```sh\nvagrant up\n```\n\nThe packages are automatically built to - $GOPATH/src/github.com/mdevilliers/redishappy/\n\nThe vagrant box also installs HAProxy, Docker and https://github.com/mdevilliers/docker-rediscluster for manual testing.\n\nDownload and build.\n\nInstall golang 1.4 +\n\n```sh\ngo get github.com/mdevilliers/redishappy\n\ncd $GOPATH/src/github.com/mdevilliers/redishappy\n\ngo get github.com/tools/godep\ngo get github.com/axw/gocov/gocov\ngo get github.com/mattn/goveralls\ngo get golang.org/x/tools/cmd/cover\ngo get golang.org/x/tools/cmd/goimports\n\ngodep restore\n\nbuild/ci.sh\n```\n\n### Defaults\n\nInstalling using the deb and rpm packages will set the following defaults -\n\n* Installs to `/usr/bin/redis-haproxy`\n* Configuration to `/etc/redishappy-haproxy`\n* Logs to file in `/var/log/redishappy-haproxy`\n* Warnings, Errors go to syslog\n\n### Configuration\n\nExample configurations can be found in the main folders\n\n* [HAProxy](main/redis-haproxy)\n* [Consul](main/redis-consul)\n\nDefinitions for the elements\n\n```js\n{\n  // OPTIONAL - TCP Keep-Alive time (in seconds)\n  \"SentinelTCPKeepAlive\" : 0\n  // REQUIRED - needs to contain at least one logical cluster\n  \"Clusters\" :[\n  {\n    \"Name\" : \"testing\", // logical name of Redis cluster\n    \"ExternalPort\" : 6379 // port to expose for the cluster via HAProxy\n  }],\n  // REQUIRED - needs to contain the details of at least one cluster\n  // redishappy will discover additional sentinels as they come online\n  \"Sentinels\" : [\n      {\"Host\" : \"172.17.42.1\", \"Port\" : 26377}\n  ],\n  // OPTIONAL for running redishappy-haproxy\n  \"HAProxy\" :\n    {\n      // REQUIRED - absolute path to the template file\n      \"TemplatePath\": \"/var/redishappy/haproxy_template.cfg\",\n      // REQUIRED - absolute path to HAProxy's config file\n      \"OutputPath\": \"/etc/haproxy/haproxy.cfg\",\n      // REQUIRED - command to run to reload the config file on changes\n      \"ReloadCommand\": \"haproxy -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)\"\n    },\n    // OPTIONAL for running redishappy-consul\n   \"Consul\" : {\n       // REQUIRED - path to Consul instance\n    \"Address\" : \"127.0.0.1:8500\",\n    // REQUIRED - for each cluster in the main config there should be a defined service\n      \"Services\" : [\n        {\n            // REQUIRED - should match a name of a Cluster in the main config\n            \"Cluster\" : \"testing\",\n            // REQUIRED - logical name for the node\n            \"Node\" : \"redis-1\",\n            // REQUIRED - logical name for the data centre\n            \"Datacenter\": \"dc1\",\n            // REQUIRED - tags for the service\n            \"tags\" : [ \"redis\", \"master\", \"anothertag\"]\n          }\n      ]\n  }\n}\n\n```\n\nOr you can configure with the following environmental variables -\n\nEnvironment Variable               | Example          | Notes\n-----------------------------------|------------------|-----------------------------------\n`REDISHAPPY_CLUSTERS`              | clustername:6379 | multiple values can be ; seperated\n`REDISHAPPY_SENTINELS`             | ip_name:26377    | multiple values can be ; seperated\n`REDISHAPPY_HAPROXY_TEMPLATE_PATH` |                  | string, see config file for example\n`REDISHAPPY_HAPROXY_OUTPUT_PATH`   |                  | string, see config file for example\n`REDISHAPPY_HAPROXY_RELOAD_CMD`    |                  | string, see config file for example\n\n\n\n### API\n\nRedisHappy provides a readonly API on port 8000. You can change the port by specifying a `PORT` environment variable. It is also possible to use a `BIND` [environment variable](https://github.com/zenazn/goji/blob/master/bind/bind.go#L59) if you wish to bind to other interfaces etc.\n\n* `GET /api/ping` - will reply \"pong\" if running\n* `GET /api/configuration` - displays the start up configuration\n* `GET /api/sentinels` - displays the sentinels being currently monitored and their current states\n* `GET /api/topology` - displays the current view of the Redis clusters, their master and their host/ip addresses\n\nredishappy-haproxy provides the following additional read only apis\n\n* `GET /api/template` - displays the current template file\n* `GET /api/haproxy` - displays the rendered HAProxy file\n\n### Hacking\n\nRunning the following script will gofmt, govet, run the tests, build all of the executables.\n\n```sh\nbuild/ci.sh\n```\n\n### Benchmarks\n\nFind some benchmarking information [here](docs/redishappy-benchmark.md).\n\n### Testing with Docker\n\nhttps://github.com/mdevilliers/docker-rediscluster\n\nWill start up a master/slave, 3 sentinel redis cluster for testing.\n\nThanks\n------\n\nBig thanks to\n\n- [Pierig Le Saux](https://github.com/lesaux) for providing the RPM packaging and Puppet expertise\n- [Gary Hawkins](https://github.com/aeriandi-garyh) for providing the deb packaging expertise\n- [Dominic Scheirlinck](https://github.com/dominics) for fixing up the consul deb package\n- [Charles Phillips](https://github.com/doublerebel) for fixing compilation on Solaris\n- [Samuel Vijaykumar M](https://github.com/samof76) for contributing his benchmarking findings\n\n\nCopyright and license\n---------------------\n\nCode and documentation copyright 2017 Mark deVilliers. Code released under the Apache 2.0 license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdevilliers%2Fredishappy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmdevilliers%2Fredishappy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdevilliers%2Fredishappy/lists"}