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  }