|
6 | 6 | package logic
|
7 | 7 |
|
8 | 8 | import (
|
| 9 | + "bytes" |
9 | 10 | "context"
|
10 | 11 | gosql "database/sql"
|
11 | 12 | "errors"
|
| 13 | + "fmt" |
| 14 | + "io" |
12 | 15 | "os"
|
13 | 16 | "path/filepath"
|
14 | 17 | "runtime"
|
@@ -316,6 +319,8 @@ func (suite *MigratorTestSuite) SetupTest() {
|
316 | 319 |
|
317 | 320 | _, err := suite.db.ExecContext(ctx, "CREATE DATABASE test")
|
318 | 321 | suite.Require().NoError(err)
|
| 322 | + |
| 323 | + os.Remove("/tmp/gh-ost.sock") |
319 | 324 | }
|
320 | 325 |
|
321 | 326 | func (suite *MigratorTestSuite) TearDownTest() {
|
@@ -379,6 +384,126 @@ func (suite *MigratorTestSuite) TestFoo() {
|
379 | 384 | suite.Require().Equal("_testing_del", tableName)
|
380 | 385 | }
|
381 | 386 |
|
| 387 | +func (suite *MigratorTestSuite) TestRetryBatchCopyWithHooks() { |
| 388 | + ctx := context.Background() |
| 389 | + |
| 390 | + _, err := suite.db.ExecContext(ctx, "CREATE TABLE test.test_retry_batch (id INT PRIMARY KEY AUTO_INCREMENT, name TEXT)") |
| 391 | + suite.Require().NoError(err) |
| 392 | + |
| 393 | + const initStride = 1000 |
| 394 | + const totalBatches = 3 |
| 395 | + for i := 0; i < totalBatches; i++ { |
| 396 | + dataSize := 50 * i |
| 397 | + for j := 0; j < initStride; j++ { |
| 398 | + _, err = suite.db.ExecContext(ctx, fmt.Sprintf("INSERT INTO test.test_retry_batch (name) VALUES ('%s')", strings.Repeat("a", dataSize))) |
| 399 | + suite.Require().NoError(err) |
| 400 | + } |
| 401 | + } |
| 402 | + |
| 403 | + _, err = suite.db.ExecContext(ctx, fmt.Sprintf("SET GLOBAL max_binlog_cache_size = %d", 1024*8)) |
| 404 | + suite.Require().NoError(err) |
| 405 | + defer func() { |
| 406 | + _, err = suite.db.ExecContext(ctx, fmt.Sprintf("SET GLOBAL max_binlog_cache_size = %d", 1024*1024*1024)) |
| 407 | + suite.Require().NoError(err) |
| 408 | + }() |
| 409 | + |
| 410 | + tmpDir, err := os.MkdirTemp("", "gh-ost-hooks") |
| 411 | + suite.Require().NoError(err) |
| 412 | + defer os.RemoveAll(tmpDir) |
| 413 | + |
| 414 | + hookScript := filepath.Join(tmpDir, "gh-ost-on-batch-copy-retry") |
| 415 | + hookContent := `#!/bin/bash |
| 416 | +# Mock hook that reduces chunk size on binlog cache error |
| 417 | +ERROR_MSG="$GH_OST_LAST_BATCH_COPY_ERROR" |
| 418 | +SOCKET_PATH="/tmp/gh-ost.sock" |
| 419 | +
|
| 420 | +if ! [[ "$ERROR_MSG" =~ "max_binlog_cache_size" ]]; then |
| 421 | + echo "Nothing to do for error: $ERROR_MSG" |
| 422 | + exit 0 |
| 423 | +fi |
| 424 | +
|
| 425 | +CHUNK_SIZE=$(echo "chunk-size=?" | nc -U $SOCKET_PATH | tr -d '\n') |
| 426 | +
|
| 427 | +MIN_CHUNK_SIZE=10 |
| 428 | +NEW_CHUNK_SIZE=$(( CHUNK_SIZE * 8 / 10 )) |
| 429 | +if [ $NEW_CHUNK_SIZE -lt $MIN_CHUNK_SIZE ]; then |
| 430 | + NEW_CHUNK_SIZE=$MIN_CHUNK_SIZE |
| 431 | +fi |
| 432 | +
|
| 433 | +if [ $CHUNK_SIZE -eq $NEW_CHUNK_SIZE ]; then |
| 434 | + echo "Chunk size unchanged: $CHUNK_SIZE" |
| 435 | + exit 0 |
| 436 | +fi |
| 437 | +
|
| 438 | +echo "[gh-ost-on-batch-copy-retry]: Changing chunk size from $CHUNK_SIZE to $NEW_CHUNK_SIZE" |
| 439 | +echo "chunk-size=$NEW_CHUNK_SIZE" | nc -U $SOCKET_PATH |
| 440 | +echo "[gh-ost-on-batch-copy-retry]: Done, exiting..." |
| 441 | +` |
| 442 | + err = os.WriteFile(hookScript, []byte(hookContent), 0755) |
| 443 | + suite.Require().NoError(err) |
| 444 | + |
| 445 | + origStdout := os.Stdout |
| 446 | + origStderr := os.Stderr |
| 447 | + |
| 448 | + rOut, wOut, _ := os.Pipe() |
| 449 | + rErr, wErr, _ := os.Pipe() |
| 450 | + os.Stdout = wOut |
| 451 | + os.Stderr = wErr |
| 452 | + |
| 453 | + connectionConfig, err := GetConnectionConfig(ctx, suite.mysqlContainer) |
| 454 | + suite.Require().NoError(err) |
| 455 | + |
| 456 | + migrationContext := base.NewMigrationContext() |
| 457 | + migrationContext.AllowedRunningOnMaster = true |
| 458 | + migrationContext.ApplierConnectionConfig = connectionConfig |
| 459 | + migrationContext.InspectorConnectionConfig = connectionConfig |
| 460 | + migrationContext.DatabaseName = "test" |
| 461 | + migrationContext.SkipPortValidation = true |
| 462 | + migrationContext.OriginalTableName = "test_retry_batch" |
| 463 | + migrationContext.SetConnectionConfig("innodb") |
| 464 | + migrationContext.AlterStatementOptions = "MODIFY name LONGTEXT, ENGINE=InnoDB" |
| 465 | + migrationContext.ReplicaServerId = 99999 |
| 466 | + migrationContext.HeartbeatIntervalMilliseconds = 100 |
| 467 | + migrationContext.ThrottleHTTPIntervalMillis = 100 |
| 468 | + migrationContext.ThrottleHTTPTimeoutMillis = 1000 |
| 469 | + migrationContext.HooksPath = tmpDir |
| 470 | + migrationContext.ChunkSize = 1000 |
| 471 | + migrationContext.SetDefaultNumRetries(10) |
| 472 | + migrationContext.ServeSocketFile = "/tmp/gh-ost.sock" |
| 473 | + |
| 474 | + migrator := NewMigrator(migrationContext, "0.0.0") |
| 475 | + |
| 476 | + err = migrator.Migrate() |
| 477 | + suite.Require().NoError(err) |
| 478 | + |
| 479 | + wOut.Close() |
| 480 | + wErr.Close() |
| 481 | + os.Stdout = origStdout |
| 482 | + os.Stderr = origStderr |
| 483 | + |
| 484 | + var bufOut, bufErr bytes.Buffer |
| 485 | + io.Copy(&bufOut, rOut) |
| 486 | + io.Copy(&bufErr, rErr) |
| 487 | + |
| 488 | + outStr := bufOut.String() |
| 489 | + errStr := bufErr.String() |
| 490 | + |
| 491 | + suite.Assert().Contains(outStr, "chunk-size: 1000") |
| 492 | + suite.Assert().Contains(errStr, "[gh-ost-on-batch-copy-retry]: Changing chunk size from 1000 to 800") |
| 493 | + suite.Assert().Contains(outStr, "chunk-size: 800") |
| 494 | + |
| 495 | + suite.Assert().Contains(errStr, "[gh-ost-on-batch-copy-retry]: Changing chunk size from 800 to 640") |
| 496 | + suite.Assert().Contains(outStr, "chunk-size: 640") |
| 497 | + |
| 498 | + suite.Assert().Contains(errStr, "[gh-ost-on-batch-copy-retry]: Changing chunk size from 640 to 512") |
| 499 | + suite.Assert().Contains(outStr, "chunk-size: 512") |
| 500 | + |
| 501 | + var count int |
| 502 | + err = suite.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM test.test_retry_batch").Scan(&count) |
| 503 | + suite.Require().NoError(err) |
| 504 | + suite.Assert().Equal(3000, count) |
| 505 | +} |
| 506 | + |
382 | 507 | func TestMigratorRetry(t *testing.T) {
|
383 | 508 | oldRetrySleepFn := RetrySleepFn
|
384 | 509 | defer func() { RetrySleepFn = oldRetrySleepFn }()
|
|
0 commit comments