-
Notifications
You must be signed in to change notification settings - Fork 433
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add daemon mode for wal-push command #1353
Changes from 16 commits
375724c
73dc6f5
ba10323
f1cd865
2d1f34c
97fcd78
4b83717
7a9c2d8
b716637
ee98bdd
ba3a1ad
4da5e24
0d7f569
7f05253
bcff731
ec6b490
1f1af79
2c117fa
6ffc97c
766a6e2
6bfd42d
6fedfec
e14af01
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package pg | ||
|
||
import ( | ||
"os" | ||
"path" | ||
|
||
"github.com/spf13/cobra" | ||
"github.com/wal-g/tracelog" | ||
"github.com/wal-g/wal-g/internal" | ||
"github.com/wal-g/wal-g/internal/asm" | ||
"github.com/wal-g/wal-g/internal/databases/postgres" | ||
"github.com/wal-g/wal-g/utility" | ||
) | ||
|
||
const DaemonShortDescription = "Uploads a WAL file to storage" | ||
|
||
// daemonCmd represents the daemon archive command | ||
var daemonCmd = &cobra.Command{ | ||
Use: "daemon daemon_socket_path", | ||
Short: DaemonShortDescription, // TODO : improve description | ||
Args: cobra.ExactArgs(1), | ||
Run: func(cmd *cobra.Command, args []string) { | ||
rogaliiik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
uploader, err := postgres.ConfigureWalUploader() | ||
tracelog.ErrorLogger.FatalOnError(err) | ||
|
||
archiveStatusManager, err := internal.ConfigureArchiveStatusManager() | ||
if err == nil { | ||
uploader.ArchiveStatusManager = asm.NewDataFolderASM(archiveStatusManager) | ||
} else { | ||
tracelog.ErrorLogger.PrintError(err) | ||
uploader.ArchiveStatusManager = asm.NewNopASM() | ||
} | ||
|
||
PGArchiveStatusManager, err := internal.ConfigurePGArchiveStatusManager() | ||
if err == nil { | ||
uploader.PGArchiveStatusManager = asm.NewDataFolderASM(PGArchiveStatusManager) | ||
} else { | ||
tracelog.ErrorLogger.PrintError(err) | ||
uploader.PGArchiveStatusManager = asm.NewNopASM() | ||
} | ||
|
||
uploader.UploadingFolder = uploader.UploadingFolder.GetSubFolder(utility.WalPath) | ||
pathToData, exist := os.LookupEnv("PGDATA") | ||
rogaliiik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if !exist { | ||
tracelog.ErrorLogger.Fatal("'PGDATA' variable is not defined or does not exist") | ||
} | ||
pathToWal := path.Join(pathToData, "pg_wal") | ||
|
||
postgres.HandleDaemon(uploader, args[0], pathToWal) | ||
}, | ||
} | ||
|
||
func init() { | ||
Cmd.AddCommand(daemonCmd) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#!/bin/sh | ||
set -e -x | ||
rogaliiik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
CONFIG_FILE="/tmp/configs/wal_perftest_config.json" | ||
|
||
COMMON_CONFIG="/tmp/configs/common_config.json" | ||
TMP_CONFIG="/tmp/configs/tmp_config.json" | ||
cat ${CONFIG_FILE} > ${TMP_CONFIG} | ||
|
||
echo "," >> ${TMP_CONFIG} | ||
cat ${COMMON_CONFIG} >> ${TMP_CONFIG} | ||
/tmp/scripts/wrap_config_file.sh ${TMP_CONFIG} | ||
|
||
WAL=$(ls -l ${PGDATA}/pg_wal | head -n2 | tail -n1 | egrep -o "[0-9A-F]{24}") | ||
|
||
wal-g --config=${TMP_CONFIG} daemon "${PGDATA}"/pg_wal | ||
|
||
# shellcheck disable=SC2039 | ||
nc -U /tmp/wal-push.sock <<< 'CHECK' | ||
sleep 1 | ||
rogaliiik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# shellcheck disable=SC2039 | ||
nc -U /tmp/wal-push.sock <<< "${WAL}" | ||
sleep 1 | ||
|
||
wal-g st ls WALG_S3_PREFIX/wal_005/"${WAL}".br | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package postgres | ||
|
||
import ( | ||
"bytes" | ||
"encoding/binary" | ||
"errors" | ||
"fmt" | ||
"net" | ||
"os" | ||
"path" | ||
|
||
"github.com/wal-g/tracelog" | ||
) | ||
|
||
type CheckType byte | ||
rogaliiik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
type FileType byte | ||
|
||
type MessageSocketHandler interface { | ||
Handle(messageBody []byte, c net.Conn, f func(string) error) error | ||
} | ||
|
||
func MessageTypeConstruct(messageType byte) MessageSocketHandler { | ||
switch messageType { | ||
case 'C': | ||
return CheckType('C') | ||
case 'F': | ||
return FileType('F') | ||
default: | ||
return nil | ||
} | ||
} | ||
|
||
func (msg CheckType) Handle(messageBody []byte, c net.Conn, f func(string) error) error { | ||
rogaliiik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
_, err := c.Write([]byte{'O', 0, 3}) | ||
if err != nil { | ||
tracelog.ErrorLogger.Printf("Error on writing in socket: %v", err) | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func (msg FileType) Handle(messageBody []byte, c net.Conn, f func(string) error) error { | ||
rogaliiik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if len(messageBody) < 24 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, we archive not only WALs but other files with length other than 24. I think we can remove this validation. |
||
if len(messageBody) > 0 { | ||
tracelog.ErrorLogger.Printf("Received incorrect message %s", messageBody) | ||
rogaliiik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else { | ||
tracelog.ErrorLogger.Println("Received empty message") | ||
} | ||
return errors.New(fmt.Sprint("Incorrect message accepted")) | ||
} | ||
tracelog.InfoLogger.Printf("wal file name: %s\n", messageBody) | ||
err := f(string(messageBody)) | ||
if err != nil { | ||
tracelog.ErrorLogger.Printf("wal-push failed: %v\n", err) | ||
return err | ||
} | ||
_, err = c.Write([]byte{'O', 0, 3}) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
// HandleDaemon is invoked to perform daemon mode | ||
func HandleDaemon(uploader *WalUploader, pathToSocket string, pathToWal string) { | ||
_ = os.Remove(pathToSocket) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do not ignore the errors |
||
l, err := net.Listen("unix", pathToSocket) | ||
if err != nil { | ||
tracelog.ErrorLogger.Fatal("Error on listening socket:", err) | ||
} | ||
for { | ||
fd, err := l.Accept() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if we would want to terminate the running daemon? I suggest to add the signal listener and listend for |
||
if err != nil { | ||
tracelog.ErrorLogger.Println("Failed to accept, err:", err) | ||
} | ||
go DaemonProcess(fd, func(walFileName string) error { | ||
fullPath := path.Join(pathToWal, walFileName) | ||
tracelog.InfoLogger.Printf("starting wal-push for %s\n", fullPath) | ||
return HandleWALPush(uploader, fullPath) | ||
}) | ||
} | ||
} | ||
|
||
func DaemonProcess(c net.Conn, f func(string) error) { | ||
defer func(c net.Conn) { | ||
err := c.Close() | ||
rogaliiik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
tracelog.ErrorLogger.Printf("Failed to close connection with %s, err: %v\n", c.RemoteAddr(), err) | ||
} | ||
}(c) | ||
buf := make([]byte, 512) | ||
for { | ||
nr, err := c.Read(buf) | ||
if err != nil { | ||
tracelog.ErrorLogger.Printf("Failed to read checking message from client %s, err: %v\n", c.RemoteAddr(), err) | ||
_, _ = c.Write([]byte{'E', 0, 3}) | ||
return | ||
} | ||
|
||
byteType, byteLength, byteBody := MessageParser(buf) | ||
requestType := MessageTypeConstruct(byteType) | ||
err = requestType.Handle(byteBody, c, f) | ||
if err != nil { | ||
tracelog.ErrorLogger.Println("Failed to handle message, err:", err) | ||
} | ||
if byteType == 'F' { | ||
return | ||
} | ||
if int(byteLength) < nr { | ||
tracelog.InfoLogger.Println("Read remaining buffer...") | ||
byteType, byteLength, byteBody = MessageParser(buf[byteLength:]) | ||
rogaliiik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
err = requestType.Handle(byteBody, c, f) | ||
if err != nil { | ||
tracelog.ErrorLogger.Println("Failed to handle message, err:", err) | ||
} | ||
return | ||
} | ||
} | ||
} | ||
|
||
// MessageParser is invoked to read bytes from buffer | ||
func MessageParser(buf []byte) (byte, uint16, []byte) { | ||
messageType := buf[0] | ||
var messageLength uint16 | ||
l := bytes.NewReader(buf[1:3]) | ||
err := binary.Read(l, binary.BigEndian, &messageLength) | ||
if err != nil { | ||
tracelog.ErrorLogger.Printf("Failed to read message length, err: %v", err) | ||
} | ||
messageBody := buf[3 : messageLength-1] | ||
return messageType, messageLength, messageBody | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add some information about this command to the documentation.