Compare commits

..

4 Commits

Author SHA1 Message Date
Jiří Čtvrtka
84889adbfb PMM-11406 Test to check current results. 2025-09-15 12:03:52 +02:00
Jiří Čtvrtka
f72d9c9637 PMM-11406 Remove unused method. 2025-09-09 20:16:53 +02:00
Sveta Smirnova
b07dd2792d PR-1013 - PMM-11406 Better check to prevent errors from missing fields
- Fixed log.Infof call on line 211
2025-09-09 16:57:02 +03:00
Jiří Čtvrtka
b2e25133e0 PMM-11406 Better check to prevent errors from missing fields. 2025-09-09 11:58:29 +02:00
13 changed files with 90 additions and 191 deletions

View File

@@ -3637,7 +3637,6 @@ sub diff {
next CONFIG if $val0 == $valN;
}
else {
next CONFIG if $ignore_case
? lc($val0) eq lc($valN)
: $val0 eq $valN;
@@ -3700,7 +3699,7 @@ sub _normalize_value {
my ($val, $is_dir, $base_path) = @args{qw(value is_directory base_path)};
$val = defined $val ? $val : '';
$val = $alt_val_for{uc($val)} if exists $alt_val_for{uc($val)};
$val = $alt_val_for{$val} if exists $alt_val_for{$val};
if ( $val =~ m/,/ && !$is_dir && !$base_path) {
$val = join(',', sort(split(',', $val)));

View File

@@ -11211,7 +11211,7 @@ sub get_unique_index_fields {
$clean .= $suffix;
my $fields = [];
my $fields_re = qr/\s(?:(?:(?:PRIMARY|UNIQUE)\s+(?:INDEX|KEY|))|UNIQUE)\s*(?:.*?)\s*\((.*?)\)/i;
my $fields_re = qr/\s(?:PRIMARY|UNIQUE)\s+(?:INDEX|KEY|)\s*(?:.*?)\s*\((.*?)\)/i;
while($clean =~ /$fields_re/g) {
push @$fields, [ split /\s*,\s*/, $1 ];

View File

@@ -12965,43 +12965,14 @@ Possible methods are:
=========== ==================
processlist SHOW PROCESSLIST
hosts SHOW REPLICAS (SHOW SLAVE HOSTS before MySQL 8.1)
dsn=DSN DSNs from a table
none Do not find replicas
The C<processlist> method is the default, because C<SHOW REPLICAS> is not
reliable. However, if the server uses a non-standard port (not 3306), then
the C<hosts> method becomes the default because it works better in this case.
The processlist method is preferred because SHOW REPLICAS is not reliable.
However, the hosts method is required if the server uses a non-standard
port (not 3306). Usually pt-table-sync does the right thing and finds
the replicas, but you may give a preferred method and it will be used first.
If it doesn't find any replicas, the other methods will be tried.
The C<hosts> method requires replicas to be configured with C<report_host>,
C<report_port>, etc.
The C<dsn> method is special: rather than automatically discovering replicas,
this method specifies a table with replica DSNs. The tool will only connect
to these replicas. This method works best when replicas do not use the same
MySQL username or password as the source, or when you want to prevent the tool
from connecting to certain replicas. The C<dsn> method is specified like:
C<--recursion-method dsn=h=host,D=percona,t=dsns>. The specified DSN must
have D and t parts, or just a database-qualified t part, which specify the
DSN table. The DSN table must have the following structure:
CREATE TABLE `dsns` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`dsn` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
DSNs are ordered by C<id>, but C<id> and C<parent_id> are otherwise ignored.
The C<dsn> column contains a replica DSN like it would be given on the command
line, for example: C<"h=replica_host,u=repl_user,p=repl_pass">.
The C<none> method makes the tool ignore all replicas and cluster nodes. This
method is not recommended because it effectively disables the
L<"REPLICA CHECKS"> and no differences can be found. It is useful, however, if
you only need to write checksums on the source or a single cluster node. The
safer alternative is C<--no-replicate-check>: the tool finds replicas and
cluster nodes, performs the L<"REPLICA CHECKS">, but does not check for
differences. See L<"--[no]replicate-check">.
=item --replace

4
go.mod
View File

@@ -33,7 +33,7 @@ require (
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.34.1
k8s.io/api v0.34.0
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
)
@@ -69,7 +69,7 @@ require (
golang.org/x/text v0.28.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apimachinery v0.34.1 // indirect
k8s.io/apimachinery v0.34.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect

8
go.sum
View File

@@ -205,10 +205,10 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
k8s.io/api v0.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE=
k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug=
k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0=
k8s.io/apimachinery v0.34.0/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=

View File

@@ -202,7 +202,6 @@ sub diff {
next CONFIG if $val0 == $valN;
}
else {
next CONFIG if $ignore_case
? lc($val0) eq lc($valN)
: $val0 eq $valN;
@@ -294,7 +293,7 @@ sub _normalize_value {
my ($val, $is_dir, $base_path) = @args{qw(value is_directory base_path)};
$val = defined $val ? $val : '';
$val = $alt_val_for{uc($val)} if exists $alt_val_for{uc($val)};
$val = $alt_val_for{$val} if exists $alt_val_for{$val};
if ( $val =~ m/,/ && !$is_dir && !$base_path) {
$val = join(',', sort(split(',', $val)));

View File

@@ -208,7 +208,7 @@ func main() {
if err != nil {
log.Infof("cannot check version updates: %s", err.Error())
} else if advice != "" {
log.Infof(advice)
log.Infof("%s", advice)
}
}
@@ -373,24 +373,6 @@ func getHostInfo(ctx context.Context, client *mongo.Client) (*hostInfo, error) {
return nil, errors.Wrap(err, "GetHostInfo.hostInfo")
}
cmdOpts := proto.CommandLineOptions{}
query := primitive.D{{Key: "getCmdLineOpts", Value: 1}}
err := client.Database("admin").RunCommand(ctx, query).Decode(&cmdOpts)
if err != nil {
return nil, errors.Wrap(err, "cannot get command line options")
}
ss := proto.ServerStatus{}
query = primitive.D{{Key: "serverStatus", Value: 1}}
if err := client.Database("admin").RunCommand(ctx, query).Decode(&ss); err != nil {
return nil, errors.Wrap(err, "GetHostInfo.serverStatus")
}
pi := procInfo{}
if err := getProcInfo(int32(ss.Pid), &pi); err != nil {
pi.Error = err
}
nodeType, _ := getNodeType(ctx, client)
procCount, _ := countMongodProcesses()
@@ -398,24 +380,41 @@ func getHostInfo(ctx context.Context, client *mongo.Client) (*hostInfo, error) {
Hostname: hi.System.Hostname,
HostOsType: hi.Os.Type,
HostSystemCPUArch: hi.System.CpuArch,
DBPath: "", // Sets default. It will be overridden later if necessary
ProcessName: ss.Process,
ProcProcessCount: procCount,
Version: ss.Version,
NodeType: nodeType,
ProcPath: pi.Path,
ProcUserName: pi.UserName,
ProcCreateTime: pi.CreateTime,
CmdlineArgs: cmdOpts.Argv,
CmdlineArgs: nil,
}
var cmdOpts proto.CommandLineOptions
query := primitive.D{{Key: "getCmdLineOpts", Value: 1}}
err := client.Database("admin").RunCommand(ctx, query).Decode(&cmdOpts)
if err == nil {
if len(cmdOpts.Argv) > 0 {
i.CmdlineArgs = cmdOpts.Argv
}
if cmdOpts.Parsed.Storage.DbPath != "" {
i.DBPath = cmdOpts.Parsed.Storage.DbPath
}
}
var ss proto.ServerStatus
query = primitive.D{{Key: "serverStatus", Value: 1}}
err = client.Database("admin").RunCommand(ctx, query).Decode(&ss)
if err == nil {
i.ProcessName = ss.Process
i.Version = ss.Version
if ss.Repl != nil {
i.ReplicasetName = ss.Repl.SetName
}
if cmdOpts.Parsed.Storage.DbPath != "" {
i.DBPath = cmdOpts.Parsed.Storage.DbPath
pi := procInfo{}
if err := getProcInfo(int32(ss.Pid), &pi); err != nil {
pi.Error = err
} else {
i.ProcPath = pi.Path
i.ProcUserName = pi.UserName
i.ProcCreateTime = pi.CreateTime
}
}
return i, nil

View File

@@ -8,9 +8,9 @@ import (
"time"
"github.com/pborman/getopt"
"github.com/stretchr/testify/require"
tu "github.com/percona/percona-toolkit/src/go/internal/testutils"
"github.com/percona/percona-toolkit/src/go/mongolib/proto"
)
func TestGetHostInfo(t *testing.T) {
@@ -49,6 +49,26 @@ func TestGetHostInfo(t *testing.T) {
}
}
func TestGetHostInfoResult(t *testing.T) {
assert := require.New(t)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := tu.TestClient(ctx, tu.MongoDBShard1PrimaryPort)
assert.NoError(err, "cannot get a new MongoDB client")
host, err := getHostInfo(ctx, client)
assert.NoError(err, "getHostInfo error")
// With the current setup, we should get this information.
assert.NotEmpty(host.ProcessName, "ProcessName should not be empty if serverStatus succeeds")
assert.NotEmpty(host.Version, "Version should not be empty if serverStatus succeeds")
assert.NotEmpty(host.ProcPath, "ProcPath should not be empty if getProcInfo succeeds")
assert.NotEmpty(host.ProcUserName, "ProcUserName should not be empty if getProcInfo succeeds")
assert.False(host.ProcCreateTime.IsZero(), "ProcCreateTime should not be zero if getProcInfo succeeds")
}
func TestClusterWideInfo(t *testing.T) {
testCases := []struct {
name string
@@ -85,16 +105,6 @@ func TestClusterWideInfo(t *testing.T) {
}
}
func addToCounters(ss proto.ServerStatus, increment int64) proto.ServerStatus {
ss.Opcounters.Command += increment
ss.Opcounters.Delete += increment
ss.Opcounters.GetMore += increment
ss.Opcounters.Insert += increment
ss.Opcounters.Query += increment
ss.Opcounters.Update += increment
return ss
}
func TestParseArgs(t *testing.T) {
tests := []struct {
args []string

View File

@@ -449,30 +449,6 @@ is_deeply(
"..but can be turned off"
);
# ############################################################################
# https://perconadev.atlassian.net/browse/PT-2014
# pt-config-diff does not honor case insensitivity flag for boolean values
# ############################################################################
$c1 = new MySQLConfig(
file => "$trunk/$sample/pt-2014-1.txt",
TextResultSetParser => $trp,
);
$c2 = new MySQLConfig(
file => "$trunk/$sample/pt-2014-2.txt",
TextResultSetParser => $trp,
);
{
my $diff = $cc->diff(
configs => [$c1, $c2],
);
is_deeply(
$diff,
undef,
"Boolean values are the same regardless of the case"
) or diag(Dumper($diff));
}
# #############################################################################
# Done.
# #############################################################################

View File

@@ -1,7 +0,0 @@
[mysqld]
log_bin=1
gtid_mode=on
read_only=off
autocommit=no
binlog_format=mixed
skip_name_resolve=true

View File

@@ -1,7 +0,0 @@
[mysqld]
log_bin=on
gtid_mode=True
read_only=FALSE
autocommit=0
binlog_format=MIXED
skip_name_resolve=TRUE

View File

@@ -19,7 +19,7 @@ use Sandbox;
use SqlModes;
use File::Temp qw/ tempdir /;
plan tests => 10;
plan tests => 6;
require "$trunk/bin/pt-online-schema-change";
@@ -88,47 +88,6 @@ like(
"PT-153 Adding multiple unique indexes -> multime example queries.",
);
# UNIQUE is possible without INDEX or KEY, we need to check this as well.
($output, $exit_status) = full_output(
sub { pt_online_schema_change::main(@args, "$source_dsn,D=test,t=t1",
'--execute',
'--alter', "ADD UNIQUE c1 (f2, f3)",
),
},
);
isnt(
$exit_status,
0,
"PT-153 Adding unique index without index/key keyword exit status != 0.",
);
like(
$output,
qr/You are trying to add an unique key. This can result in data loss if the data is not unique/s,
"PT-153 Adding unique index without index/key keyword warning message.",
);
($output, $exit_status) = full_output(
sub { pt_online_schema_change::main(@args, "$source_dsn,D=test,t=t1",
'--execute',
'--alter', "ADD UNIQUE(f2, f3)",
),
},
);
isnt(
$exit_status,
0,
"PT-153 Adding unique index without index/key keyword and index name exit status != 0.",
);
like(
$output,
qr/You are trying to add an unique key. This can result in data loss if the data is not unique/s,
"PT-153 Adding unique index without index/key keyword and index name warning message.",
);
$source_dbh->do("DROP DATABASE IF EXISTS test");
# #############################################################################

View File

@@ -44,16 +44,16 @@ $output = `$trunk/bin/pt-table-sync --sync-to-source h=127.1,P=12346,u=msandbox,
# and EXPLAIN does not guarantee accuracy of results.
like(
$output,
qr/FROM `issue_375`.`t` FORCE INDEX \(`updated_at`\) WHERE \(`updated_at` > 0 AND `updated_at` < '2009-09-05 \d\d:\d\d:\d\d'/,
qr/FROM `issue_375`.`t` FORCE INDEX \(`updated_at`\) WHERE \(`updated_at` > 0 AND `updated_at` < '2009-09-05 02:\d\d:\d\d'/,
'--chunk-index',
) or diag($output);
);
$output = `$trunk/bin/pt-table-sync --sync-to-source h=127.1,P=12346,u=msandbox,p=msandbox -d issue_375 --print -v -v --chunk-size 50 --chunk-column updated_at`;
like(
$output,
qr/FROM `issue_375`.`t` FORCE INDEX \(`updated_at`\) WHERE \(`updated_at` > 0 AND `updated_at` < '2009-09-05 \d\d:\d\d:\d\d'/,
qr/FROM `issue_375`.`t` FORCE INDEX \(`updated_at`\) WHERE \(`updated_at` > 0 AND `updated_at` < '2009-09-05 02:\d\d:\d\d'/,
'--chunk-column',
) or diag($output);
);
# #############################################################################
# Done.