diff --git a/CHANGELOG.md b/CHANGELOG.md index 17b2fb756..0cd62029e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +### Fixed + +## [1.5.2] - 2023-06-12 + +Same as 1.5.0. GitHub does not like gpg signed text in the tag message (even with prefixed armor), +so pushing a new tag. + +## [1.5.1] - 2023-06-12 + +Same as 1.5.0. GitHub does not like gpg signed text in the tag message, +so pushing a new tag. + +## [1.5.0] - 2023-06-12 + +### Added + +- gocql now advertises the driver name and version in the STARTUP message to the server. + The values are taken from the Go module's path and version + (or from the replacement module, if used). (#1702) + That allows the server to track which fork of the driver is being used. +- Query.Values() to retrieve the values bound to the Query. + This makes writing wrappers around Query easier. (#1700) + ### Fixed - Potential panic on deserialization (#1695) - Unmarshalling of dates outside of `[1677-09-22, 2262-04-11]` range. (#1692) diff --git a/conn.go b/conn.go index 2b535ad81..b0e15851d 100644 --- a/conn.go +++ b/conn.go @@ -423,7 +423,9 @@ func (s *startupCoordinator) options(ctx context.Context) error { func (s *startupCoordinator) startup(ctx context.Context, supported map[string][]string) error { m := map[string]string{ - "CQL_VERSION": s.conn.cfg.CQLVersion, + "CQL_VERSION": s.conn.cfg.CQLVersion, + "DRIVER_NAME": driverName, + "DRIVER_VERSION": driverVersion, } if s.conn.compressor != nil { diff --git a/control.go b/control.go index bda84ebce..47ec7abaf 100644 --- a/control.go +++ b/control.go @@ -282,7 +282,7 @@ func (c *controlConn) setupConn(conn *Conn) error { } if err := c.registerEvents(conn); err != nil { - return err + return fmt.Errorf("register events: %v", err) } ch := &connHost{ @@ -347,6 +347,20 @@ func (c *controlConn) reconnect() { } defer atomic.StoreInt32(&c.reconnecting, 0) + conn, err := c.attemptReconnect() + + if conn == nil { + c.session.logger.Printf("gocql: unable to reconnect control connection: %v\n", err) + return + } + + err = c.session.refreshRing() + if err != nil { + c.session.logger.Printf("gocql: unable to refresh ring: %v\n", err) + } +} + +func (c *controlConn) attemptReconnect() (*Conn, error) { hosts := c.session.ring.allHosts() hosts = shuffleHosts(hosts) @@ -363,6 +377,25 @@ func (c *controlConn) reconnect() { ch.conn.Close() } + conn, err := c.attemptReconnectToAnyOfHosts(hosts) + + if conn != nil { + return conn, err + } + + c.session.logger.Printf("gocql: unable to connect to any ring node: %v\n", err) + c.session.logger.Printf("gocql: control falling back to initial contact points.\n") + // Fallback to initial contact points, as it may be the case that all known initialHosts + // changed their IPs while keeping the same hostname(s). + initialHosts, resolvErr := addrsToHosts(c.session.cfg.Hosts, c.session.cfg.Port, c.session.logger) + if resolvErr != nil { + return nil, fmt.Errorf("resolve contact points' hostnames: %v", resolvErr) + } + + return c.attemptReconnectToAnyOfHosts(initialHosts) +} + +func (c *controlConn) attemptReconnectToAnyOfHosts(hosts []*HostInfo) (*Conn, error) { var conn *Conn var err error for _, host := range hosts { @@ -379,15 +412,7 @@ func (c *controlConn) reconnect() { conn.Close() conn = nil } - if conn == nil { - c.session.logger.Printf("gocql: control unable to register events: %v\n", err) - return - } - - err = c.session.refreshRing() - if err != nil { - c.session.logger.Printf("gocql: unable to refresh ring: %v\n", err) - } + return conn, err } func (c *controlConn) HandleError(conn *Conn, err error, closed bool) { diff --git a/doc.go b/doc.go index b48cef88e..6739d98e4 100644 --- a/doc.go +++ b/doc.go @@ -30,6 +30,9 @@ // protocol version explicitly, as it's not defined which version will be used in certain situations (for example // during upgrade of the cluster when some of the nodes support different set of protocol versions than other nodes). // +// The driver advertises the module name and version in the STARTUP message, so servers are able to detect the version. +// If you use replace directive in go.mod, the driver will send information about the replacement module instead. +// // When ready, create a session from the configuration. Don't forget to Close the session once you are done with it: // // session, err := cluster.CreateSession() diff --git a/host_source.go b/host_source.go index ac30bfc55..a0b7058d7 100644 --- a/host_source.go +++ b/host_source.go @@ -714,7 +714,7 @@ func refreshRing(r *ringDescriber) error { if !ok { return fmt.Errorf("get existing host=%s from prevHosts: %w", h, ErrCannotFindHost) } - if h.nodeToNodeAddress().Equal(existing.nodeToNodeAddress()) { + if h.connectAddress.Equal(existing.connectAddress) && h.nodeToNodeAddress().Equal(existing.nodeToNodeAddress()) { // no host IP change host.update(h) } else { diff --git a/session.go b/session.go index 3b0d71c8d..d5ff9ecae 100644 --- a/session.go +++ b/session.go @@ -93,8 +93,8 @@ var queryPool = &sync.Pool{ func addrsToHosts(addrs []string, defaultPort int, logger StdLogger) ([]*HostInfo, error) { var hosts []*HostInfo - for _, hostport := range addrs { - resolvedHosts, err := hostInfo(hostport, defaultPort) + for _, hostaddr := range addrs { + resolvedHosts, err := hostInfo(hostaddr, defaultPort) if err != nil { // Try other hosts if unable to resolve DNS name if _, ok := err.(*net.DNSError); ok { @@ -935,6 +935,12 @@ func (q Query) Statement() string { return q.stmt } +// Values returns the values passed in via Bind. +// This can be used by a wrapper type that needs to access the bound values. +func (q Query) Values() []interface{} { + return q.values +} + // String implements the stringer interface. func (q Query) String() string { return fmt.Sprintf("[query statement=%q values=%+v consistency=%s]", q.stmt, q.values, q.cons) diff --git a/version.go b/version.go new file mode 100644 index 000000000..015b40e1e --- /dev/null +++ b/version.go @@ -0,0 +1,28 @@ +package gocql + +import "runtime/debug" + +const ( + mainModule = "github.com/gocql/gocql" +) + +var driverName string + +var driverVersion string + +func init() { + buildInfo, ok := debug.ReadBuildInfo() + if ok { + for _, d := range buildInfo.Deps { + if d.Path == mainModule { + driverName = mainModule + driverVersion = d.Version + if d.Replace != nil { + driverName = d.Replace.Path + driverVersion = d.Replace.Version + } + break + } + } + } +}