[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"sanity-oO476BOcozBcOgO0NVcMrbR74os9lw5Zcx-F2aShvPY":3,"sanity-wJ-4iIUVTxJAXwsgbS1sH3wPNV9mwwunlMsdVGuTxRU":1196},{"data":4,"sourceMap":-1},{"latestPodcast":5,"latestReleases":14,"post":39,"recent":1171},[6],{"_id":7,"publishedAt":8,"slug":9,"sponsored":12,"title":13},"d81860b7-7b72-4ba5-8ad5-3b77fd9a8e9b","2026-07-14T07:40:00.000Z",{"_type":10,"current":11},"slug","your-ai-is-only-as-responsible-as-you-are",null,"Your AI is only as responsible as you are",[15,21,27,33],{"_id":16,"publishedAt":17,"slug":18,"title":20},"eb5b66eb-9410-4329-83bb-22bbff39402a","2026-04-28T13:00:00.000Z",{"_type":10,"current":19},"turn-scattered-knowledge-into-trusted-intelligence","Turning scattered knowledge into trusted intelligence: Stack Internal 2026.3",{"_id":22,"publishedAt":23,"slug":24,"title":26},"369c2401-b62e-4a37-8ff8-bf603023ecad","2026-03-02T15:03:00.988Z",{"_type":10,"current":25},"what-s-new-at-stack-overflow-march-2026","What’s new at Stack Overflow: March 2026",{"_id":28,"publishedAt":29,"slug":30,"title":32},"5e9053a4-07ea-447c-91ea-29e0b6228537","2026-02-02T15:00:00.000Z",{"_type":10,"current":31},"what-s-new-at-stack-overflow-february-2026","What’s new at Stack Overflow: February 2026",{"_id":34,"publishedAt":35,"slug":36,"title":38},"a1b538eb-a8a6-46d0-80a1-ac70ec9bb935","2026-01-05T10:00:00.000-05:00",{"_type":10,"current":37},"what-s-new-at-stack-overflow-january-2026","What’s new at Stack Overflow: January 2026",{"_createdAt":40,"_id":41,"_rev":42,"_type":43,"_updatedAt":44,"author":45,"body":62,"comments":1133,"dateUrl":1134,"excerpt":435,"image":1135,"legacyBody":1138,"product":12,"publishedAt":1141,"slug":1142,"sponsored":12,"tags":1144,"title":1170,"visible":1133},"2023-05-25T09:39:17Z","wp-post-16760","07ZbrKPSUrjrV4wQ6fIpan","blogPost","2023-07-13T14:55:46Z",[46],{"_createdAt":47,"_id":48,"_rev":49,"_type":50,"_updatedAt":51,"avatar":52,"bio":57,"employee":58,"name":59,"slug":60},"2023-05-23T16:27:18Z","wp-author-cap-16761","07ZbrKPSUrjrV4wQ6fDpaa","blogAuthor","2023-06-20T15:05:10Z",{"_type":53,"asset":54},"image",{"_ref":55,"_type":56},"image-a95faea5bc622473b0febc815c3c61bed5f4611d-199x199-jpg","reference","Michael is a back-end data engineer and technical writer based in Accra, Ghana. His tech stack includes\nbut not limited to Kafka, Hadoop, Storm, Python, Cassandra, Postgres, Elastic Search,\nRedis and Zookeeper.","none","Michael Aboagye",{"current":61},"michael-aboagye",[63,74,82,90,98,106,137,145,153,161,171,179,187,195,203,213,221,229,237,245,253,261,269,277,285,293,305,317,325,333,341,349,357,365,373,382,390,398,406,414,422,431,436,455,498,506,544,552,560,568,572,596,608,624,632,636,644,679,698,706,718,730,742,758,766,782,790,805,813,829,837,845,849,873,881,900,916,919,943,951,959,963,979,987,995,1003,1011,1019,1027,1035,1043,1051,1085,1093,1101,1109,1117,1125],{"_key":64,"_type":65,"children":66,"markDefs":72,"style":73},"a16c10da076e","block",[67],{"_key":68,"_type":69,"marks":70,"text":71},"a16c10da076e0","span",[],"We tend to rely on caching solutions to improve database performance. Caching frequently-accessed queries in memory or via a database can optimize write\u002Fread performance and reduce network latency, especially for heavy-workload applications, such as gaming services and Q&A portals. But you can further improve performance by pooling users' connections to a database.",[],"normal",{"_key":75,"_type":65,"children":76,"markDefs":81,"style":73},"f5af23c48637",[77],{"_key":78,"_type":69,"marks":79,"text":80},"f5af23c486370",[],"Client users need to create a connection to a web service before they can perform CRUD operations. Most web services are backed by relational database servers such as Postgres or MySQL. With PostgreSQL, each new connection can take up to 1.3MB in memory. In a production environment where we expect to receive thousands or millions of concurrent connections to the backend service, this can quickly exceed your memory resources (or if you have a scalable cloud, it can get very expensive very quickly).",[],{"_key":83,"_type":65,"children":84,"markDefs":89,"style":73},"922d16075f85",[85],{"_key":86,"_type":69,"marks":87,"text":88},"922d16075f850",[],"Because each time a client attempts to access a backend service, it requires OS resources to create, maintain, and close connections to the datastore. This creates a large amount of overhead causing database performance to deteriorate.",[],{"_key":91,"_type":65,"children":92,"markDefs":97,"style":73},"8e925979b584",[93],{"_key":94,"_type":69,"marks":95,"text":96},"8e925979b5840",[],"Consumers of your service expect fast response times. If that performance deteriorates, it can lead to poor user experiences, revenue losses, and even unscheduled downtime. If you expose your backend service as an API, repeated slowdowns and failures could cause cascading problems and lose you customers.",[],{"_key":99,"_type":65,"children":100,"markDefs":105,"style":73},"243b1d0d7d7b",[101],{"_key":102,"_type":69,"marks":103,"text":104},"243b1d0d7d7b0",[],"Instead of opening and closing connections for every request, connection pooling uses a cache of database connections that can be reused when future requests to the database are required. It lets your database scale effectively as the data stored there and the number of clients accessing it grow. Traffic is never constant, so pooling can better manage traffic peaks without causing outages. Your production database shouldn’t be your bottleneck.",[],{"_key":107,"_type":65,"children":108,"markDefs":131,"style":73},"a850da409d0b",[109,113,118,122,127],{"_key":110,"_type":69,"marks":111,"text":112},"a850da409d0b0",[],"In this article, we will explore how we can use connection pooling middleware like ",{"_key":114,"_type":69,"marks":115,"text":117},"a850da409d0b1",[116],"5809c533a5a1","pgpool",{"_key":119,"_type":69,"marks":120,"text":121},"a850da409d0b2",[]," and ",{"_key":123,"_type":69,"marks":124,"text":126},"a850da409d0b3",[125],"b644db90c07f","pgbouncer",{"_key":128,"_type":69,"marks":129,"text":130},"a850da409d0b4",[]," to reduce overhead and network latency. For illustration purposes, I will use pgpool-II and pgbouncer to explain concepts of connection pooling and compare which one is more effective in pooling connections because some connection poolers can even affect database performance.",[132,135],{"_key":116,"_type":133,"href":134,"reference":12},"link","https:\u002F\u002Fwww.pgpool.net\u002Fmediawiki\u002Findex.php\u002FMain_Page",{"_key":125,"_type":133,"href":136,"reference":12},"https:\u002F\u002Fwww.pgbouncer.org\u002F",{"_key":138,"_type":65,"children":139,"markDefs":144,"style":73},"4e92cb08a563",[140],{"_key":141,"_type":69,"marks":142,"text":143},"4e92cb08a5630",[],"We will look at how to use pgbench to benchmark Postgres databases since it is the standard tool provided by PostgreSQL.",[],{"_key":146,"_type":65,"children":147,"markDefs":152,"style":73},"4736a1db20a0",[148],{"_key":149,"_type":69,"marks":150,"text":151},"4736a1db20a00",[],"Different hardware provides different benchmarking results based on the plan you set. For the tests below, I’m using these specifications.",[],{"_key":154,"_type":65,"children":155,"markDefs":160,"style":73},"dcb2a765bf98",[156],{"_key":157,"_type":69,"marks":158,"text":159},"dcb2a765bf980",[],"Specs of my test machine:",[],{"_key":162,"_type":65,"children":163,"level":168,"listItem":169,"markDefs":170,"style":73},"ac6cc26be6b8",[164],{"_key":165,"_type":69,"marks":166,"text":167},"ac6cc26be6b80",[],"Linode Server: Ubuntu 16 - 64 bit ( Virtual Machine)",1,"bullet",[],{"_key":172,"_type":65,"children":173,"level":168,"listItem":169,"markDefs":178,"style":73},"2ce728304da3",[174],{"_key":175,"_type":69,"marks":176,"text":177},"2ce728304da30",[],"Postgres version 9.5",[],{"_key":180,"_type":65,"children":181,"level":168,"listItem":169,"markDefs":186,"style":73},"bf8ba9832de3",[182],{"_key":183,"_type":69,"marks":184,"text":185},"bf8ba9832de30",[],"Memory: 2GBDatabase size: 800MB",[],{"_key":188,"_type":65,"children":189,"level":168,"listItem":169,"markDefs":194,"style":73},"2f52824e0bbe",[190],{"_key":191,"_type":69,"marks":192,"text":193},"2f52824e0bbe0",[],"Storage: 2GB",[],{"_key":196,"_type":65,"children":197,"markDefs":202,"style":73},"9a997710e0d5",[198],{"_key":199,"_type":69,"marks":200,"text":201},"9a997710e0d50",[],"Also it is important to isolate the Postgres database server from other frameworks like logstash shipper and other servers for collecting performance metrics because most of these components consume more memory and will affect the test results.",[],{"_key":204,"_type":65,"children":205,"markDefs":211,"style":212},"1ccf57b4cf5c",[206],{"_key":207,"_type":69,"marks":208,"text":210},"1ccf57b4cf5c0",[209],"strong","Creating a pooled connection",[],"h2",{"_key":214,"_type":65,"children":215,"markDefs":220,"style":73},"cbdf6898e105",[216],{"_key":217,"_type":69,"marks":218,"text":219},"cbdf6898e1050",[],"Connecting to a backend service is an expensive operation, as it consists of the following steps:",[],{"_key":222,"_type":65,"children":223,"level":168,"listItem":169,"markDefs":228,"style":73},"ada4e4cfb6da",[224],{"_key":225,"_type":69,"marks":226,"text":227},"ada4e4cfb6da0",[],"Open a connection to the database using the database driver.",[],{"_key":230,"_type":65,"children":231,"level":168,"listItem":169,"markDefs":236,"style":73},"63b5c05a17d2",[232],{"_key":233,"_type":69,"marks":234,"text":235},"63b5c05a17d20",[],"Open a TCP socket for CRUD operations",[],{"_key":238,"_type":65,"children":239,"level":168,"listItem":169,"markDefs":244,"style":73},"7d5377507c21",[240],{"_key":241,"_type":69,"marks":242,"text":243},"7d5377507c210",[],"Perform CRUD operations over the socket.",[],{"_key":246,"_type":65,"children":247,"level":168,"listItem":169,"markDefs":252,"style":73},"abaf22d1af16",[248],{"_key":249,"_type":69,"marks":250,"text":251},"abaf22d1af160",[],"Close the connection.",[],{"_key":254,"_type":65,"children":255,"level":168,"listItem":169,"markDefs":260,"style":73},"bb4ca968dff1",[256],{"_key":257,"_type":69,"marks":258,"text":259},"bb4ca968dff10",[],"Close the socket.",[],{"_key":262,"_type":65,"children":263,"markDefs":268,"style":73},"3cf136b8e9f1",[264],{"_key":265,"_type":69,"marks":266,"text":267},"3cf136b8e9f10",[],"In a production environment where we expect thousands of concurrent open and close connections from clients, doing the above steps for every single connection can cause the database to perform poorly.",[],{"_key":270,"_type":65,"children":271,"markDefs":276,"style":73},"47c47ae5ceef",[272],{"_key":273,"_type":69,"marks":274,"text":275},"47c47ae5ceef0",[],"We can resolve this problem by pooling connections from clients. Instead of creating a new connection with every request, connection poolers reuse some existing connections. Thus there is no need to perform multiple expensive full database trips by opening and closing connections to backend service. It prevents the overhead of creating a new connection to the database every time there is a request for a database connection with the same properties (i.e name, database, protocol version).",[],{"_key":278,"_type":65,"children":279,"markDefs":284,"style":73},"c391fa5f7620",[280],{"_key":281,"_type":69,"marks":282,"text":283},"c391fa5f76200",[],"Pooling middleware like pgbouncer comes with a pool manager. Usually, the connection pool manager maintains a pool of open database connections. You can not pool connections without a pool manager.",[],{"_key":286,"_type":65,"children":287,"markDefs":292,"style":73},"6468cd27a95e",[288],{"_key":289,"_type":69,"marks":290,"text":291},"6468cd27a95e0",[],"A pool contains two types of connections:",[],{"_key":294,"_type":65,"children":295,"level":168,"listItem":169,"markDefs":304,"style":73},"c8a6b3dbb24d",[296,300],{"_key":297,"_type":69,"marks":298,"text":299},"c8a6b3dbb24d0",[209],"Active connection",{"_key":301,"_type":69,"marks":302,"text":303},"c8a6b3dbb24d1",[],": In use by the application.",[],{"_key":306,"_type":65,"children":307,"level":168,"listItem":169,"markDefs":316,"style":73},"52552cbbce90",[308,312],{"_key":309,"_type":69,"marks":310,"text":311},"52552cbbce900",[209],"Idle connection:",{"_key":313,"_type":69,"marks":314,"text":315},"52552cbbce901",[]," Available for use by the application.",[],{"_key":318,"_type":65,"children":319,"markDefs":324,"style":73},"f6761933de58",[320],{"_key":321,"_type":69,"marks":322,"text":323},"f6761933de580",[],"When a new request to access data from the backend service comes in, the pool manager checks if the pool contains any unused connection and returns one if available. If all the connections in the pool are active, then a new connection is created and added to the pool by the pool manager. When the pool reaches its maximum size, all new connections are queued until a connection in the pool becomes available.",[],{"_key":326,"_type":65,"children":327,"markDefs":332,"style":73},"4be35bf0f0da",[328],{"_key":329,"_type":69,"marks":330,"text":331},"4be35bf0f0da0",[],"Although most databases do not have an in-built connection pooling system, there are middleware solutions that we can use to pool connections from clients.",[],{"_key":334,"_type":65,"children":335,"markDefs":340,"style":73},"6db3e1ca7bdb",[336],{"_key":337,"_type":69,"marks":338,"text":339},"6db3e1ca7bdb0",[],"For a PostgreSQL database server, both pgbouncer and pgpool-II can serve as a pooling interface between a web service and a Postgres database. Both utilities use the same logic to pool connections from clients.",[],{"_key":342,"_type":65,"children":343,"markDefs":348,"style":73},"911b572c3609",[344],{"_key":345,"_type":69,"marks":346,"text":347},"911b572c36090",[],"pgpool-II offers more features beyond connection pooling, such as replication, load balancing, and parallel query features.",[],{"_key":350,"_type":65,"children":351,"markDefs":356,"style":73},"0b3fd873b368",[352],{"_key":353,"_type":69,"marks":354,"text":355},"0b3fd873b3680",[],"How do you add connection pooling? Is it as simple as installing the utilities?",[],{"_key":358,"_type":65,"children":359,"markDefs":364,"style":212},"e4f87b2d3f15",[360],{"_key":361,"_type":69,"marks":362,"text":363},"e4f87b2d3f150",[209],"Two ways to integrate a connection pooler",[],{"_key":366,"_type":65,"children":367,"markDefs":372,"style":73},"533ea2e78b62",[368],{"_key":369,"_type":69,"marks":370,"text":371},"533ea2e78b620",[],"There are two ways of implementing connection pooling for PostgreSQL application:",[],{"_key":374,"_type":65,"children":375,"level":168,"listItem":380,"markDefs":381,"style":73},"8543bf5e6426",[376],{"_key":377,"_type":69,"marks":378,"text":379},"8543bf5e64260",[209],"As an external service or middleware such as pgbouncer","number",[],{"_key":383,"_type":65,"children":384,"markDefs":389,"style":73},"3e95cfa98f58",[385],{"_key":386,"_type":69,"marks":387,"text":388},"3e95cfa98f580",[],"Connection poolers such as pgbouncer and pgpool-II can be used to pool connections from clients to a PostgreSQL database. The connection pooler sits in between the application and the database server. Pgbouncer or pgpool-II can be configured in a way to relay requests from the application to the database server.",[],{"_key":391,"_type":65,"children":392,"level":168,"listItem":380,"markDefs":397,"style":73},"def2136c20d3",[393],{"_key":394,"_type":69,"marks":395,"text":396},"def2136c20d30",[209],"Client-side libraries such as c3p0",[],{"_key":399,"_type":65,"children":400,"markDefs":405,"style":73},"f36711e2da4c",[401],{"_key":402,"_type":69,"marks":403,"text":404},"f36711e2da4c0",[],"There exist libraries such as c3p0 which extend database driver functionality to include connection pooling support.",[],{"_key":407,"_type":65,"children":408,"markDefs":413,"style":73},"aab21ec624bb",[409],{"_key":410,"_type":69,"marks":411,"text":412},"aab21ec624bb0",[],"However, the best way to implement connection pooling for applications is to make use of an external service or middleware since it is easier to set up and manage. In addition external middleware like pgpool2 provides other features such as load balancing apart from pooling connections.",[],{"_key":415,"_type":65,"children":416,"markDefs":421,"style":73},"16291d919f8a",[417],{"_key":418,"_type":69,"marks":419,"text":420},"16291d919f8a0",[],"Now let’s take a deeper look at what happens when a backend service connects to a Postgres database, both with and without pooling.",[],{"_key":423,"_type":65,"children":424,"markDefs":429,"style":430},"90335c71fce6",[425],{"_key":426,"_type":69,"marks":427,"text":428},"90335c71fce60",[209],"Scaling database performance without connection pooling",[],"h3",{"_key":432,"_type":53,"alt":12,"asset":433,"caption":435,"markDefs":12},"c0be11224e08",{"_ref":434,"_type":56},"image-9371a4ac21a80c2965e78b1d19a130e5b9946205-630x281-png","",{"_key":437,"_type":65,"children":438,"markDefs":452,"style":73},"848d06a7967e",[439,443,448],{"_key":440,"_type":69,"marks":441,"text":442},"848d06a7967e0",[],"We do not need a connection pooler to connect to a backend service. We can connect to a Postgres database directly. To examine how long it takes to execute concurrent connections to a database without a connection pooler, we will use ",{"_key":444,"_type":69,"marks":445,"text":447},"848d06a7967e1",[446],"9f66e0f1586c","pgbench",{"_key":449,"_type":69,"marks":450,"text":451},"848d06a7967e2",[]," to benchmark connections to the Postgres database.",[453],{"_key":446,"_type":133,"href":454,"reference":12},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002F10\u002Fpgbench.html",{"_key":456,"_type":65,"children":457,"markDefs":495,"style":73},"c64e43d04de8",[458,462,467,471,475,479,483,487,491],{"_key":459,"_type":69,"marks":460,"text":461},"c64e43d04de80",[],"Pgbench is based on ",{"_key":463,"_type":69,"marks":464,"text":466},"c64e43d04de81",[465],"6dd2f06ed947","TPC-B",{"_key":468,"_type":69,"marks":469,"text":470},"c64e43d04de82",[],". TPC-B measures throughput in terms of how many transactions per second a system can perform. Pgbench executes five ",{"_key":472,"_type":69,"marks":473,"text":474},"c64e43d04de83",[209],"SELECT",{"_key":476,"_type":69,"marks":477,"text":478},"c64e43d04de84",[],", ",{"_key":480,"_type":69,"marks":481,"text":482},"c64e43d04de85",[209],"INSERT",{"_key":484,"_type":69,"marks":485,"text":486},"c64e43d04de86",[],", and ",{"_key":488,"_type":69,"marks":489,"text":490},"c64e43d04de87",[209],"UPDATE",{"_key":492,"_type":69,"marks":493,"text":494},"c64e43d04de88",[]," commands per transaction.",[496],{"_key":465,"_type":133,"href":497,"reference":12},"http:\u002F\u002Fwww.tpc.org\u002Ftpcb\u002F",{"_key":499,"_type":65,"children":500,"markDefs":505,"style":73},"1c38a59764d4",[501],{"_key":502,"_type":69,"marks":503,"text":504},"1c38a59764d40",[],"Based on TPC-B-like transactions, pgbench runs the same sequence of SQL commands repeatedly in multiple concurrent database sessions and calculates the average transaction rate.",[],{"_key":507,"_type":65,"children":508,"markDefs":543,"style":73},"babe8c1b5ee6",[509,513,518,521,525,528,532,535,539],{"_key":510,"_type":69,"marks":511,"text":512},"babe8c1b5ee60",[],"Before we run pgbench, we need to initialize it with the following command to create the ",{"_key":514,"_type":69,"marks":515,"text":517},"babe8c1b5ee61",[209,516],"code","pgbench_history",{"_key":519,"_type":69,"marks":520,"text":478},"babe8c1b5ee62",[],{"_key":522,"_type":69,"marks":523,"text":524},"babe8c1b5ee63",[209,516],"pgbench_branches",{"_key":526,"_type":69,"marks":527,"text":478},"babe8c1b5ee64",[],{"_key":529,"_type":69,"marks":530,"text":531},"babe8c1b5ee65",[209,516],"pgbench_tellers",{"_key":533,"_type":69,"marks":534,"text":486},"babe8c1b5ee66",[],{"_key":536,"_type":69,"marks":537,"text":538},"babe8c1b5ee67",[209,516],"pgbench_accounts",{"_key":540,"_type":69,"marks":541,"text":542},"babe8c1b5ee68",[]," tables. Pgbench uses the following tables to run transactions for benchmarking.",[],{"_key":545,"_type":65,"children":546,"markDefs":551,"style":73},"6e543ffd6fb3",[547],{"_key":548,"_type":69,"marks":549,"text":550},"6e543ffd6fb30",[209,516],"pgbench -i -s 50 database_name",[],{"_key":553,"_type":65,"children":554,"markDefs":559,"style":73},"d3a4e042ad04",[555],{"_key":556,"_type":69,"marks":557,"text":558},"d3a4e042ad040",[],"Afterward, I executed the command below to test the database with 150 clients",[],{"_key":561,"_type":65,"children":562,"markDefs":567,"style":73},"9c7116f6b5c8",[563],{"_key":564,"_type":69,"marks":565,"text":566},"9c7116f6b5c80",[209,516],"pgbench -c 10 -j 2 -t 10000 database_name",[],{"_key":569,"_type":53,"alt":12,"asset":570,"caption":435,"markDefs":12},"e8cea169ea63",{"_ref":571,"_type":56},"image-73311bde6b2c9d562b69ec86f009ddc3ae4b4e7f-497x199-png",{"_key":573,"_type":65,"children":574,"markDefs":595,"style":73},"32bbf102214d",[575,579,583,587,591],{"_key":576,"_type":69,"marks":577,"text":578},"32bbf102214d0",[],"As you see, in our initial baseline test, I instructed pgbench to execute with ",{"_key":580,"_type":69,"marks":581,"text":582},"32bbf102214d1",[209],"ten",{"_key":584,"_type":69,"marks":585,"text":586},"32bbf102214d2",[]," different client sessions. Each client session will execute",{"_key":588,"_type":69,"marks":589,"text":590},"32bbf102214d3",[209]," 10,000",{"_key":592,"_type":69,"marks":593,"text":594},"32bbf102214d4",[]," transactions.",[],{"_key":597,"_type":65,"children":598,"markDefs":607,"style":73},"50ad0b46ded2",[599,603],{"_key":600,"_type":69,"marks":601,"text":602},"50ad0b46ded20",[],"From these results, it seems our initial baseline test is ",{"_key":604,"_type":69,"marks":605,"text":606},"50ad0b46ded21",[209],"486 transactions per second.",[],{"_key":609,"_type":65,"children":610,"markDefs":623,"style":73},"9fb49693f7eb",[611,615,619],{"_key":612,"_type":69,"marks":613,"text":614},"9fb49693f7eb0",[],"Let’s see how we can make use of connection poolers like pgbouncer and pgpool to increase transaction throughput and avoid a ",{"_key":616,"_type":69,"marks":617,"text":618},"9fb49693f7eb1",[209],"‘Sorry!, too many clients already’",{"_key":620,"_type":69,"marks":621,"text":622},"9fb49693f7eb2",[]," error.",[],{"_key":625,"_type":65,"children":626,"markDefs":631,"style":430},"3ecbf38a48d6",[627],{"_key":628,"_type":69,"marks":629,"text":630},"3ecbf38a48d60",[209],"Scaling database performance with pgbouncer",[],{"_key":633,"_type":53,"alt":12,"asset":634,"caption":435,"markDefs":12},"b1e15d754c46",{"_ref":635,"_type":56},"image-1e38b5e64b338f68a3b84094f24f1e3a2c14ab6e-739x251-png",{"_key":637,"_type":65,"children":638,"markDefs":643,"style":73},"6d38dfd4861f",[639],{"_key":640,"_type":69,"marks":641,"text":642},"6d38dfd4861f0",[],"Let’s look at how we can use pgbouncer to increase transaction throughput.",[],{"_key":645,"_type":65,"children":646,"markDefs":676,"style":73},"afb8014e2a17",[647,651,656,660,664,668,672],{"_key":648,"_type":69,"marks":649,"text":650},"afb8014e2a170",[],"Pgbouncer can be installed on almost all Linux distributions. You can check ",{"_key":652,"_type":69,"marks":653,"text":655},"afb8014e2a171",[654],"5634b92fd40a","here",{"_key":657,"_type":69,"marks":658,"text":659},"afb8014e2a172",[]," how to set up pgbouncer. Alternatively, you can install pgbouncer using package managers like ",{"_key":661,"_type":69,"marks":662,"text":663},"afb8014e2a173",[209,516],"apt-get",{"_key":665,"_type":69,"marks":666,"text":667},"afb8014e2a174",[]," or ",{"_key":669,"_type":69,"marks":670,"text":671},"afb8014e2a175",[209,516],"yum",{"_key":673,"_type":69,"marks":674,"text":675},"afb8014e2a176",[],".",[677],{"_key":654,"_type":133,"href":678,"reference":12},"https:\u002F\u002Fwww.pgbouncer.org\u002Finstall.html",{"_key":680,"_type":65,"children":681,"markDefs":695,"style":73},"ed6b32a8ad94",[682,686,691],{"_key":683,"_type":69,"marks":684,"text":685},"ed6b32a8ad940",[],"If you find it difficult to authenticate clients with pgbouncer, you can check ",{"_key":687,"_type":69,"marks":688,"text":690},"ed6b32a8ad941",[689],"0fb7b46c0af3","GitHub",{"_key":692,"_type":69,"marks":693,"text":694},"ed6b32a8ad942",[]," on how to do so.",[696],{"_key":689,"_type":133,"href":697,"reference":12},"https:\u002F\u002Fgithub.com\u002FRev0kz\u002Fauthenticate-pgbouncer",{"_key":699,"_type":65,"children":700,"markDefs":705,"style":73},"4c26e7bcd011",[701],{"_key":702,"_type":69,"marks":703,"text":704},"4c26e7bcd0110",[],"Pgbouncer comes with three types of pooling:",[],{"_key":707,"_type":65,"children":708,"level":168,"listItem":380,"markDefs":717,"style":73},"b91b35b39484",[709,713],{"_key":710,"_type":69,"marks":711,"text":712},"b91b35b394840",[209],"Session pooling",{"_key":714,"_type":69,"marks":715,"text":716},"b91b35b394841",[],": One of the connections in the pool is assigned to a client until the timeout is reached.",[],{"_key":719,"_type":65,"children":720,"level":168,"listItem":380,"markDefs":729,"style":73},"d4a388d1e991",[721,725],{"_key":722,"_type":69,"marks":723,"text":724},"d4a388d1e9910",[209],"Transaction pooling",{"_key":726,"_type":69,"marks":727,"text":728},"d4a388d1e9911",[],": Similar to session polling, it gets a connection from the pool. It keeps it until the transaction is done. If the same client wants to run another transaction, it has to wait until it gets another transaction assigned to it.",[],{"_key":731,"_type":65,"children":732,"level":168,"listItem":380,"markDefs":741,"style":73},"aef454151368",[733,737],{"_key":734,"_type":69,"marks":735,"text":736},"aef4541513680",[209],"Statement pooling",{"_key":738,"_type":69,"marks":739,"text":740},"aef4541513681",[],": Connection is returned to the pool as soon as the first query is completed.",[],{"_key":743,"_type":65,"children":744,"markDefs":757,"style":73},"7b09bb10025c",[745,749,753],{"_key":746,"_type":69,"marks":747,"text":748},"7b09bb10025c0",[],"We will make use of the transaction pooling mode. Inside the ",{"_key":750,"_type":69,"marks":751,"text":752},"7b09bb10025c1",[516],"pgbouncer.ini",{"_key":754,"_type":69,"marks":755,"text":756},"7b09bb10025c2",[]," file, I modified the following parameter:",[],{"_key":759,"_type":65,"children":760,"markDefs":765,"style":73},"3e669a64d4c4",[761],{"_key":762,"_type":69,"marks":763,"text":764},"3e669a64d4c40",[516],"max_client_conn = 100",[],{"_key":767,"_type":65,"children":768,"markDefs":781,"style":73},"ef444a93d2fe",[769,773,777],{"_key":770,"_type":69,"marks":771,"text":772},"ef444a93d2fe0",[],"The ",{"_key":774,"_type":69,"marks":775,"text":776},"ef444a93d2fe1",[209,516],"max_client_conn",{"_key":778,"_type":69,"marks":779,"text":780},"ef444a93d2fe2",[]," parameter defines how many client connections to pgbouncer (instead of Postgres) are allowed.",[],{"_key":783,"_type":65,"children":784,"markDefs":789,"style":73},"dddcb4e921e1",[785],{"_key":786,"_type":69,"marks":787,"text":788},"dddcb4e921e10",[516],"default_pool_size = 25",[],{"_key":791,"_type":65,"children":792,"markDefs":804,"style":73},"027a0b5a40e8",[793,796,800],{"_key":794,"_type":69,"marks":795,"text":772},"027a0b5a40e80",[],{"_key":797,"_type":69,"marks":798,"text":799},"027a0b5a40e81",[209,516],"default_pool_size",{"_key":801,"_type":69,"marks":802,"text":803},"027a0b5a40e82",[]," parameter defines how many server connections to allow per user\u002Fdatabase pair.",[],{"_key":806,"_type":65,"children":807,"markDefs":812,"style":73},"e196b8e97162",[808],{"_key":809,"_type":69,"marks":810,"text":811},"e196b8e971620",[516],"reserve_pool_size = 5",[],{"_key":814,"_type":65,"children":815,"markDefs":828,"style":73},"642f50f39764",[816,820,824],{"_key":817,"_type":69,"marks":818,"text":819},"642f50f397640",[],"The",{"_key":821,"_type":69,"marks":822,"text":823},"642f50f397641",[209,516],"reserve_pool_size",{"_key":825,"_type":69,"marks":826,"text":827},"642f50f397642",[]," parameter defines how many additional connections are allowed to the pool.",[],{"_key":830,"_type":65,"children":831,"markDefs":836,"style":73},"472dbd5ebcf9",[832],{"_key":833,"_type":69,"marks":834,"text":835},"472dbd5ebcf90",[],"As in the previous test I executed pgbench with ten different client sessions. Each client executes 1000 transactions as shown below.",[],{"_key":838,"_type":65,"children":839,"markDefs":844,"style":73},"220ccf2ad808",[840],{"_key":841,"_type":69,"marks":842,"text":843},"220ccf2ad8080",[209,516],"pgbench -c 10 -p -j 2 -t 1000 database_name",[],{"_key":846,"_type":53,"alt":12,"asset":847,"caption":435,"markDefs":12},"edb02bd5bf79",{"_ref":848,"_type":56},"image-dbd3398ace9456b244c10f9ad0401bf4ae1ec5a8-521x201-png",{"_key":850,"_type":65,"children":851,"markDefs":872,"style":73},"ebd25d98a0d8",[852,856,860,864,868],{"_key":853,"_type":69,"marks":854,"text":855},"ebd25d98a0d80",[],"As you see, transaction throughput increased from ",{"_key":857,"_type":69,"marks":858,"text":859},"ebd25d98a0d81",[209],"486 transactions per second",{"_key":861,"_type":69,"marks":862,"text":863},"ebd25d98a0d82",[]," to ",{"_key":865,"_type":69,"marks":866,"text":867},"ebd25d98a0d83",[209],"566 transactions per second. ",{"_key":869,"_type":69,"marks":870,"text":871},"ebd25d98a0d84",[],"With the help of pgbouncer, transaction throughput improved by approximately 60%.",[],{"_key":874,"_type":65,"children":875,"markDefs":880,"style":73},"d397b4ba4a29",[876],{"_key":877,"_type":69,"marks":878,"text":879},"d397b4ba4a290",[],"Now let’s see how we can increase transaction throughput with pgpool-II since it comes with connection pooling features.",[],{"_key":882,"_type":65,"children":883,"markDefs":897,"style":73},"373775330737",[884,888,893],{"_key":885,"_type":69,"marks":886,"text":887},"3737753307370",[],"Unlike pgbouncer, pgpool-II offers features beyond connection pooling. The ",{"_key":889,"_type":69,"marks":890,"text":892},"3737753307371",[891],"3e79cfa4afa6","documentation ",{"_key":894,"_type":69,"marks":895,"text":896},"3737753307372",[],"provides detailed information about pgpool-II features and how to set it up from source or via a package manager",[898],{"_key":891,"_type":133,"href":899,"reference":12},"https:\u002F\u002Fwww.pgpool.net\u002Fdocs\u002Fpgpool-II-3.2.1\u002Ftutorial-en.html",{"_key":901,"_type":65,"children":902,"markDefs":915,"style":73},"3ee3dcea2f38",[903,907,911],{"_key":904,"_type":69,"marks":905,"text":906},"3ee3dcea2f380",[],"I changed the following parameters in the ",{"_key":908,"_type":69,"marks":909,"text":910},"3ee3dcea2f381",[209,516],"pgpool.conf",{"_key":912,"_type":69,"marks":913,"text":914},"3ee3dcea2f382",[]," file to make it route clients connections from pgpool2 to Postgres database server.",[],{"_key":917,"_type":516,"code":918,"markDefs":12},"4e08777f29ca","connection_cache  = on  \nlisten_addresses  = ‘postgres_database_name’’  \nport  = 5432 ",{"_key":920,"_type":65,"children":921,"markDefs":942,"style":73},"8251d1915958",[922,926,930,934,938],{"_key":923,"_type":69,"marks":924,"text":925},"8251d19159580",[],"Setting the ",{"_key":927,"_type":69,"marks":928,"text":929},"8251d19159581",[209,516],"connection_cache",{"_key":931,"_type":69,"marks":932,"text":933},"8251d19159582",[],"parameter to ",{"_key":935,"_type":69,"marks":936,"text":937},"8251d19159583",[209,516],"on",{"_key":939,"_type":69,"marks":940,"text":941},"8251d19159584",[]," activates pgpool2 pooling capability.",[],{"_key":944,"_type":65,"children":945,"markDefs":950,"style":73},"40ee82d7a297",[946],{"_key":947,"_type":69,"marks":948,"text":949},"40ee82d7a2970",[],"Like the previous test, pgbench executed ten different client sessions. Each client executes 1000 transactions to the Postgres database server. Thus we expect a total of 10,000 transactions from all clients.",[],{"_key":952,"_type":65,"children":953,"markDefs":958,"style":73},"7ac90046643c",[954],{"_key":955,"_type":69,"marks":956,"text":957},"7ac90046643c0",[516],"gbench -p 9999 -c 10 -C -t 1000 postgres_database",[],{"_key":960,"_type":53,"alt":12,"asset":961,"caption":435,"markDefs":12},"506989c1c32c",{"_ref":962,"_type":56},"image-6b7b260022cebfed8b67cc959d91ddbb8ff36228-494x197-png",{"_key":964,"_type":65,"children":965,"markDefs":978,"style":73},"0483592696f9",[966,970,974],{"_key":967,"_type":69,"marks":968,"text":969},"0483592696f90",[],"In the same way we increased transaction throughput with pgbouncer, it seems pgpool2 also increased transaction throughput by ",{"_key":971,"_type":69,"marks":972,"text":973},"0483592696f91",[209],"75%",{"_key":975,"_type":69,"marks":976,"text":977},"0483592696f92",[]," as compared to the initial test.",[],{"_key":980,"_type":65,"children":981,"markDefs":986,"style":73},"f309d58cddb8",[982],{"_key":983,"_type":69,"marks":984,"text":985},"f309d58cddb80",[],"Pgbouncer implements connection pooling ‘out of the box’ without the need to fine-tune parameters while pgpool2 allows you to fine-tune parameters to enhance connection pooling.",[],{"_key":988,"_type":65,"children":989,"markDefs":994,"style":212},"a18c14cbfba8",[990],{"_key":991,"_type":69,"marks":992,"text":993},"a18c14cbfba80",[209],"Choosing a connection pooler: pgpool-II or pgbouncer?",[],{"_key":996,"_type":65,"children":997,"markDefs":1002,"style":73},"d2433695fe0c",[998],{"_key":999,"_type":69,"marks":1000,"text":1001},"d2433695fe0c0",[],"There are several factors to consider when choosing a connection pooler to use. Although pgbouncer and pgpool-II are great solutions for connection pooling, each tool has its strengths and weaknesses.",[],{"_key":1004,"_type":65,"children":1005,"markDefs":1010,"style":430},"e920747f6bc0",[1006],{"_key":1007,"_type":69,"marks":1008,"text":1009},"e920747f6bc00",[],"Memory\u002Fresource consumption",[],{"_key":1012,"_type":65,"children":1013,"markDefs":1018,"style":73},"ce845e2dd7d0",[1014],{"_key":1015,"_type":69,"marks":1016,"text":1017},"ce845e2dd7d00",[],"If you are interested in a lightweight connection pooler for your backend service, then pgbouncer is the right tool for you. Unlike pgpool-II, which by default allows 32 child processes to be forked, pgbouncer uses only one process. Thus pgbouncer consumes less memory than pgpool2.",[],{"_key":1020,"_type":65,"children":1021,"markDefs":1026,"style":430},"599552297c7b",[1022],{"_key":1023,"_type":69,"marks":1024,"text":1025},"599552297c7b0",[],"Streaming Replication",[],{"_key":1028,"_type":65,"children":1029,"markDefs":1034,"style":73},"0aee7276491c",[1030],{"_key":1031,"_type":69,"marks":1032,"text":1033},"0aee7276491c0",[],"Apart from pooling connections, you can also manage your Postgres cluster with streaming replication using pgpool-II. Streaming replication copies data from a primary node to a secondary node. Pgpool-II supports Postgres streaming replication, while pgbouncer does not. It is the best way to achieve high availability and prevent data loss.",[],{"_key":1036,"_type":65,"children":1037,"markDefs":1042,"style":430},"d13b6801b8de",[1038],{"_key":1039,"_type":69,"marks":1040,"text":1041},"d13b6801b8de0",[],"Centralized password management",[],{"_key":1044,"_type":65,"children":1045,"markDefs":1050,"style":73},"cc37f1802f07",[1046],{"_key":1047,"_type":69,"marks":1048,"text":1049},"cc37f1802f070",[],"In a production environment where you expect many clients\u002Fapplications to connect to the database through a connection pooler concurrently, it is necessary to use a centralized password management system to manage clients' credentials.",[],{"_key":1052,"_type":65,"children":1053,"markDefs":1082,"style":73},"4ad7be814313",[1054,1058,1063,1067,1071,1075,1078],{"_key":1055,"_type":69,"marks":1056,"text":1057},"4ad7be8143130",[],"You can make use of ",{"_key":1059,"_type":69,"marks":1060,"text":1062},"4ad7be8143131",[1061],"9f14cf502db7","auth_query",{"_key":1064,"_type":69,"marks":1065,"text":1066},"4ad7be8143132",[]," in pgbouncer to load clients’ credentials from the database instead of storing clients’ credentials in a ",{"_key":1068,"_type":69,"marks":1069,"text":1070},"4ad7be8143133",[516],"userlist.txt",{"_key":1072,"_type":69,"marks":1073,"text":1074},"4ad7be8143134",[]," file and comparing credentials from the connection string against the ",{"_key":1076,"_type":69,"marks":1077,"text":1070},"4ad7be8143135",[516],{"_key":1079,"_type":69,"marks":1080,"text":1081},"4ad7be8143136",[]," file.",[1083],{"_key":1061,"_type":133,"href":1084,"reference":12},"https:\u002F\u002Fwww.pgbouncer.org\u002Fconfig.html#authentication-file-format",{"_key":1086,"_type":65,"children":1087,"markDefs":1092,"style":430},"d4b151e12c64",[1088],{"_key":1089,"_type":69,"marks":1090,"text":1091},"d4b151e12c640",[],"Load balancing and high availability",[],{"_key":1094,"_type":65,"children":1095,"markDefs":1100,"style":73},"5dda2708c170",[1096],{"_key":1097,"_type":69,"marks":1098,"text":1099},"5dda2708c1700",[],"Finally, if you want to add load balancing and high availability to your pooled connections, then pgpool2 is the right tool to use. pgpool2 supports Postgres high availability through the in-built watchdog processes. This pgpool2 sub-process monitors the health of pgpool2 nodes participating in the watchdog cluster as well as coordinating between multiple pgpool2 nodes.",[],{"_key":1102,"_type":65,"children":1103,"markDefs":1108,"style":212},"6bdf544f5e19",[1104],{"_key":1105,"_type":69,"marks":1106,"text":1107},"6bdf544f5e190",[209],"Conclusion",[],{"_key":1110,"_type":65,"children":1111,"markDefs":1116,"style":73},"317f1ce42655",[1112],{"_key":1113,"_type":69,"marks":1114,"text":1115},"317f1ce426550",[],"Database performance can be improved beyond connection pooling. Replication, load balancing, and in-memory caching can contribute to efficient database performance.",[],{"_key":1118,"_type":65,"children":1119,"markDefs":1124,"style":73},"4cc3488d7209",[1120],{"_key":1121,"_type":69,"marks":1122,"text":1123},"4cc3488d72090",[],"If a web service is designed to make a lot of read and write queries to a database, then you have multiple instances of a Postgres database in place to take care of write queries from clients through a load balancer such as pgpool-II while in-memory caching can be used to optimize read queries.",[],{"_key":1126,"_type":65,"children":1127,"markDefs":1132,"style":73},"60dde29458ca",[1128],{"_key":1129,"_type":69,"marks":1130,"text":1131},"60dde29458ca0",[],"Despite the pgpool-II ability to function as a loader balancer and connection pooler, pgbouncer is the preferred middleware solution for connection pooling because it is easy to set up, not too difficult to manage, and primarily serves as a connection pooler without any other functions.",[],true,"2020\u002F10\u002F14",{"_type":53,"asset":1136},{"_ref":1137,"_type":56},"image-33806edc728d76f7c6e8e52ab90c9f8e7dee728b-2560x987-jpg",{"code":1139,"language":1140},"\u003C!-- wp:paragraph -->\n\u003Cp>We tend to rely on caching solutions to improve database performance. Caching frequently-accessed queries in memory or via a database can optimize write\u002Fread performance and reduce network latency, especially for heavy-workload applications, such as gaming services and Q&amp;A portals. But you can further improve performance by pooling users' connections to a database.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Client users need to create a connection to a web service before they can perform CRUD operations. Most web services are backed by relational database servers such as Postgres or MySQL. With PostgreSQL, each new connection can take up to 1.3MB in memory. In a production environment where we expect to receive thousands or millions of concurrent connections to the backend service, this can quickly exceed your memory resources (or if you have a scalable cloud, it can get very expensive very quickly).\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Because each time a client attempts to access a backend service, it requires OS resources to create, maintain, and close connections to the datastore. This creates a large amount of overhead causing database performance to deteriorate.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Consumers of your service expect fast response times. If that performance deteriorates, it can lead to poor user experiences, revenue losses, and even unscheduled downtime. If you expose your backend service as an API, repeated slowdowns and failures could cause cascading problems and lose you customers.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Instead of opening and closing connections for every request, connection pooling uses a cache of database connections that can be reused when future requests to the database are required. It lets your database scale effectively as the data stored there and the number of clients accessing it grow. Traffic is never constant, so pooling can better manage traffic peaks without causing outages. Your production database shouldn’t be your bottleneck.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>In this article, we will explore how we can use connection pooling middleware like \u003Ca href=\"https:\u002F\u002Fwww.pgpool.net\u002Fmediawiki\u002Findex.php\u002FMain_Page\">pgpool\u003C\u002Fa> and \u003Ca href=\"https:\u002F\u002Fwww.pgbouncer.org\u002F\">pgbouncer\u003C\u002Fa> to reduce overhead and network latency. For illustration purposes, I will use pgpool-II and pgbouncer to explain concepts of connection pooling and compare which one is more effective in pooling connections because some connection poolers can even affect database performance.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>We will look at how to use pgbench to benchmark Postgres databases since it is the standard tool provided by PostgreSQL.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Different hardware provides different benchmarking results based on the plan you set. For the&nbsp; tests below, I’m using these specifications.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Specs of my test machine:\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:list -->\n\u003Cul>\u003Cli>Linode Server: Ubuntu 16 - 64 bit ( Virtual Machine)&nbsp;&nbsp;\u003C\u002Fli>\u003Cli>Postgres version 9.5\u003C\u002Fli>\u003Cli>Memory: 2GBDatabase size: 800MB\u003C\u002Fli>\u003Cli>Storage: 2GB\u003C\u002Fli>\u003C\u002Ful>\n\u003C!-- \u002Fwp:list -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Also it is important to isolate the Postgres database server from other frameworks like logstash shipper and other servers for collecting performance metrics because most of these components consume more memory and will affect the test results.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading -->\n\u003Ch2 id=\"h-creating-a-pooled-connection\">\u003Cstrong>Creating a pooled connection\u003C\u002Fstrong>&nbsp;&nbsp;\u003C\u002Fh2>\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Connecting to a backend service is an expensive operation, as it consists of the following steps:\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:list -->\n\u003Cul>\u003Cli>Open a connection to the database using the database driver.\u003C\u002Fli>\u003Cli>Open a TCP socket for CRUD operations\u003C\u002Fli>\u003Cli>&nbsp;Perform CRUD operations over the socket.&nbsp;&nbsp;\u003C\u002Fli>\u003Cli>Close the connection.\u003C\u002Fli>\u003Cli>Close the socket.&nbsp;&nbsp;\u003C\u002Fli>\u003C\u002Ful>\n\u003C!-- \u002Fwp:list -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>In a production environment where we expect thousands of concurrent open and close connections from clients, doing the above steps for every single connection can cause the database to perform poorly.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>We can resolve this problem by pooling connections from clients. Instead of creating a new connection with every request, connection poolers reuse some existing connections. Thus there is no need to perform multiple expensive full database trips by opening and closing connections to backend service. It prevents the overhead of creating a new connection to the database every time there is a request for a database connection with the same properties (i.e name, database, protocol version).\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Pooling middleware like pgbouncer comes with a pool manager. Usually, the connection pool manager maintains a pool of open database connections. You can not pool connections without a pool manager.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>&nbsp;A pool contains two types of connections:\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:list -->\n\u003Cul>\u003Cli>\u003Cstrong>Active connection\u003C\u002Fstrong>: In use by the application.\u003C\u002Fli>\u003Cli>\u003Cstrong>Idle connection:\u003C\u002Fstrong>&nbsp; Available for use by the application.\u003C\u002Fli>\u003C\u002Ful>\n\u003C!-- \u002Fwp:list -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>When a new request to access data from the backend service comes in, the pool manager checks if the pool contains any unused connection and returns one if available. If all the connections in the pool are active, then a new connection is created and added to the pool by the pool manager. When the pool reaches its maximum size, all new connections are queued until a connection in the pool becomes available.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Although most databases do not have an in-built connection pooling system, there are middleware solutions that we can use to pool connections from clients.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>For a PostgreSQL database server, both pgbouncer and pgpool-II can serve as a pooling interface between a web service and a Postgres database. Both utilities use the same logic to pool connections from clients.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>pgpool-II offers more features beyond connection pooling, such as replication, load balancing, and parallel query features.&nbsp;&nbsp;&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>How do you add connection pooling? Is it as simple as installing the utilities?&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading -->\n\u003Ch2 id=\"h-two-ways-to-integrate-a-connection-pooler\">\u003Cstrong>Two ways to integrate a connection pooler&nbsp;\u003C\u002Fstrong>\u003C\u002Fh2>\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>&nbsp;There are two ways of implementing connection pooling for PostgreSQL application:\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:list {\"ordered\":true} -->\n\u003Col>\u003Cli>&nbsp;\u003Cstrong>As an external service or middleware such as pgbouncer\u003C\u002Fstrong>\u003C\u002Fli>\u003C\u002Fol>\n\u003C!-- \u002Fwp:list -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Connection poolers such as pgbouncer and pgpool-II can be used to pool connections from clients to a PostgreSQL database. The connection pooler sits in between the application and the database server. Pgbouncer or pgpool-II can be configured in a way to relay requests from the application to the database server.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:list {\"ordered\":true,\"start\":2} -->\n\u003Col start=\"2\">\u003Cli>\u003Cstrong>Client-side libraries such as c3p0&nbsp;\u003C\u002Fstrong>\u003C\u002Fli>\u003C\u002Fol>\n\u003C!-- \u002Fwp:list -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>There exist libraries such as c3p0 which extend database driver functionality to include connection pooling support.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>However, the best way to implement connection pooling for applications is to make use of an external service or middleware since it is easier to set up and manage. In addition external middleware like pgpool2 provides other features such as load balancing apart from pooling connections.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Now let’s take a deeper look at what happens when a backend service connects to a Postgres database, both with and without pooling.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading {\"level\":3} -->\n\u003Ch3 id=\"h-scaling-database-performance-without-connection-pooling\">\u003Cstrong>Scaling database performance without connection pooling\u003C\u002Fstrong>\u003C\u002Fh3>\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:image -->\n\u003Cfigure class=\"wp-block-image\">\u003Cimg src=\"https:\u002F\u002Flh4.googleusercontent.com\u002FQJ9_lP-BVHZA-tXo_TXe7RxrRycg94VNwPGhPaHiYYXeZIoAHo5SIAsdfOFJM2zWstM5RFM0mt6bVvwFtn9ZgF16N3C8edS1-jShcA5fP-NDauXX5hS8BvHFwl0x43rlrJi_u-vG\" alt=\"\"\u002F>\u003C\u002Ffigure>\n\u003C!-- \u002Fwp:image -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>We do not need a connection pooler to connect to a backend service. We can connect to a Postgres database directly. To examine how long it takes to execute concurrent connections to a database without a connection pooler, we will use \u003Ca href=\"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002F10\u002Fpgbench.html\">pgbench\u003C\u002Fa> to benchmark connections to the Postgres database.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Pgbench is based on \u003Ca href=\"http:\u002F\u002Fwww.tpc.org\u002Ftpcb\u002F\">TPC-B\u003C\u002Fa>. TPC-B measures throughput in terms of how many transactions per second a system can perform. Pgbench executes five \u003Cstrong>SELECT\u003C\u002Fstrong>, \u003Cstrong>INSERT\u003C\u002Fstrong>, and \u003Cstrong>UPDATE\u003C\u002Fstrong> commands per transaction.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Based on TPC-B-like transactions, pgbench runs the same sequence of SQL commands repeatedly in multiple concurrent database sessions and calculates the average transaction rate.&nbsp;&nbsp;&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Before we run pgbench, we need to initialize it with the following command to create the \u003Cstrong>\u003Ccode>pgbench_history\u003C\u002Fcode>\u003C\u002Fstrong>, \u003Cstrong>\u003Ccode>pgbench_branches\u003C\u002Fcode>\u003C\u002Fstrong>, \u003Cstrong>\u003Ccode>pgbench_tellers\u003C\u002Fcode>\u003C\u002Fstrong>, and \u003Cstrong>\u003Ccode>pgbench_accounts\u003C\u002Fcode>\u003C\u002Fstrong> tables. Pgbench uses the following tables to run transactions for benchmarking.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>\u003Cstrong>\u003Ccode>pgbench&nbsp; -i&nbsp; -s 50&nbsp; database_name\u003C\u002Fcode>\u003C\u002Fstrong>\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Afterward, I executed the command below to test the database with 150 clients&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>\u003Cstrong>\u003Ccode>pgbench&nbsp; -c 10&nbsp; -j 2&nbsp; -t&nbsp; 10000&nbsp; database_name\u003C\u002Fcode>\u003C\u002Fstrong>&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:image -->\n\u003Cfigure class=\"wp-block-image\">\u003Cimg src=\"https:\u002F\u002Flh4.googleusercontent.com\u002FYycflt0Xx6cDxCfQr4cpx5JymXXbw4orgoWhZRT-vCOc9_aGW-OAKH26L49KbyjCLnqHILGGBAYgwj5m_1yUEwW9yJdlEw_7RuuwYbpVhEOGGqAsaqSa6kBelubDWGsf5Li66He-\" alt=\"\"\u002F>\u003C\u002Ffigure>\n\u003C!-- \u002Fwp:image -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>As you see, in our initial baseline test, I instructed pgbench to execute with \u003Cstrong>ten\u003C\u002Fstrong> different client sessions. Each client session will execute\u003Cstrong> 10,000\u003C\u002Fstrong> transactions.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>From these results, it seems our initial baseline test is \u003Cstrong>486 transactions per second.\u003C\u002Fstrong>\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Let’s see how we can make use of connection poolers like pgbouncer and pgpool to increase transaction throughput and avoid a \u003Cstrong>‘Sorry!, too many clients already’\u003C\u002Fstrong> error.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading {\"level\":3} -->\n\u003Ch3 id=\"h-scaling-database-performance-with-pgbouncer\">\u003Cstrong>Scaling database performance with pgbouncer\u003C\u002Fstrong>\u003C\u002Fh3>\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:image -->\n\u003Cfigure class=\"wp-block-image\">\u003Cimg src=\"https:\u002F\u002Flh6.googleusercontent.com\u002F76TzyagPYklx3AVmjV6REg4cNgHiD9Wv0NElHEQJH0igOl4ReF2lS3dh83V6xbAZDU_L5LHyKPTYYtQjESXWxoDt_CdWKxBwcyDnX7DAhaeD-eb65gEgtj5HULwYfsbResQZUrZA\" alt=\"\"\u002F>\u003C\u002Ffigure>\n\u003C!-- \u002Fwp:image -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Let’s look at how we can use pgbouncer to increase transaction throughput.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Pgbouncer can be installed on almost all Linux distributions. You can check \u003Ca href=\"https:\u002F\u002Fwww.pgbouncer.org\u002Finstall.html\">here\u003C\u002Fa> how to set up pgbouncer. Alternatively, you can install pgbouncer using package managers like \u003Cstrong>\u003Ccode>apt-get\u003C\u002Fcode>\u003C\u002Fstrong> or \u003Cstrong>\u003Ccode>yum\u003C\u002Fcode>\u003C\u002Fstrong>.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>If you find it difficult to authenticate clients with pgbouncer, you can check \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FRev0kz\u002Fauthenticate-pgbouncer\">GitHub\u003C\u002Fa> on how to do so.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Pgbouncer comes with three types of pooling:&nbsp;&nbsp;&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:list {\"ordered\":true} -->\n\u003Col>\u003Cli>\u003Cstrong>Session pooling\u003C\u002Fstrong>: One of the connections in the pool is assigned to a client until the timeout is reached.&nbsp;&nbsp;\u003C\u002Fli>\u003Cli>\u003Cstrong>Transaction pooling\u003C\u002Fstrong>: Similar to session polling, it gets a connection from the pool. It keeps it until the transaction is done. If the same client wants to run another transaction, it has to wait until it gets another transaction assigned to it.&nbsp;\u003C\u002Fli>\u003Cli>\u003Cstrong>Statement pooling\u003C\u002Fstrong>: Connection is returned to the pool as soon as the first query is completed.\u003C\u002Fli>\u003C\u002Fol>\n\u003C!-- \u002Fwp:list -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>We will make use of the transaction pooling mode. Inside the \u003Ccode>pgbouncer.ini\u003C\u002Fcode> file, I modified the following parameter:&nbsp;&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>\u003Ccode>max_client_conn = 100 \u003C\u002Fcode>\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>The \u003Cstrong>\u003Ccode>max_client_conn\u003C\u002Fcode>\u003C\u002Fstrong> parameter defines how many client connections to pgbouncer (instead of Postgres) are allowed.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>\u003Ccode>default_pool_size = 25\u003C\u002Fcode>&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>The \u003Cstrong>\u003Ccode>default_pool_size\u003C\u002Fcode>\u003C\u002Fstrong> parameter defines how many server connections to allow per user\u002Fdatabase pair.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>\u003Ccode>reserve_pool_size = 5\u003C\u002Fcode>&nbsp;&nbsp;&nbsp;&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>The\u003Cstrong> \u003Ccode>reserve_pool_size\u003C\u002Fcode>\u003C\u002Fstrong> parameter defines how many additional connections are allowed to the pool.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>As in the previous test I executed pgbench with ten different client sessions. Each client executes 1000 transactions as shown below.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>\u003Cstrong>\u003Ccode>pgbench&nbsp; -c 10&nbsp; -p -j 2&nbsp; -t 1000 database_name\u003C\u002Fcode>\u003C\u002Fstrong>&nbsp;&nbsp;&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:image -->\n\u003Cfigure class=\"wp-block-image\">\u003Cimg src=\"https:\u002F\u002Flh6.googleusercontent.com\u002F-ywF0vJJ9mg-UWw7lPr7pUZzjqcBeA8iuLgf_zJ5AKO9lrBgJIRJA81i7iFa0QWVvjJS4l-MS7GEW3zdV9OcrtBP17xIdFYsL6IQwdFGDb1Kx4xNwipEyFw4zF3IoVumvLftF4DI\" alt=\"\"\u002F>\u003C\u002Ffigure>\n\u003C!-- \u002Fwp:image -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>&nbsp;As you see, transaction throughput increased from \u003Cstrong>486 transactions per second\u003C\u002Fstrong> to \u003Cstrong>566 transactions per second. \u003C\u002Fstrong>With the help of pgbouncer, transaction throughput improved by approximately 60%.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Now let’s see how we can increase transaction throughput with pgpool-II since it comes with connection pooling features.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Unlike pgbouncer, pgpool-II offers features beyond connection pooling. The \u003Ca href=\"https:\u002F\u002Fwww.pgpool.net\u002Fdocs\u002Fpgpool-II-3.2.1\u002Ftutorial-en.html\">documentation \u003C\u002Fa>provides detailed information about pgpool-II features and how to set it up from source or via a package manager\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>I changed the following parameters in the \u003Cstrong>\u003Ccode>pgpool.conf\u003C\u002Fcode>\u003C\u002Fstrong> file to make it route clients connections from pgpool2 to Postgres database server.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:preformatted -->\n\u003Cpre class=\"wp-block-preformatted\">\u003Cstrong>connection_cache\u003C\u002Fstrong>&nbsp; = on&nbsp;&nbsp;\n\u003Cstrong>listen_addresses\u003C\u002Fstrong>&nbsp; = ‘postgres_database_name’’&nbsp;&nbsp;\n\u003Cstrong>port\u003C\u002Fstrong>&nbsp; = 5432&nbsp;\u003C\u002Fpre>\n\u003C!-- \u002Fwp:preformatted -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Setting the \u003Cstrong>\u003Ccode>connection_cache\u003C\u002Fcode> \u003C\u002Fstrong>parameter to \u003Cstrong>\u003Ccode>on\u003C\u002Fcode>\u003C\u002Fstrong> activates pgpool2 pooling capability.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Like the previous test, pgbench executed ten different client sessions. Each client executes 1000 transactions to the Postgres database server. Thus we expect a total of 10,000 transactions from all clients.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>\u003Ccode>gbench&nbsp; -p 9999&nbsp; -c&nbsp; 10&nbsp; -C&nbsp; -t 1000&nbsp; postgres_database \u003C\u002Fcode>&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:image -->\n\u003Cfigure class=\"wp-block-image\">\u003Cimg src=\"https:\u002F\u002Flh3.googleusercontent.com\u002F3inrK8JcrDUiSFo9-vzijvglcWwaBy5LaZ3RE0y3t8oV57JcbxBwZPcF07LyVbqZph2THYm46qt9moOy4ko9-SImi1eTrVPmdqyHLTdq9F4_NWxW9YO-zbmYkN6aMYo9hDpwsRxo\" alt=\"\"\u002F>\u003C\u002Ffigure>\n\u003C!-- \u002Fwp:image -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>In the same way we increased transaction throughput with pgbouncer, it seems pgpool2 also increased transaction throughput by \u003Cstrong>75%\u003C\u002Fstrong> as compared to the initial test.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Pgbouncer implements connection pooling ‘out of the box’ without the need to fine-tune parameters while pgpool2 allows you to fine-tune parameters to enhance connection pooling.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading -->\n\u003Ch2 id=\"h-choosing-a-connection-pooler-pgpool-ii-or-pgbouncer\">\u003Cstrong>Choosing a connection pooler: pgpool-II or pgbouncer?\u003C\u002Fstrong>\u003C\u002Fh2>\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>There are several factors to consider when choosing a connection pooler to use. Although pgbouncer and pgpool-II are great solutions for connection pooling, each tool has its strengths and weaknesses.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading {\"level\":3} -->\n\u003Ch3 id=\"h-memory-resource-consumption\">Memory\u002Fresource consumption&nbsp;\u003C\u002Fh3>\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>If you are interested in a lightweight connection pooler for your backend service, then pgbouncer is the right tool for you. Unlike pgpool-II, which by default allows 32 child processes to be forked, pgbouncer uses only one process. Thus pgbouncer consumes less memory than pgpool2.\u003Cstrong>&nbsp;\u003C\u002Fstrong>\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading {\"level\":3} -->\n\u003Ch3 id=\"h-streaming-replication\">Streaming Replication\u003C\u002Fh3>\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Apart from pooling connections, you can also manage your Postgres cluster with streaming replication using pgpool-II.&nbsp; Streaming replication copies data from a primary node to a secondary node. Pgpool-II supports Postgres streaming replication, while pgbouncer does not. It is the best way to achieve high availability and prevent data loss.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading {\"level\":3} -->\n\u003Ch3 id=\"h-centralized-password-management\">Centralized password management&nbsp;\u003C\u002Fh3>\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>In a production environment where you expect many clients\u002Fapplications to connect to the database through a connection pooler concurrently, it is necessary to use a centralized password management system to manage clients' credentials.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>You can make use of \u003Ca href=\"https:\u002F\u002Fwww.pgbouncer.org\u002Fconfig.html#authentication-file-format\">auth_query\u003C\u002Fa> in pgbouncer to load clients’ credentials from the database instead of storing clients’ credentials in a \u003Ccode>userlist.txt\u003C\u002Fcode> file and comparing credentials from the connection string against the \u003Ccode>userlist.txt\u003C\u002Fcode> file.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading {\"level\":3} -->\n\u003Ch3 id=\"h-load-balancing-and-high-availability\">Load balancing and high availability\u003C\u002Fh3>\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Finally, if you want to add load balancing and high availability to your pooled connections, then pgpool2 is the right tool to use. pgpool2 supports Postgres high availability through the in-built watchdog processes. This pgpool2 sub-process monitors the health of pgpool2 nodes participating in the watchdog cluster as well as coordinating between multiple pgpool2 nodes.&nbsp;&nbsp;&nbsp;&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:heading -->\n\u003Ch2 id=\"h-conclusion\">\u003Cstrong>Conclusion \u003C\u002Fstrong>&nbsp;\u003C\u002Fh2>\n\u003C!-- \u002Fwp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Database performance can be improved beyond connection pooling. Replication, load balancing, and in-memory caching can contribute to efficient database performance.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>If a web service is designed to make a lot of read and write queries to a database, then you have multiple instances of a Postgres database in place to take care of write queries from clients through a load balancer such as pgpool-II while in-memory caching can be used to optimize read queries.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Despite the pgpool-II ability to function as a loader balancer and connection pooler, pgbouncer is the preferred middleware solution for connection pooling because it is easy to set up, not too difficult to manage, and primarily serves as a connection pooler without any other functions.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->","html","2020-10-14T12:25:53.000Z",{"current":1143},"improve-database-performance-with-connection-pooling",[1145,1153,1158,1162,1165],{"_createdAt":1146,"_id":1147,"_rev":1148,"_type":1149,"_updatedAt":1146,"slug":1150,"title":1152},"2023-05-23T16:43:21Z","wp-tagcat-code-for-a-living","9HpbCsT2tq0xwozQfkc4ih","blogTag",{"current":1151},"code-for-a-living","Code for a Living",{"_createdAt":1146,"_id":1154,"_rev":1148,"_type":1149,"_updatedAt":1146,"slug":1155,"title":1157},"wp-tagcat-connection-pooling",{"current":1156},"connection-pooling","connection pooling",{"_createdAt":1146,"_id":1159,"_rev":1148,"_type":1149,"_updatedAt":1146,"slug":1160,"title":1161},"wp-tagcat-databases",{"current":1161},"databases",{"_createdAt":1146,"_id":1163,"_rev":1148,"_type":1149,"_updatedAt":1146,"slug":1164,"title":126},"wp-tagcat-pgbouncer",{"current":126},{"_createdAt":1146,"_id":1166,"_rev":1148,"_type":1149,"_updatedAt":1146,"slug":1167,"title":1169},"wp-tagcat-postgresql",{"current":1168},"postgresql","postgreSQL","Improve database performance with connection pooling",[1172,1178,1184,1190],{"_id":1173,"publishedAt":1174,"slug":1175,"sponsored":12,"title":1177},"76c9771b-34e6-4d98-8641-ecefc711f0ef","2026-07-06T15:23:34.559Z",{"_type":10,"current":1176},"when-the-sensor-starts-thinking-snortml-agentic-ai-and-the-evolving-architecture-of-intrusion-detection","When the sensor starts thinking: SnortML, agentic AI, and the evolving architecture of intrusion detection",{"_id":1179,"publishedAt":1180,"slug":1181,"sponsored":12,"title":1183},"28e560af-f0aa-4d46-bd90-f435ad604aa7","2026-06-26T14:00:27.102Z",{"_type":10,"current":1182},"paging-charity-how-can-engineering-leaders-avoid-becoming-bond-villains","Paging Charity! How can engineering leaders avoid becoming Bond villains?",{"_id":1185,"publishedAt":1186,"slug":1187,"sponsored":12,"title":1189},"4b22c2a3-3779-4966-93eb-5230391dbdce","2026-06-23T14:08:58.595Z",{"_type":10,"current":1188},"your-ai-shipped-a-backend-that-boots-that-is-the-whole-problem","Your AI shipped a backend that boots. That is the whole problem.",{"_id":1191,"publishedAt":1192,"slug":1193,"sponsored":12,"title":1195},"5cf362e1-fe7b-45af-b69c-914731c6a052","2026-06-23T14:00:00.000Z",{"_type":10,"current":1194},"the-2026-developer-survey-is-now-open-for-human-developers-only","The 2026 Developer Survey is now open (for human developers only)!",{"data":1197,"sourceMap":-1},{"count":1198,"lastTimestamp":1199},13,"2026-04-06T20:39:39Z"]