Skip to content

Commit 62ed98e

Browse files
authored
Fix #45: Support Connection Load balancing (#85)
* Add load balancing * Update README.md
1 parent facae10 commit 62ed98e

File tree

4 files changed

+183
-0
lines changed

4 files changed

+183
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ Currently supported query arguments are:
9595
|----------------|-------------|--------|
9696
| use_prepared_statements | whether to use client-side query interpolation or server-side argument binding | 1 = (default) use server-side bindings |
9797
| | | 0 = user client side interpolation **(LESS SECURE)** |
98+
| connection_load_balance | whether to enable connection load balancing on the client side | 0 = (default) disable load balancing |
99+
| | | 1 = enable load balancing |
98100
| tlsmode | the ssl/tls policy for this connection | 'none' (default) = don't use SSL/TLS for this connection |
99101
| | | 'server' = server must support SSL/TLS, but skip verification **(INSECURE!)** |
100102
| | | 'server-strict' = server must support SSL/TLS |

connection.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ func newConnection(connString string) (*connection, error) {
180180
result.usePreparedStmts = iFlag == "1"
181181
}
182182

183+
// Read connection load balance flag.
184+
loadBalanceFlag := result.connURL.Query().Get("connection_load_balance")
185+
183186
sslFlag := strings.ToLower(result.connURL.Query().Get("tlsmode"))
184187
if sslFlag == "" {
185188
sslFlag = "none"
@@ -191,6 +194,13 @@ func newConnection(connString string) (*connection, error) {
191194
return nil, fmt.Errorf("cannot connect to %s (%s)", result.connURL.Host, err.Error())
192195
}
193196

197+
// Load Balancing
198+
if loadBalanceFlag == "1" {
199+
if err = result.balanceLoad(); err != nil {
200+
return nil, err
201+
}
202+
}
203+
194204
if sslFlag != "none" {
195205
if err = result.initializeSSL(sslFlag); err != nil {
196206
return nil, err
@@ -432,6 +442,67 @@ func (v *connection) readAll(buf []byte) error {
432442
}
433443
}
434444

445+
func (v *connection) balanceLoad() error {
446+
v.sendMessage(&msgs.FELoadBalanceMsg{})
447+
response := v.scratch[:1]
448+
449+
var err error
450+
if err = v.readAll(response); err != nil {
451+
return err
452+
}
453+
454+
if response[0] == 'N' {
455+
// keep existing connection
456+
connectionLogger.Debug("<- LoadBalanceResponse: N")
457+
connectionLogger.Warn("Load balancing requested but not supported by server")
458+
return nil
459+
}
460+
461+
if response[0] != 'Y' {
462+
connectionLogger.Debug("<- LoadBalanceResponse: %c", response[0])
463+
return fmt.Errorf("Load balancing request gave unknown response: %c", response[0])
464+
}
465+
466+
header := v.scratch[1:5]
467+
if err = v.readAll(header); err != nil {
468+
return err
469+
}
470+
msgSize := int(binary.BigEndian.Uint32(header) - 4)
471+
msgBytes := v.scratch[5:]
472+
473+
var y []byte
474+
if msgSize > 0 {
475+
if msgSize <= len(msgBytes) {
476+
y = msgBytes[:msgSize]
477+
} else {
478+
y = make([]byte, msgSize)
479+
}
480+
if err = v.readAll(y); err != nil {
481+
return err
482+
}
483+
}
484+
485+
bem, err := msgs.CreateBackEndMsg(response[0], y)
486+
if err != nil {
487+
return err
488+
}
489+
connectionLogger.Debug("<- " + bem.String())
490+
msg := bem.(*msgs.BELoadBalanceMsg)
491+
492+
// v.connURL.Hostname() is used by initializeSSL(), so load balancing info should not write into v.connURL
493+
loadBalanceAddr := fmt.Sprintf("%s:%d", msg.Host, msg.Port)
494+
495+
// Connect to new host
496+
v.conn.Close()
497+
v.conn, err = net.Dial("tcp", loadBalanceAddr)
498+
499+
if err != nil {
500+
return fmt.Errorf("cannot redirect to %s (%s)", loadBalanceAddr, err.Error())
501+
}
502+
503+
return nil
504+
}
505+
435506
func (v *connection) initializeSSL(sslFlag string) error {
436507
v.sendMessage(&msgs.FESSLMsg{})
437508

msgs/beloadbalancemsg.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package msgs
2+
3+
// Copyright (c) 2020 Micro Focus or one of its affiliates.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
//
17+
// Permission is hereby granted, free of charge, to any person obtaining a copy
18+
// of this software and associated documentation files (the "Software"), to deal
19+
// in the Software without restriction, including without limitation the rights
20+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21+
// copies of the Software, and to permit persons to whom the Software is
22+
// furnished to do so, subject to the following conditions:
23+
//
24+
// The above copyright notice and this permission notice shall be included in
25+
// all copies or substantial portions of the Software.
26+
//
27+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
33+
// THE SOFTWARE.
34+
35+
import (
36+
"fmt"
37+
)
38+
39+
type BELoadBalanceMsg struct {
40+
Port uint32
41+
Host string
42+
}
43+
44+
// CreateFromMsgBody
45+
func (m *BELoadBalanceMsg) CreateFromMsgBody(buf *msgBuffer) (BackEndMsg, error) {
46+
msg := &BELoadBalanceMsg{
47+
Port: buf.readUint32(),
48+
Host: buf.readString(),
49+
}
50+
return msg, nil
51+
}
52+
53+
func (m *BELoadBalanceMsg) String() string {
54+
return fmt.Sprintf("LoadBalanceResponse: host=%s, port=%d", m.Host, m.Port)
55+
}
56+
57+
func init() {
58+
registerBackEndMsgType('Y', &BELoadBalanceMsg{})
59+
}

msgs/feloadbalancemsg.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package msgs
2+
3+
// Copyright (c) 2020 Micro Focus or one of its affiliates.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
//
17+
// Permission is hereby granted, free of charge, to any person obtaining a copy
18+
// of this software and associated documentation files (the "Software"), to deal
19+
// in the Software without restriction, including without limitation the rights
20+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21+
// copies of the Software, and to permit persons to whom the Software is
22+
// furnished to do so, subject to the following conditions:
23+
//
24+
// The above copyright notice and this permission notice shall be included in
25+
// all copies or substantial portions of the Software.
26+
//
27+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
33+
// THE SOFTWARE.
34+
35+
// FELoadBalanceMsg docs
36+
type FELoadBalanceMsg struct {
37+
}
38+
39+
// Flatten docs
40+
func (m *FELoadBalanceMsg) Flatten() ([]byte, byte) {
41+
42+
buf := newMsgBuffer()
43+
44+
buf.appendUint32(80936960)
45+
46+
return buf.bytes(), 0
47+
}
48+
49+
func (m *FELoadBalanceMsg) String() string {
50+
return "LoadBalanceRequest"
51+
}

0 commit comments

Comments
 (0)