vertica/vertica-sql-go

does this go driver support connection load balancing

rajsameer opened this issue · 10 comments

Hi,
Does this driver in the current phase support connection load balancing.

Hi @rajsameer
In the current phase this go client does not support connection load balancing.

Thanks. Any plans on adding the support.

@rajsameer It is penciled in to the roadmap, but nobody is actively assigned to it. Being open source, you're encouraged to contribute features. If not, the dev team can look at prioritizing such a feature if the need is immediate.

Is there any documentation around Vertica's load balancing protocol that could be used as a reference for work on this feature?

@watercraft The implementation of vertica-python might be a good reference: https://github.com/vertica/vertica-python/blob/6df1abec10ca189331d9f61bc56e11e1dcf86b18/vertica_python/vertica/connection.py#L422

Load balancing happens between the initial establishment of the client connection and enabling SSL.

LoadBalanceRequest Message

Int32 (value=8) | Length of message contents in bytes, including self.
Int32 (value=80936960) | The LoadBalance request code. The value is chosen to contain 1235 in the most significant 16 bits, and 0000 in the least 16 significant bits.

LoadBalanceResponse 'N' Message

Byte1 (value='N') | Identifies the message as a LoadBalance response. The server rejects the client LoadBalance request.

LoadBalanceResponse 'Y' Message

Byte1 (value='Y') | Identifies the message as a LoadBalance response. The server accepts the client LoadBalance request.
Int32 | Length of message contents in bytes, including self.
Int32 | The port number of the load balance target.
String | The host of the load balance target. The string is null terminated.

Below was my first attempt to translate the Python code before I read your reply.
I believe this is handling success correctly but I'm not sure about failure.
The BELoadBalanceMsg only has Host & Port, how do I get Byte1 ?

func (v *connection) balanceLoad() error {

	if err := v.sendMessage(&msgs.FELoadBalanceMsg{}); err != nil {
		return err
	}

	bMsg, err := v.recvMessage()
	if err != nil {
		return err
	}

	switch msg := bMsg.(type) {
	case *msgs.BEErrorMsg:
		return msg.ToErrorType()
	case *msgs.BELoadBalanceMsg:
		v.conn.Close()
		newURL, err := url.Parse(fmt.Sprintf("vertica://%s:%d", msg.Host, msg.Port))
		if err != nil {
			return err
		}
		v.conn, err = net.Dial("tcp", newURL.Host)
		if err != nil {
			return fmt.Errorf("cannot connect to %s (%s)", newURL.Host, err.Error())
		}
		return nil
	default:
		_, err = v.defaultMessageHandler(msg)
		if err != nil {
			return err
		}
	}

	return nil
}

@watercraft Thanks for doing that. For BELoadBalanceMsg, you might have to define two different message types, like BELoadBalanceSuccessMsg and BELoadBalanceFailMsg. It looks fine to include BELoadBalanceFailMsg case into default:, but you can separate them and give better messages. My suggestion is that if balanceLoad() raise an error, the client should use the initial connection rather than give an error to the user.

I've coded the two message in my fork (watercraft/vertica-sql-go), however, I'm confused by the behavior when the sever has load balancing enabled but the client is configured to not do load balancing. The connection succeeds, however, the first command I make comes back with EOF. Do I need to always issue the load balancing exchange to deal with this and perhaps ignore the response or must the client and server configurations be in sync?

@watercraft My guess is that in that case Vertica returns some kind of error that is not currently being handled by the driver, similar to #51. I could be wrong though.

@watercraft If the client side disables load balancing, then following communications will use the initial connection, no LoadBalanceRequest Message will send to the server. Could you please write down the log of message communications? So that I can see why it returned a EOF.