github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/lorry/engines/postgres/manager.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package postgres
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  
    26  	"github.com/jackc/pgx/v5/pgconn"
    27  	"github.com/jackc/pgx/v5/pgxpool"
    28  	"github.com/pkg/errors"
    29  	"github.com/shirou/gopsutil/v3/process"
    30  	"github.com/spf13/viper"
    31  	ctrl "sigs.k8s.io/controller-runtime"
    32  
    33  	"github.com/1aal/kubeblocks/pkg/lorry/dcs"
    34  	"github.com/1aal/kubeblocks/pkg/lorry/engines"
    35  )
    36  
    37  type Manager struct {
    38  	engines.DBManagerBase
    39  	MajorVersion int
    40  	Pool         PgxPoolIFace
    41  	Proc         *process.Process
    42  	Config       *Config
    43  	isLeader     int
    44  }
    45  
    46  func NewManager(properties map[string]string) (engines.DBManager, error) {
    47  	logger := ctrl.Log.WithName("PostgreSQL")
    48  	config, err := NewConfig(properties)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	pool, err := pgxpool.NewWithConfig(context.Background(), config.pgxConfig)
    54  	if err != nil {
    55  		return nil, errors.Errorf("unable to ping the DB: %v", err)
    56  	}
    57  
    58  	managerBase, err := engines.NewDBManagerBase(logger)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	managerBase.DataDir = viper.GetString(PGDATA)
    63  
    64  	mgr := &Manager{
    65  		DBManagerBase: *managerBase,
    66  		Pool:          pool,
    67  		Config:        config,
    68  		MajorVersion:  viper.GetInt(PGMAJOR),
    69  	}
    70  
    71  	return mgr, nil
    72  }
    73  
    74  func (mgr *Manager) IsRunning() bool {
    75  	if mgr.Proc != nil {
    76  		if isRunning, err := mgr.Proc.IsRunning(); isRunning && err == nil {
    77  			return true
    78  		}
    79  		mgr.Proc = nil
    80  	}
    81  
    82  	return mgr.newProcessFromPidFile() == nil
    83  }
    84  
    85  func (mgr *Manager) newProcessFromPidFile() error {
    86  	pidFile, err := readPidFile(mgr.DataDir)
    87  	if err != nil {
    88  		mgr.Logger.Error(err, "read pid file failed, err")
    89  		return err
    90  	}
    91  
    92  	proc, err := process.NewProcess(pidFile.pid)
    93  	if err != nil {
    94  		mgr.Logger.Error(err, "new process failed, err")
    95  		return err
    96  	}
    97  
    98  	mgr.Proc = proc
    99  	return nil
   100  }
   101  
   102  func (mgr *Manager) Recover(context.Context) error {
   103  	return nil
   104  }
   105  
   106  func (mgr *Manager) GetHealthiestMember(*dcs.Cluster, string) *dcs.Member {
   107  	return nil
   108  }
   109  
   110  func (mgr *Manager) SetIsLeader(isLeader bool) {
   111  	if isLeader {
   112  		mgr.isLeader = 1
   113  	} else {
   114  		mgr.isLeader = -1
   115  	}
   116  }
   117  
   118  func (mgr *Manager) UnsetIsLeader() {
   119  	mgr.isLeader = 0
   120  }
   121  
   122  // GetIsLeader returns whether the "isLeader" is set or not and whether current member is leader or not
   123  func (mgr *Manager) GetIsLeader() (bool, bool) {
   124  	return mgr.isLeader != 0, mgr.isLeader == 1
   125  }
   126  
   127  func (mgr *Manager) IsLeaderMember(ctx context.Context, cluster *dcs.Cluster, member *dcs.Member) (bool, error) {
   128  	if member == nil {
   129  		return false, errors.Errorf("member is nil, can't check is leader member or not")
   130  	}
   131  
   132  	leaderMember := cluster.GetLeaderMember()
   133  	if leaderMember == nil {
   134  		return false, errors.Errorf("leader member is nil, can't check is leader member or not")
   135  	}
   136  
   137  	if leaderMember.Name != member.Name {
   138  		return false, nil
   139  	}
   140  
   141  	return true, nil
   142  }
   143  
   144  func (mgr *Manager) ReadCheck(ctx context.Context, host string) bool {
   145  	readSQL := fmt.Sprintf(`select check_ts from kb_health_check where type=%d limit 1;`, engines.CheckStatusType)
   146  	_, err := mgr.QueryWithHost(ctx, readSQL, host)
   147  	if err != nil {
   148  		var pgErr *pgconn.PgError
   149  		if errors.As(err, &pgErr) && pgErr.Code == "42P01" {
   150  			// no healthy check records, return true
   151  			return true
   152  		}
   153  		mgr.Logger.Error(err, "read check failed")
   154  		return false
   155  	}
   156  	return true
   157  }
   158  
   159  func (mgr *Manager) WriteCheck(ctx context.Context, host string) bool {
   160  	writeSQL := fmt.Sprintf(`
   161  		create table if not exists kb_health_check(type int, check_ts timestamp, primary key(type));
   162  		insert into kb_health_check values(%d, CURRENT_TIMESTAMP) on conflict(type) do update set check_ts = CURRENT_TIMESTAMP;
   163  		`, engines.CheckStatusType)
   164  	_, err := mgr.ExecWithHost(ctx, writeSQL, host)
   165  	if err != nil {
   166  		mgr.Logger.Error(err, "write check failed")
   167  		return false
   168  	}
   169  	return true
   170  }
   171  
   172  func (mgr *Manager) PgReload(ctx context.Context) error {
   173  	reload := "select pg_reload_conf();"
   174  
   175  	_, err := mgr.Exec(ctx, reload)
   176  
   177  	return err
   178  }
   179  
   180  func (mgr *Manager) IsPgReady(ctx context.Context) bool {
   181  	err := mgr.Pool.Ping(ctx)
   182  	if err != nil {
   183  		mgr.Logger.Error(err, "DB is not ready, ping failed")
   184  		return false
   185  	}
   186  
   187  	return true
   188  }
   189  
   190  func (mgr *Manager) Lock(ctx context.Context, reason string) error {
   191  	sql := "alter system set default_transaction_read_only=on;"
   192  
   193  	_, err := mgr.Exec(ctx, sql)
   194  	if err != nil {
   195  		mgr.Logger.Error(err, fmt.Sprintf("exec sql:%s failed", sql))
   196  		return err
   197  	}
   198  
   199  	if err = mgr.PgReload(ctx); err != nil {
   200  		mgr.Logger.Error(err, "reload conf failed")
   201  		return err
   202  	}
   203  
   204  	mgr.Logger.Info(fmt.Sprintf("Lock db success: %s", reason))
   205  	return nil
   206  }
   207  
   208  func (mgr *Manager) Unlock(ctx context.Context) error {
   209  	sql := "alter system set default_transaction_read_only=off;"
   210  
   211  	_, err := mgr.Exec(ctx, sql)
   212  	if err != nil {
   213  		mgr.Logger.Error(err, fmt.Sprintf("exec sql:%s failed", sql))
   214  		return err
   215  	}
   216  
   217  	if err = mgr.PgReload(ctx); err != nil {
   218  		mgr.Logger.Error(err, "reload conf failed")
   219  		return err
   220  	}
   221  
   222  	mgr.Logger.Info("UnLock db success")
   223  	return nil
   224  }
   225  
   226  func (mgr *Manager) ShutDownWithWait() {
   227  	mgr.Pool.Close()
   228  }