github.com/cloudberrydb/gpbackup@v1.0.3-0.20240118031043-5410fd45eed6/utils/gpexpand_sensor.go (about) 1 package utils 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 8 "github.com/blang/vfs" 9 "github.com/cloudberrydb/gp-common-go-libs/dbconn" 10 "github.com/cloudberrydb/gp-common-go-libs/gplog" 11 "github.com/pkg/errors" 12 ) 13 14 const ( 15 BackupPreventedByGpexpandMessage GpexpandFailureMessage = `CloudberryDB expansion currently in process, please re-run gpbackup when the expansion has completed` 16 17 RestorePreventedByGpexpandMessage GpexpandFailureMessage = `CloudberryDB expansion currently in process. Once expansion is complete, it will be possible to restart gprestore, but please note existing backup sets taken with a different cluster configuration may no longer be compatible with the newly expanded cluster configuration` 18 19 CoordinatorDataDirQuery = `select datadir from gp_segment_configuration where content=-1 and role='p'` 20 GpexpandTemporaryTableStatusQuery = `SELECT status FROM gpexpand.status ORDER BY updated DESC LIMIT 1` 21 GpexpandStatusTableExistsQuery = `select relname from pg_class JOIN pg_namespace on (pg_class.relnamespace = pg_namespace.oid) where relname = 'status' and pg_namespace.nspname = 'gpexpand'` 22 23 GpexpandStatusFilename = "gpexpand.status" 24 ) 25 26 type GpexpandSensor struct { 27 fs vfs.Filesystem 28 postgresConn *dbconn.DBConn 29 } 30 31 type GpexpandFailureMessage string 32 33 func CheckGpexpandRunning(errMsg GpexpandFailureMessage) { 34 postgresConn := dbconn.NewDBConnFromEnvironment("postgres") 35 postgresConn.MustConnect(1) 36 defer postgresConn.Close() 37 if true { 38 gpexpandSensor := NewGpexpandSensor(vfs.OS(), postgresConn) 39 isGpexpandRunning, err := gpexpandSensor.IsGpexpandRunning() 40 gplog.FatalOnError(err) 41 if isGpexpandRunning { 42 gplog.Fatal(errors.New(string(errMsg)), "") 43 } 44 } 45 } 46 47 func NewGpexpandSensor(myfs vfs.Filesystem, conn *dbconn.DBConn) GpexpandSensor { 48 return GpexpandSensor{ 49 fs: myfs, 50 postgresConn: conn, 51 } 52 } 53 54 func (sensor GpexpandSensor) IsGpexpandRunning() (bool, error) { 55 err := validateConnection(sensor.postgresConn) 56 if err != nil { 57 gplog.Error(fmt.Sprintf("Error encountered validating db connection: %v", err)) 58 return false, err 59 } 60 coordinatorDataDir, err := dbconn.SelectString(sensor.postgresConn, CoordinatorDataDirQuery) 61 if err != nil { 62 gplog.Error(fmt.Sprintf("Error encountered retrieving data directory: %v", err)) 63 return false, err 64 } 65 66 _, err = sensor.fs.Stat(filepath.Join(coordinatorDataDir, GpexpandStatusFilename)) 67 // error has 3 possible states: 68 if err == nil { 69 // file exists, so gpexpand is running 70 return true, nil 71 } 72 if os.IsNotExist(err) { 73 // file not present means gpexpand is not in "phase 1". 74 // now check whether the postgres database has evidence of a "phase 2" status for gpexpand, 75 // by querying a temporary status table 76 var tableName string 77 tableName, err = dbconn.SelectString(sensor.postgresConn, GpexpandStatusTableExistsQuery) 78 if err != nil { 79 gplog.Error(fmt.Sprintf("Error encountered retrieving gpexpand status: %v", err)) 80 return false, err 81 } 82 if len(tableName) <= 0 { 83 // table does not exist 84 return false, nil 85 } 86 87 var status string 88 status, err = dbconn.SelectString(sensor.postgresConn, GpexpandTemporaryTableStatusQuery) 89 if err != nil { 90 gplog.Error(fmt.Sprintf("Error encountered retrieving gpexpand status: %v", err)) 91 return false, err 92 } 93 94 // gpexpand should indicate being finished with either of 3 possible status messages: 95 if status == "EXPANSION STOPPED" || // error case 96 status == "EXPANSION COMPLETE" || // success case 97 status == "SETUP DONE" { // only one phase completed case 98 return false, nil 99 } 100 101 return true, nil 102 } 103 104 // Stat command returned a "real" error 105 return false, err 106 } 107 108 func validateConnection(conn *dbconn.DBConn) error { 109 if conn.DBName != "postgres" { 110 return errors.New("gpexpand sensor requires a connection to the postgres database") 111 } 112 return nil 113 }