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 }