Repair performs XID counter repair to prevent XID wraparound issues This function uses a safe approach: only advance XID counter, don't modify existing data
(ctx context.Context, config *RepairConfig)
| 43 | // Repair performs XID counter repair to prevent XID wraparound issues |
| 44 | // This function uses a safe approach: only advance XID counter, don't modify existing data |
| 45 | func (p *Postgres) Repair(ctx context.Context, config *RepairConfig) (*RepairResult, error) { |
| 46 | if config == nil { |
| 47 | config = DefaultRepairConfig() |
| 48 | } |
| 49 | |
| 50 | // Validate BatchSize - don't accept 0 or negative values |
| 51 | if config.BatchSize <= 0 { |
| 52 | config.BatchSize = 1000 // Use default value |
| 53 | slog.InfoContext(ctx, "Invalid BatchSize provided, using default", slog.Int("default_batch_size", 1000)) |
| 54 | } |
| 55 | |
| 56 | result := &RepairResult{ |
| 57 | Errors: make([]error, 0), |
| 58 | } |
| 59 | |
| 60 | slog.InfoContext(ctx, "Starting PostgreSQL transaction ID counter repair", |
| 61 | slog.Bool("dry_run", config.DryRun), |
| 62 | slog.Int("batch_size", config.BatchSize)) |
| 63 | |
| 64 | // Step 1: Get current PostgreSQL XID |
| 65 | currentXID, err := p.getCurrentPostgreXID(ctx) |
| 66 | if err != nil { |
| 67 | return result, fmt.Errorf("failed to get current PostgreSQL XID: %w", err) |
| 68 | } |
| 69 | |
| 70 | if config.Verbose { |
| 71 | slog.InfoContext(ctx, "Current PostgreSQL transaction ID", slog.Uint64("current_xid", currentXID)) |
| 72 | } |
| 73 | |
| 74 | // Step 2: Get maximum referenced XID from transactions table |
| 75 | maxReferencedXID, err := p.getMaxReferencedXID(ctx) |
| 76 | if err != nil { |
| 77 | result.Errors = append(result.Errors, fmt.Errorf("failed to get max referenced XID: %w", err)) |
| 78 | return result, nil |
| 79 | } |
| 80 | |
| 81 | if config.Verbose { |
| 82 | slog.InfoContext(ctx, "Maximum referenced transaction ID", slog.Uint64("max_referenced_xid", maxReferencedXID)) |
| 83 | } |
| 84 | |
| 85 | // Step 3: Advance XID counter if needed |
| 86 | if maxReferencedXID > currentXID { |
| 87 | counterDelta := int(maxReferencedXID - currentXID + 1000) // Add safety buffer |
| 88 | |
| 89 | if config.DryRun { |
| 90 | slog.InfoContext(ctx, "Would advance XID counter", |
| 91 | slog.Int("counter_delta", counterDelta), |
| 92 | slog.Uint64("target_xid", maxReferencedXID+1000)) |
| 93 | result.CreatedTxIdFixed = counterDelta |
| 94 | } else { |
| 95 | if err := p.advanceXIDCounterByDelta(ctx, counterDelta, config); err != nil { |
| 96 | result.Errors = append(result.Errors, fmt.Errorf("failed to advance XID counter: %w", err)) |
| 97 | } else { |
| 98 | result.CreatedTxIdFixed = counterDelta |
| 99 | } |
| 100 | } |
| 101 | } else { |
| 102 | slog.InfoContext(ctx, "No XID counter advancement needed") |
no test coverage detected