forked from tarantool/tt
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tt replicaset
: add subcommand downgrade
Closes tarantool#968 @TarantoolBot document Title: `tt replicaset downgrade` downgrades database schema. The `tt replicaset downgrade` command allows for a automate downgrade of each replicaset in a Tarantool cluster. The process is performed sequentially on the master instance and its replicas to ensure data consistency. Below are the steps involved: For Each Replicaset: - **On the Master Instance**: 1. Run the following commands in sequence to downgrade the schema and take a snapshot: ```lua box.schema.downgrade(<..version..>) box.snapshot() ``` - **On Each Replica**: 1. Wait for the replica to apply all transactions produced by the `box.schema.downgrade` command executed on the master. This is done by monitoring the vector clocks (vclock) to ensure synchronization. 2. Once the repica has caught up, run the following command to take a snapshot: ```lua box.snapshot() ``` > **Error Handling**: If any errors occur during the downgrade process, the operation will halt, and an error report will be generated. --- - Timeout for Synchronization Replicas will wait for synchronization for a maximum of `Timeout` seconds. The default timeout is set to 5 seconds, but this can be adjusted manually using the `--timeout` option. **Example:** ```bash $ tt replicaset downgrade [<APP_NAME> | <URI>] -v 3.0.0 ``` - Selecting Replicasets for Downgrade You can specify which replicaset(s) to downgrade by using the `--replicaset` or `-r` option to target specific replicaset names. **Example:** ```bash $ tt replicaset upgrade [<APP_NAME> | <URI>] -v 3.0.0 replicaset <RS_NAME_1> -r <RS_NAME_2> ... ``` This provides flexibility in downgrading only the desired parts of the cluster without affecting the entire system.
- Loading branch information
Showing
5 changed files
with
452 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package replicasetcmd | ||
|
||
import ( | ||
_ "embed" | ||
"fmt" | ||
|
||
"github.com/mitchellh/mapstructure" | ||
"github.com/tarantool/tt/cli/connector" | ||
"github.com/tarantool/tt/cli/replicaset" | ||
"github.com/tarantool/tt/cli/running" | ||
) | ||
|
||
// DowngradeOpts contains options used for the downgrade process. | ||
type DowngradeOpts struct { | ||
// List of replicaset names specified by the user for the downgrade. | ||
ChosenReplicasetAliases []string | ||
// Timeout period (in seconds) for waiting on LSN synchronization. | ||
LsnTimeout int | ||
// Schema version to downgrade. | ||
DowngradeVersion string | ||
} | ||
|
||
//go:embed lua/downgrade.lua | ||
var downgradeMasterLua string | ||
|
||
func Downgrade(discoveryCtx DiscoveryCtx, opts DowngradeOpts, | ||
connOpts connector.ConnectOpts) error { | ||
replicasets, err := getReplicasets(discoveryCtx) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
replicasets = fillAliases(replicasets) | ||
replicasetsToDowngrade, err := filterReplicasetsByAliases(replicasets, | ||
opts.ChosenReplicasetAliases) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return internalDowngrade(replicasetsToDowngrade, opts.LsnTimeout, | ||
opts.DowngradeVersion, connOpts) | ||
} | ||
|
||
func internalDowngrade(replicasets []replicaset.Replicaset, lsnTimeout int, version string, | ||
connOpts connector.ConnectOpts) error { | ||
for _, replicaset := range replicasets { | ||
err := downgradeReplicaset(replicaset, lsnTimeout, version, connOpts) | ||
if err != nil { | ||
fmt.Printf("• %s: error\n", replicaset.Alias) | ||
return fmt.Errorf("replicaset %s: %w", replicaset.Alias, err) | ||
} | ||
fmt.Printf("• %s: ok\n", replicaset.Alias) | ||
} | ||
return nil | ||
} | ||
|
||
func downgradeMaster(master *instanceMeta, version string) (syncInfo, error) { | ||
var downgradeInfo syncInfo | ||
fullMasterName := running.GetAppInstanceName(master.run) | ||
res, err := master.conn.Eval(downgradeMasterLua, | ||
[]interface{}{version}, connector.RequestOpts{}) | ||
if err != nil { | ||
return downgradeInfo, fmt.Errorf( | ||
"failed to execute downgrade script on master instance - %s: %w", | ||
fullMasterName, err) | ||
} | ||
|
||
if err := mapstructure.Decode(res[0], &downgradeInfo); err != nil { | ||
return downgradeInfo, fmt.Errorf( | ||
"failed to decode response from master instance - %s: %w", | ||
fullMasterName, err) | ||
} | ||
|
||
if downgradeInfo.Err != nil { | ||
return downgradeInfo, fmt.Errorf( | ||
"master instance downgrade failed - %s: %s", | ||
fullMasterName, *downgradeInfo.Err) | ||
} | ||
return downgradeInfo, nil | ||
} | ||
|
||
func downgradeReplicaset(replicaset replicaset.Replicaset, lsnTimeout int, version string, | ||
connOpts connector.ConnectOpts) error { | ||
master, replicas, err := collectRWROInfo(replicaset, connOpts) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
defer closeConnectors(master, replicas) | ||
|
||
// Downgrade master instance, collect LSN and IID from master instance. | ||
downgradeInfo, err := downgradeMaster(master, version) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Downgrade replica instances. | ||
masterLSN := downgradeInfo.LSN | ||
masterIID := downgradeInfo.IID | ||
|
||
for _, replica := range replicas { | ||
fullReplicaName := running.GetAppInstanceName(replica.run) | ||
err := waitLSN(replica.conn, masterIID, masterLSN, lsnTimeout) | ||
if err != nil { | ||
return fmt.Errorf("can't ensure that downgrade operations performed on "+ | ||
"%s are replicated to %s to perform snapshotting on it: error "+ | ||
"waiting LSN %d in vclock component %d: %w", | ||
running.GetAppInstanceName(master.run), fullReplicaName, | ||
masterLSN, masterIID, err) | ||
} | ||
err = snapshot(&replica) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
local version = ... | ||
local allowed_versions = box.schema.downgrade_versions() | ||
|
||
local function is_version_allowed() | ||
for _, allowed_version in ipairs(allowed_versions) do | ||
if allowed_version == version then | ||
return true | ||
end | ||
end | ||
return false | ||
end | ||
|
||
local err | ||
local ok = false | ||
|
||
if not is_version_allowed() then | ||
err = "Version '" .. version .. "' is not allowed." | ||
local result = "[" | ||
for i, value in ipairs(allowed_versions) do | ||
result = result .. tostring(value) | ||
if i < #allowed_versions then | ||
result = result .. ", " | ||
else | ||
result = result .. "]" | ||
end | ||
end | ||
err = err .. "\nAllowed versions: " .. result | ||
end | ||
|
||
if err == nil then | ||
ok, err = pcall(box.schema.downgrade, tostring(version)) | ||
if ok then | ||
ok, err = pcall(box.snapshot) | ||
end | ||
end | ||
|
||
return { | ||
lsn = box.info.lsn, | ||
iid = box.info.id, | ||
err = (not ok) and tostring(err) or nil, | ||
} |
Oops, something went wrong.