Skip to content

Commit

Permalink
pool: get instance status from WatchOnce request
Browse files Browse the repository at this point in the history
Starting from Tarantool version >= 3.0.0 `WatchOnce` requset is
supported. So we can get instance status using this request instead
of calling `box.info`.

This way user can add instances to the ConnectionPool without the
`execute` access.

Closes #380
  • Loading branch information
DerekBum committed Mar 5, 2024
1 parent c070b26 commit 136ef5d
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 9 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.

### Changed

- `execute` access for `box.info` is no longer required for ConnectionPool
for a Tarantool version >= 3.0.0 (#380)

### Fixed

- `ConnectionPool.Remove()` does not notify a `ConnectionHandler` after
Expand Down
3 changes: 3 additions & 0 deletions pool/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ box.once("init", function()
box.schema.user.create('test', { password = 'test' })
box.schema.user.grant('test', 'read,write,execute', 'universe')

box.schema.user.create('test_noexec', { password = 'test' })
box.schema.user.grant('test_noexec', 'read,write', 'universe')

local s = box.schema.space.create('testPool', {
id = 520,
if_not_exists = true,
Expand Down
17 changes: 15 additions & 2 deletions pool/connection_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,20 @@ func (p *ConnectionPool) DoInstance(req tarantool.Request, name string) *taranto
//

func (p *ConnectionPool) getConnectionRole(conn *tarantool.Connection) (Role, error) {
data, err := conn.Do(tarantool.NewCallRequest("box.info")).Get()
var (
roFieldName string
data []interface{}
err error
)

if isFeatureInSlice(iproto.IPROTO_FEATURE_WATCH_ONCE, conn.ProtocolInfo().Features) {
roFieldName = "is_ro"
data, err = conn.Do(tarantool.NewWatchOnceRequest("box.status")).Get()
} else {
roFieldName = "ro"
data, err = conn.Do(tarantool.NewCallRequest("box.info")).Get()
}

if err != nil {
return UnknownRole, err
}
Expand All @@ -1033,7 +1046,7 @@ func (p *ConnectionPool) getConnectionRole(conn *tarantool.Connection) (Role, er
return UnknownRole, ErrIncorrectStatus
}

replicaRole, ok := data[0].(map[interface{}]interface{})["ro"]
replicaRole, ok := data[0].(map[interface{}]interface{})[roFieldName]
if !ok {
return UnknownRole, ErrIncorrectResponse
}
Expand Down
85 changes: 85 additions & 0 deletions pool/connection_pool_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package pool_test

import (
"bytes"
"context"
"fmt"
"log"
Expand All @@ -22,6 +23,7 @@ import (
)

var user = "test"
var userNoExec = "test_noexec"
var pass = "test"
var spaceNo = uint32(520)
var spaceName = "testPool"
Expand Down Expand Up @@ -68,6 +70,18 @@ func makeInstance(server string, opts tarantool.Opts) pool.Instance {
}
}

func makeNoExecuteInstance(server string, opts tarantool.Opts) pool.Instance {
return pool.Instance{
Name: server,
Dialer: tarantool.NetDialer{
Address: server,
User: userNoExec,
Password: pass,
},
Opts: opts,
}
}

func makeInstances(servers []string, opts tarantool.Opts) []pool.Instance {
var instances []pool.Instance
for _, server := range servers {
Expand Down Expand Up @@ -130,6 +144,77 @@ func TestConnSuccessfully(t *testing.T) {
require.Nil(t, err)
}

func TestConn_no_execute_supported(t *testing.T) {
test_helpers.SkipIfWatchOnceUnsupported(t)

healthyServ := servers[0]

ctx, cancel := test_helpers.GetPoolConnectContext()
defer cancel()
connPool, err := pool.Connect(ctx,
[]pool.Instance{makeNoExecuteInstance(healthyServ, connOpts)})
require.Nilf(t, err, "failed to connect")
require.NotNilf(t, connPool, "conn is nil after Connect")

defer connPool.Close()

args := test_helpers.CheckStatusesArgs{
ConnPool: connPool,
Mode: pool.ANY,
Servers: []string{healthyServ},
ExpectedPoolStatus: true,
ExpectedStatuses: map[string]bool{
healthyServ: true,
},
}

err = test_helpers.CheckPoolStatuses(args)
require.Nil(t, err)

_, err = connPool.Do(tarantool.NewPingRequest(), pool.ANY).Get()
require.Nil(t, err)
}

func TestConn_no_execute_unsupported(t *testing.T) {
test_helpers.SkipIfWatchOnceSupported(t)

var buf bytes.Buffer
log.SetOutput(&buf)
defer log.SetOutput(os.Stderr)

healthyServ := servers[0]

ctx, cancel := test_helpers.GetPoolConnectContext()
defer cancel()
connPool, err := pool.Connect(ctx,
[]pool.Instance{makeNoExecuteInstance(healthyServ, connOpts)})
require.Nilf(t, err, "failed to connect")
require.NotNilf(t, connPool, "conn is nil after Connect")

defer connPool.Close()

require.Contains(t, buf.String(),
fmt.Sprintf("connect to %s failed: Execute access to function "+
"'box.info' is denied for user '%s'", servers[0], userNoExec))

args := test_helpers.CheckStatusesArgs{
ConnPool: connPool,
Mode: pool.ANY,
Servers: []string{healthyServ},
ExpectedPoolStatus: false,
ExpectedStatuses: map[string]bool{
healthyServ: false,
},
}

err = test_helpers.CheckPoolStatuses(args)
require.Nil(t, err)

_, err = connPool.Do(tarantool.NewPingRequest(), pool.ANY).Get()
require.Error(t, err)
require.Equal(t, "can't find healthy instance in pool", err.Error())
}

func TestConnect_empty(t *testing.T) {
cases := []struct {
Name string
Expand Down
8 changes: 1 addition & 7 deletions tarantool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2591,13 +2591,7 @@ func TestConnectionDoWatchOnceRequest(t *testing.T) {
}

func TestConnectionDoWatchOnceOnEmptyKey(t *testing.T) {
watchOnceNotSupported, err := test_helpers.IsTarantoolVersionLess(3, 0, 0)
if err != nil {
log.Fatalf("Could not check the Tarantool version: %s", err)
}
if watchOnceNotSupported {
return
}
test_helpers.SkipIfWatchOnceUnsupported(t)

conn := test_helpers.ConnectWithValidation(t, dialer, opts)
defer conn.Close()
Expand Down
8 changes: 8 additions & 0 deletions test_helpers/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,14 @@ func SkipIfWatchOnceUnsupported(t *testing.T) {
SkipIfFeatureUnsupported(t, "watch once", 3, 0, 0)
}

// SkipIfWatchOnceSupported skips test run if Tarantool with WatchOnce
// request type is used.
func SkipIfWatchOnceSupported(t *testing.T) {
t.Helper()

SkipIfFeatureSupported(t, "watch once", 3, 0, 0)
}

// SkipIfCrudSpliceBroken skips test run if splice operation is broken
// on the crud side.
// https://github.com/tarantool/crud/issues/397
Expand Down

0 comments on commit 136ef5d

Please sign in to comment.