Skip to content

Extend box information with replication fields #427

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.

### Added

- Extend box with replication information (#427).

### Changed

### Fixed
Expand Down
53 changes: 53 additions & 0 deletions box/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,59 @@ type Info struct {
Status string `msgpack:"status"`
// LSN - Log sequence number of the instance.
LSN uint64 `msgpack:"lsn"`
// Replication - replication status.
Replication map[int]Replication `msgpack:"replication,omitempty"`
}

// Replication section of box.info() is a table with statistics for all instances
// in the replica set that the current instance belongs to.
type Replication struct {
// ID is a short numeric identifier of instance n within the replica set.
ID int `msgpack:"id"`
// UUID - Unique identifier of the instance.
UUID string `msgpack:"uuid"`
// LSN - Log sequence number of the instance.
LSN uint64 `msgpack:"lsn"`
// Upstream - information about upstream.
Upstream Upstream `msgpack:"upstream,omitempty"`
// Downstream - information about downstream.
Downstream Downstream `msgpack:"downstream,omitempty"`
}

// Upstream information.
type Upstream struct {
// Status is replication status of the connection with the instance.
Status string `msgpack:"status"`
// Idle is the time (in seconds) since the last event was received.
Idle float64 `msgpack:"idle"`
// Peer contains instance n’s URI.
Peer string `msgpack:"peer"`
// Lag is the time difference between the local time of instance n,
// recorded when the event was received, and the local time at another master
// recorded when the event was written to the write-ahead log on that master.
Lag float64 `msgpack:"lag"`
// Message contains an error message in case of a degraded state; otherwise, it is nil.
Message string `msgpack:"message,omitempty"`
// SystemMessage contains an error message in case of a degraded state; otherwise, it is nil.
SystemMessage string `msgpack:"system_message,omitempty"`
}

// Downstream information.
type Downstream struct {
// Status is replication status of the connection with the instance.
Status string `msgpack:"status"`
// Idle is the time (in seconds) since the last event was received.
Idle float64 `msgpack:"idle"`
// VClock contains the vector clock, which is a table of ‘id, lsn’ pairs.
VClock map[int]uint64 `msgpack:"vclock"`
// Lag is the time difference between the local time of instance n,
// recorded when the event was received, and the local time at another master
// recorded when the event was written to the write-ahead log on that master.
Lag float64 `msgpack:"lag"`
// Message contains an error message in case of a degraded state; otherwise, it is nil.
Message string `msgpack:"message,omitempty"`
// SystemMessage contains an error message in case of a degraded state; otherwise, it is nil.
SystemMessage string `msgpack:"system_message,omitempty"`
}

// InfoResponse represents the response structure
Expand Down
126 changes: 126 additions & 0 deletions box/info_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package box

import (
"testing"

"github.com/stretchr/testify/require"
"github.com/vmihailenco/msgpack/v5"
)

func TestInfo(t *testing.T) {
id := 1
cases := []struct {
Name string
Struct Info
Data map[string]interface{}
}{
{
Name: "Case: base info struct",
Struct: Info{
Version: "2.11.4-0-g8cebbf2cad",
ID: &id,
RO: false,
UUID: "69360e9b-4641-4ec3-ab51-297f46749849",
PID: 1,
Status: "running",
LSN: 8,
},
Data: map[string]interface{}{
"version": "2.11.4-0-g8cebbf2cad",
"id": 1,
"ro": false,
"uuid": "69360e9b-4641-4ec3-ab51-297f46749849",
"pid": 1,
"status": "running",
"lsn": 8,
},
},
{
Name: "Case: info struct with replication",
Struct: Info{
Version: "2.11.4-0-g8cebbf2cad",
ID: &id,
RO: false,
UUID: "69360e9b-4641-4ec3-ab51-297f46749849",
PID: 1,
Status: "running",
LSN: 8,
Replication: map[int]Replication{
1: {
ID: 1,
UUID: "69360e9b-4641-4ec3-ab51-297f46749849",
LSN: 8,
},
2: {
ID: 2,
UUID: "75f5f5aa-89f0-4d95-b5a9-96a0eaa0ce36",
LSN: 0,
Upstream: Upstream{
Status: "follow",
Idle: 2.4564633660484,
Peer: "other.tarantool:3301",
Lag: 0.00011920928955078,
Message: "'getaddrinfo: Name or service not known'",
SystemMessage: "Input/output error",
},
Downstream: Downstream{
Status: "follow",
Idle: 2.8306158290943,
VClock: map[int]uint64{1: 8},
Lag: 0,
Message: "'unexpected EOF when reading from socket'",
SystemMessage: "Broken pipe",
},
},
},
},
Data: map[string]interface{}{
"version": "2.11.4-0-g8cebbf2cad",
"id": 1,
"ro": false,
"uuid": "69360e9b-4641-4ec3-ab51-297f46749849",
"pid": 1,
"status": "running",
"lsn": 8,
"replication": map[interface{}]interface{}{
1: map[string]interface{}{
"id": 1,
"uuid": "69360e9b-4641-4ec3-ab51-297f46749849",
"lsn": 8,
},
2: map[string]interface{}{
"id": 2,
"uuid": "75f5f5aa-89f0-4d95-b5a9-96a0eaa0ce36",
"lsn": 0,
"upstream": map[string]interface{}{
"status": "follow",
"idle": 2.4564633660484,
"peer": "other.tarantool:3301",
"lag": 0.00011920928955078,
"message": "'getaddrinfo: Name or service not known'",
"system_message": "Input/output error",
},
"downstream": map[string]interface{}{
"status": "follow",
"idle": 2.8306158290943,
"vclock": map[interface{}]interface{}{1: 8},
"lag": 0,
"message": "'unexpected EOF when reading from socket'",
"system_message": "Broken pipe",
},
},
},
},
},
}
for _, tc := range cases {
data, err := msgpack.Marshal(tc.Data)
require.NoError(t, err, tc.Name)

var result Info
err = msgpack.Unmarshal(data, &result)
require.NoError(t, err, tc.Name)

require.Equal(t, tc.Struct, result)
}
}
6 changes: 6 additions & 0 deletions box/tarantool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ func validateInfo(t testing.TB, info box.Info) {
require.NotEmpty(t, info.Version)
// Check that pid parsed correctly.
require.NotEqual(t, info.PID, 0)

// Check replication is parsed correctly.
require.NotEmpty(t, info.Replication)

// Check one replica uuid is equal system uuid.
require.Equal(t, info.UUID, info.Replication[1].UUID)
}

func TestBox_Sugar_Info(t *testing.T) {
Expand Down
3 changes: 3 additions & 0 deletions box/testdata/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ box.schema.user.grant('test', 'execute', 'universe', nil, { if_not_exists = true
-- Set listen only when every other thing is configured.
box.cfg{
listen = os.getenv("TEST_TNT_LISTEN"),
replication = {
os.getenv("TEST_TNT_LISTEN"),
},
}
Loading