{"id":30939176,"url":"https://github.com/questdb/questdb-ha-reads","last_synced_at":"2025-09-10T19:09:39.276Z","repository":{"id":312062583,"uuid":"1046128843","full_name":"questdb/questdb-ha-reads","owner":"questdb","description":"example for HA reads using QuestDB","archived":false,"fork":false,"pushed_at":"2025-09-01T10:53:33.000Z","size":1328,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-01T12:45:09.629Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","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/questdb.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-28T08:24:30.000Z","updated_at":"2025-09-01T10:53:36.000Z","dependencies_parsed_at":"2025-09-01T12:45:13.804Z","dependency_job_id":null,"html_url":"https://github.com/questdb/questdb-ha-reads","commit_stats":null,"previous_names":["javier/questdb-ha-reads","questdb/questdb-ha-reads"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/questdb/questdb-ha-reads","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/questdb%2Fquestdb-ha-reads","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/questdb%2Fquestdb-ha-reads/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/questdb%2Fquestdb-ha-reads/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/questdb%2Fquestdb-ha-reads/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/questdb","download_url":"https://codeload.github.com/questdb/questdb-ha-reads/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/questdb%2Fquestdb-ha-reads/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274509940,"owners_count":25299071,"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","status":"online","status_checked_at":"2025-09-10T02:00:12.551Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2025-09-10T19:09:30.173Z","updated_at":"2025-09-10T19:09:39.252Z","avatar_url":"https://github.com/questdb.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# QuestDB Highly Available Reads\n\nThis is an example repository companion to the blog post [Highly Available Reads with QuestDB](https://questdb.com/blog/highly-available-reads-with-questdb),\nshowing how you can get HA reads when using multiple QuestDB instances.\n\nThe goal of this example is to demonstrate how you can point your client application to multiple QuestDB replicated\ninstances, and get automatic failover of any query to the next available instance, so your queries are always served\nfrom an instance in your cluster even in\nthe event of a server going down (accidentally or for scheduled maintenance).\n\n![Highly Available Reads in Action](./ha_reads_demo.gif)\n\nReplication is out of the box when using QuestDB Enterprise. Implementing replication by adding a custom layer on top\nof QuestDB Open Source is discouraged, as consistency and data recovery is not guaranteed.\n\n## Starting the demo environment\n\nWe recommend using a QuestDB cluster with at least one read-replica, so you can verify the client applications failover\non consistent data.\n\nIf you don't have access to QuestDB Enterprise, or if you prefer to test in a local environment, for the purpose of this\ndemo you can start three independent, not replicated, QuestDB instances on a single host using Docker. Note this will\nnot replicate any data, as instances are unaware of each other.\n\n### Start several QuestDB containers using Docker\n\n```sh\ndocker run --name primary -d --rm -e QDB_CAIRO_WAL_TEMP_PENDING_RENAME_TABLE_PREFIX=temp_primary  -p 9000:9000  -p 8812:8812 questdb/questdb\n\ndocker run --name replica1 -d --rm -e QDB_CAIRO_WAL_TEMP_PENDING_RENAME_TABLE_PREFIX=temp_replica1  -p 9001:9000  -p 8813:8812 questdb/questdb\n\ndocker run --name replica2 -d --rm -e QDB_CAIRO_WAL_TEMP_PENDING_RENAME_TABLE_PREFIX=temp_replica2  -p 9002:9000  -p 8814:8812 questdb/questdb\n```\n\nSo you have now three instances running, each listening on different HTTP port (9000, 9001, and 9002) and postgresql port (8812, 8813, 8814).\nI passed an environment variable to set a different temporary path on each instance; this way, I can easily identify which instance\nI am connecting to when examining the parameters. On Enterprise, we can just check the `replication.role` parameter.\n\n```sql\nselect value from (show parameters) where property_path IN ( 'replication.role', 'cairo.wal.temp.pending.rename.table.prefix') limit 1;\n```\n\nIn the case of running QuestDB Enterprise, the output for the command will be either `primary` or `replica`. In the case\nof running the local docker setup, the output will be `temp_primary`, `temp_replica1`, and `temp_replica2`.\n\n### Running the demo\n\n\nNow that the three containers are running, if you start any of the demos provided they should all show the output. Note\nthat of you are running QuestDB Enterprise, and you don't want to change the config variable to\n\nreplication.role\n\n```\ntemp_primary\ntemp_primary\ntemp_primary\n```\n\nYou can then stop the primary on a separate terminal with:\n\n```\ndocker stop primary\n```\n\nAnd you should see, after the app notices the connection was failing, that the value has changed to:\n\n```\ntemp_replica1\ntemp_replica1\ntemp_replica1\n```\n\nYou can keep stopping and restarting containers to see how the demo behaves. When all containers are down, the demo\nwill just keep retrying until a connection succeeds.\n\nAll the demos will send the query 250 times at 300ms intervals and then will exit.\n\n\n## Python\n\nThe demo uses psycopg3. It is easy to adapt to psycopg2, as the overall structure would be the same and psycopg2 also\nsupports multiple hosts on the connection string.\n\n```sh\npip install -r requirements.txt\npython ha_reads.py\n```\n\n\n## Java\n\nThe demo is packed as a maven project. You can either execute from your IDE, or use the `mvn` command line:\n\n```sh\nmvn compile exec:java -Dexec.mainClass=HAReads\n```\n\n## Node.js\n\nNode.js supports libpq-style multi-host connection strings only when using `pg-native`. The default `pg` client (pure JS)\ndoes not support it. If you want cross-platform compatibility or don’t want to rely on native bindings, you need to\nimplement the failover logic manually.\n\nWhat we are doing here is storing all the hosts in an array, and test each one in a sequence until successful. The rest of\n the process is identical to what we are doing in the other languages.\n\n```sh\nnpm install\nnpm start\n```\n\n## Dot Net (C#)\n\n```sh\ndotnet restore\ndotnet run\n```\n\n## Go\n\n```sh\ngo mod tidy\ngo run .\n```\n\n## Rust\n\n```sh\ncargo run\n```\n\n## C++\n\nThe build process differs depending on your platform: **Linux**, **macOS**, or **Windows**.\n\n### Linux\n\nUse `apt` (or your distro’s equivalent) to install dependencies:\n\n```sh\nsudo apt update\nsudo apt install -y g++ cmake pkg-config libpqxx-dev\n```\n\nNow build with:\n\n```sh\nmkdir -p build \u0026\u0026 cd build\n\ncmake ..\nmake\n```\n\nExecute the demo with:\n\n```sh\n./HAReads\n```\n\nYou can clean up with:\n\n```sh\ncd build\nmake clean\n```\n\n### macOS\n\nUse Homebrew to install `libpqxx`. This also pulls in the required `libpq`:\n\n```sh\nbrew install libpqxx\n```\n\nNow build with:\n```sh\ngit clone https://github.com/YOUR_REPO/questdb-ha-reads\ncd questdb-ha-reads/cpp\nmkdir -p build \u0026\u0026 cd build\n\ncmake .. \\\n  -DCMAKE_PREFIX_PATH=/opt/homebrew/opt/libpqxx \\\n  -DCMAKE_LIBRARY_PATH=/opt/homebrew/lib \\\n  -DCMAKE_INCLUDE_PATH=/opt/homebrew/include\n\nmake\n```\n\nExecute the demo with:\n\n```sh\n./HAReads\n```\n\nYou can clean up with:\n\n```sh\ncd build\nmake clean\n```\n\n### Windows\n\nYou can use WSL for compatibility or MSYS2\n\n#### WSL\n\nInstall WSL + Ubuntu:\n\n```sh\nwsl --install\n```\n\nInside WSL terminal, you can now follow the same process we described above for Linux\n\n#### MSYS2\n\nOpen MSYS2 MinGW 64-bit shell and run:\n\n```sh\npacman -Syuu\npacman -S --needed mingw-w64-x86_64-toolchain cmake git pkgconf mingw-w64-x86_64-libpqxx\n```\n\nNow build with:\n```sh\nmkdir -p build \u0026\u0026 cd build\n\ncmake -G \"MinGW Makefiles\" ..\nmingw32-make\n```\n\nExecute the demo with:\n\n```sh\n./HAReads.exe\n```\n\nYou can clean up with:\n\n```sh\ncd build\nmingw32-make clean\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquestdb%2Fquestdb-ha-reads","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fquestdb%2Fquestdb-ha-reads","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquestdb%2Fquestdb-ha-reads/lists"}