Skip to content

Commit eae64e9

Browse files
committed
Merge branch '169-sync-init-params' into 'master'
feat: synchronize original configs(max_connections) on restore (#169) Closes #169 See merge request postgres-ai/database-lab!183
2 parents e7cd7df + 31296e4 commit eae64e9

File tree

1 file changed

+104
-13
lines changed

1 file changed

+104
-13
lines changed

pkg/retrieval/engine/postgres/physical/physical.go

Lines changed: 104 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package physical
77

88
import (
99
"bufio"
10+
"bytes"
1011
"context"
1112
"fmt"
1213
"io"
@@ -45,6 +46,11 @@ const (
4546
defaultPgConfigsDir = "default"
4647
)
4748

49+
var (
50+
// List of original parameters to synchronize on restore.
51+
originalParamsToRestore = []string{"max_connections"}
52+
)
53+
4854
// RestoreJob describes a job for physical restoring.
4955
type RestoreJob struct {
5056
name string
@@ -131,7 +137,9 @@ func (r *RestoreJob) Run(ctx context.Context) (err error) {
131137
}
132138
}()
133139

134-
isEmpty, err := tools.IsEmptyDirectory(r.globalCfg.DataDir())
140+
dataDir := r.globalCfg.DataDir()
141+
142+
isEmpty, err := tools.IsEmptyDirectory(dataDir)
135143
if err != nil {
136144
return errors.Wrap(err, "failed to explore the data directory")
137145
}
@@ -175,7 +183,7 @@ func (r *RestoreJob) Run(ctx context.Context) (err error) {
175183
log.Err("Failed to mark database data: ", err)
176184
}
177185

178-
pgVersion, err := tools.DetectPGVersion(r.globalCfg.DataDir())
186+
pgVersion, err := tools.DetectPGVersion(dataDir)
179187
if err != nil {
180188
return errors.Wrap(err, "failed to detect the Postgres version")
181189
}
@@ -186,27 +194,33 @@ func (r *RestoreJob) Run(ctx context.Context) (err error) {
186194
return errors.Wrap(err, "cannot get path to default configs")
187195
}
188196

189-
if err := fs.CopyDirectoryContent(sourceConfigDir, r.globalCfg.DataDir()); err != nil {
197+
if err := fs.CopyDirectoryContent(sourceConfigDir, dataDir); err != nil {
190198
return errors.Wrap(err, "failed to set default configuration files")
191199
}
192200

193-
if err := configuration.Run(r.globalCfg.DataDir()); err != nil {
201+
if err := configuration.Run(dataDir); err != nil {
194202
return errors.Wrap(err, "failed to configure")
195203
}
196204

197-
if err := r.adjustRecoveryConfiguration(pgVersion, r.globalCfg.DataDir()); err != nil {
205+
if err := r.adjustRecoveryConfiguration(pgVersion, dataDir); err != nil {
198206
return err
199207
}
200208

201209
// Set permissions.
202210
if err := tools.ExecCommand(ctx, r.dockerClient, contID, types.ExecConfig{
203-
Cmd: []string{"chown", "-R", "postgres", r.globalCfg.DataDir()},
211+
Cmd: []string{"chown", "-R", "postgres", dataDir},
204212
}); err != nil {
205213
return errors.Wrap(err, "failed to set permissions")
206214
}
207215

216+
// Apply important initial configs.
217+
if err := r.applyInitParams(ctx, contID, pgVersion, dataDir); err != nil {
218+
return errors.Wrap(err, "failed to adjust by init parameters")
219+
}
220+
208221
// Start PostgreSQL instance.
209-
startCommand, err := r.dockerClient.ContainerExecCreate(ctx, contID, startingPostgresConfig(r.globalCfg.DataDir(), pgVersion))
222+
startCommand, err := r.dockerClient.ContainerExecCreate(ctx, contID,
223+
pgCommandConfig("postgres", dataDir, pgVersion))
210224

211225
if err != nil {
212226
return errors.Wrap(err, "failed to create an exec command")
@@ -263,8 +277,8 @@ func (r *RestoreJob) startContainer(ctx context.Context, containerName, containe
263277
return syncInstance.ID, nil
264278
}
265279

266-
func startingPostgresConfig(pgDataDir, pgVersion string) types.ExecConfig {
267-
command := fmt.Sprintf("/usr/lib/postgresql/%s/bin/postgres", pgVersion)
280+
func pgCommandConfig(cmd, pgDataDir, pgVersion string) types.ExecConfig {
281+
command := fmt.Sprintf("/usr/lib/postgresql/%s/bin/%s", pgVersion, cmd)
268282

269283
return types.ExecConfig{
270284
AttachStdout: true,
@@ -350,7 +364,8 @@ func (r *RestoreJob) runSyncInstance(ctx context.Context) error {
350364
return err
351365
}
352366

353-
startSyncCommand, err := r.dockerClient.ContainerExecCreate(ctx, syncInstanceID, startingPostgresConfig(r.globalCfg.DataDir(), pgVersion))
367+
startSyncCommand, err := r.dockerClient.ContainerExecCreate(ctx, syncInstanceID,
368+
pgCommandConfig("postgres", r.globalCfg.DataDir(), pgVersion))
354369
if err != nil {
355370
return errors.Wrap(err, "failed to create exec command")
356371
}
@@ -446,14 +461,90 @@ func (r *RestoreJob) adjustRecoveryConfiguration(pgVersion, pgDataDir string) er
446461
recoveryFilename = "recovery.conf"
447462
}
448463

449-
recoveryFile, err := os.OpenFile(path.Join(pgDataDir, recoveryFilename), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
464+
return r.appendConfigFile(path.Join(pgDataDir, recoveryFilename), r.restorer.GetRecoveryConfig())
465+
}
466+
467+
func (r *RestoreJob) applyInitParams(ctx context.Context, contID, pgVersion, dataDir string) error {
468+
initConfCmd, err := r.dockerClient.ContainerExecCreate(ctx, contID,
469+
pgCommandConfig("pg_controldata", dataDir, pgVersion))
470+
471+
if err != nil {
472+
return errors.Wrap(err, "failed to create an exec command")
473+
}
474+
475+
log.Msg("Check initial configs")
476+
477+
attachResponse, err := r.dockerClient.ContainerExecAttach(ctx, initConfCmd.ID, types.ExecStartCheck{})
478+
if err != nil {
479+
return errors.Wrap(err, "failed to attach to the exec command")
480+
}
481+
482+
defer attachResponse.Close()
483+
484+
initParams, err := r.extractInitParams(ctx, attachResponse.Reader)
485+
if err != nil {
486+
return err
487+
}
488+
489+
return r.appendInitConfigs(initParams, dataDir)
490+
}
491+
492+
func (r *RestoreJob) extractInitParams(ctx context.Context, read io.Reader) (map[string]string, error) {
493+
extractedConfigs := make(map[string]string)
494+
scanner := bufio.NewScanner(read)
495+
496+
const settingSuffix = " setting:"
497+
498+
for scanner.Scan() {
499+
if ctx.Err() != nil {
500+
return extractedConfigs, ctx.Err()
501+
}
502+
503+
responseLine := scanner.Text()
504+
505+
for _, param := range originalParamsToRestore {
506+
extractedName := param + settingSuffix
507+
508+
if !strings.HasPrefix(responseLine, extractedName) {
509+
continue
510+
}
511+
512+
value := strings.TrimSpace(strings.TrimPrefix(responseLine, extractedName))
513+
514+
extractedConfigs[param] = value
515+
}
516+
517+
if len(originalParamsToRestore) == len(extractedConfigs) {
518+
break
519+
}
520+
}
521+
522+
return extractedConfigs, nil
523+
}
524+
525+
func (r *RestoreJob) appendInitConfigs(initConfiguration map[string]string, pgDataDir string) error {
526+
if len(initConfiguration) == 0 {
527+
return nil
528+
}
529+
530+
buffer := bytes.NewBuffer([]byte("\n"))
531+
532+
for key, value := range initConfiguration {
533+
buffer.WriteString(fmt.Sprintf("%s = '%s'\n", key, value))
534+
}
535+
536+
return r.appendConfigFile(path.Join(pgDataDir, "postgresql.conf"), buffer.Bytes())
537+
}
538+
539+
func (r *RestoreJob) appendConfigFile(file string, data []byte) error {
540+
configFile, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
450541
if err != nil {
451542
return err
452543
}
453544

454-
defer func() { _ = recoveryFile.Close() }()
545+
defer func() { _ = configFile.Close() }()
455546

456-
if _, err := recoveryFile.Write(r.restorer.GetRecoveryConfig()); err != nil {
547+
if _, err := configFile.Write(data); err != nil {
457548
return err
458549
}
459550

0 commit comments

Comments
 (0)