Skip to content

Commit 61e7a78

Browse files
Changes as per review and Readme Update
1 parent 47b08d3 commit 61e7a78

File tree

2 files changed

+82
-11
lines changed

2 files changed

+82
-11
lines changed

README.md

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,70 @@
44
<span class="badge-npmversion"><a href="https://npmjs.org/package/pg" title="View this project on NPM"><img src="https://img.shields.io/npm/v/pg.svg" alt="NPM version" /></a></span>
55
<span class="badge-npmdownloads"><a href="https://npmjs.org/package/pg" title="View this project on NPM"><img src="https://img.shields.io/npm/dm/pg.svg" alt="NPM downloads" /></a></span>
66

7-
This is a fork of [node-postgres](https://github.com/brianc/node-postgres) which includes smart feature like Cluster Aware and Topology Aware load balancing. To know more visit the [docs page](https://docs.yugabyte.com/preview/drivers-orms/).
7+
This is a fork of [node-postgres](https://github.com/brianc/node-postgres) which includes the following additional features:
8+
9+
# Connection load balancing
10+
11+
Users can use this feature in two configurations.
12+
13+
### Cluster-aware / Uniform connection load balancing
14+
15+
In the cluster-aware connection load balancing, connections are distributed across all the tservers in the cluster, irrespective of their placements.
16+
17+
To enable the cluster-aware connection load balancing, provide the parameter `loadBalance` set to true as `loadBalance=true` in the connection url or the connection string (DSN style).
18+
19+
```
20+
"postgresql://username:password@localhost:5433/database_name?loadBalance=true"
21+
```
22+
23+
With this parameter specified in the url, the driver will fetch and maintain the list of tservers from the given endpoint (`localhost` in above example) available in the YugabyteDB cluster and distribute the connections equally across them.
24+
25+
This list is refreshed every 5 minutes, when a new connection request is received.
26+
27+
Application needs to use the same connection url to create every connection it needs, so that the distribution happens equally.
28+
29+
### Topology-aware connection load balancing
30+
31+
With topology-aware connnection load balancing, users can target tservers in specific zones by specifying these zones as `topologyKeys` with values in the format `cloudname.regionname.zonename`. Multiple zones can be specified as comma separated values.
32+
33+
The connections will be distributed equally with the tservers in these zones.
34+
35+
Note that, you would still need to specify `loadBalance=true` to enable the topology-aware connection load balancing.
36+
37+
```
38+
"postgresql://username:password@localhost:5433/database_name?loadBalance=true&topologyKeys=cloud1.region1.zone1,cloud1.region1.zone2"
39+
```
40+
### Specifying fallback zones
41+
42+
For topology-aware load balancing, you can now specify fallback placements too. This is not applicable for cluster-aware load balancing.
43+
Each placement value can be suffixed with a colon (`:`) followed by a preference value between 1 and 10.
44+
A preference value of `:1` means it is a primary placement. A preference value of `:2` means it is the first fallback placement and so on.If no preference value is provided, it is considered to be a primary placement (equivalent to one with preference value `:1`). Example given below.
45+
46+
```
47+
"postgres://username:password@localhost:5433/database_name?loadBalance=true&topologyKeys=cloud1.region1.zone1:1,cloud1.region1.zone2:2";
48+
```
49+
50+
You can also use `*` for specifying all the zones in a given region as shown below. This is not allowed for cloud or region values.
51+
52+
```
53+
"postgres://username:password@localhost:5433/database_name?loadBalance=true&topologyKeys=cloud1.region1.*:1,cloud1.region2.*:2";
54+
```
55+
56+
The driver attempts connection to servers in the first fallback placement(s) if it does not find any servers available in the primary placement(s), then it attempts to connect to servers in the second fallback placement(s), if specified and so on. At last if no servers specified in topologyKeys are available, then it will attempt to connect to any server in the cluster (load balancing will still be followed for the entire cluster).
57+
And this repeats for each connection request.
58+
59+
## Specifying Refresh Interval
60+
61+
Users can specify Refresh Time Interval, in seconds. It is the time interval between two attempts to refresh the information about cluster nodes. Default is 300. Valid values are integers between 0 and 600. Value 0 means refresh for each connection request. Any value outside this range is ignored and the default is used.
62+
63+
To specify Refresh Interval, use the parameter `ybServersRefreshInterval` in the connection url or the connection string.
64+
65+
```
66+
"postgres://username:password@localhost:5433/database_name?ybServersRefreshInterval=X&loadBalance=true&topologyKeys=cloud1.region1.*:1,cloud1.region2.*:2";
67+
```
68+
To know more visit the [docs page](https://docs.yugabyte.com/preview/drivers-orms/).
69+
70+
For a working example which demonstrates the configurations of connection load balancing see the [driver-examples](https://github.com/yugabyte/driver-examples/tree/main/go/pgx) repository.
871

972
Non-blocking PostgreSQL client for Node.js. Pure JavaScript and optional native libpq bindings.
1073

packages/pg/lib/client.js

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -158,14 +158,14 @@ class Client extends EventEmitter {
158158
for (let value of hosts) {
159159
let host = value
160160
let placementInfoOfHost
161-
if(Client.hostServerInfo.has(host)){
161+
if (Client.hostServerInfo.has(host)) {
162162
placementInfoOfHost = Client.hostServerInfo.get(host).placementInfo
163-
}else{
163+
} else {
164164
placementInfoOfHost = hostsList.get(host).placementInfo
165165
}
166166
var toCheckStar = placementInfoOfHost.split('.')
167-
var StarplacementInfoOfHost = toCheckStar[0]+"."+toCheckStar[1]+".*"
168-
if (!Client.topologyKeyMap.get(i).includes(placementInfoOfHost) && !Client.topologyKeyMap.get(i).includes(StarplacementInfoOfHost) ) {
167+
var starPlacementInfoOfHost = toCheckStar[0]+"."+toCheckStar[1]+".*"
168+
if (!Client.topologyKeyMap.get(i).includes(placementInfoOfHost) && !Client.topologyKeyMap.get(i).includes(starPlacementInfoOfHost)) {
169169
continue
170170
}
171171
let hostCount
@@ -215,24 +215,32 @@ class Client extends EventEmitter {
215215

216216
isValidKey(key) {
217217
var zones = key.split(':')
218-
if(zones.length == 0 || zones.length >2){
218+
if (zones.length == 0 || zones.length >2) {
219219
return false
220220
}
221221
var keyParts = zones[0].split('.')
222222
if (keyParts.length !== 3) {
223223
return false
224224
}
225-
if(zones[1]==undefined){
225+
if (zones[1]==undefined) {
226226
zones[1]='1'
227227
}
228228
zones[1]=Number(zones[1])
229-
if(zones[1]<1 || zones[1]>10 || isNaN(zones[1]) || !Number.isInteger(zones[1])){
229+
if (zones[1]<1 || zones[1]>10 || isNaN(zones[1]) || !Number.isInteger(zones[1])) {
230230
return false
231231
}
232-
if(keyParts[2]!="*"){
232+
if (keyParts[2]!="*") {
233233
return Client.placementInfoHostMap.has(zones[0])
234+
} else {
235+
var allPlacementInfo = Client.placementInfoHostMap.keys();
236+
for(let placeInfo of allPlacementInfo){
237+
var placeInfoParts = placeInfo.split('.')
238+
if(keyParts[0]==placeInfoParts[0] && keyParts[1]==placeInfoParts[1]){
239+
return true
240+
}
241+
}
234242
}
235-
return true
243+
return false
236244
}
237245

238246
incrementConnectionCount() {
@@ -483,7 +491,7 @@ class Client extends EventEmitter {
483491
let key = seperatedKeys[idx]
484492
if (this.isValidKey(key)) {
485493
var zones = key.split(':')
486-
if(zones[1]==undefined){
494+
if (zones[1]==undefined) {
487495
zones[1]='1'
488496
}
489497
zones[1]=parseInt(zones[1])

0 commit comments

Comments
 (0)