Skip to content

Commit d98d3ec

Browse files
committed
fix: always use absolute paths for WAL upload (#45)
While pgbackrest can work with relative ones, such setup requires additional config flags and a matching Postgresql working directory configuration, which seems to be invalid in the context of the plugin's container. Signed-off-by: Szymon Soloch <[email protected]>
1 parent c5df2b9 commit d98d3ec

File tree

2 files changed

+56
-38
lines changed

2 files changed

+56
-38
lines changed

internal/pgbackrest/archiver/command.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ import (
3737
// `requestedWALFile` is the name of the file whose archiving was requested by
3838
// PostgreSQL, and that file is always the first of the list and is always included.
3939
// `parallel` is the maximum number of WALs that we can archive in parallel
40+
// It's important to ensure this method returns absolute paths. While pgbackrest can
41+
// work with relative ones, such setup requires additional config flags and a matching
42+
// Postgresql working directory configuration, which seems to be invalid in the context
43+
// of the plugin's container.
4044
func (archiver *WALArchiver) GatherWALFilesToArchive(
4145
ctx context.Context,
4246
requestedWALFile string,
@@ -57,7 +61,10 @@ func (archiver *WALArchiver) GatherWALFilesToArchive(
5761
// slightly more optimized, but equivalent to:
5862
// walList = []string{requestedWALFile}
5963
walList = make([]string, 1, walListLength)
60-
walList[0] = requestedWALFile
64+
// Ensure it's an absolute path. While Postgres should be configured to use absolute
65+
// paths in the archive command, its documentation mentions that even in this mode
66+
// a relative path might be used in some cases.
67+
walList[0] = filepath.Join(pgWalDirectory, filepath.Base(requestedWALFile))
6168

6269
err := filepath.WalkDir(archiveStatusPath, func(path string, d os.DirEntry, err error) error {
6370
// If err is set, it means the current path is a directory and the readdir raised an error
@@ -96,7 +103,7 @@ func (archiver *WALArchiver) GatherWALFilesToArchive(
96103
return nil
97104
}
98105

99-
walList = append(walList, filepath.Join("pg_wal", walFileName))
106+
walList = append(walList, filepath.Join(pgWalDirectory, walFileName))
100107
return nil
101108
})
102109

internal/pgbackrest/archiver/command_test.go

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -111,96 +111,112 @@ var _ = Describe("GatherWALFilesToArchive", func() {
111111
os.Unsetenv("PGDATA")
112112
})
113113

114-
// Helper function to create .ready files
115-
createReadyFile := func(walName string) {
114+
// Helper function to create .ready files and return the absolute path to the file.
115+
// Pgbackrest preffers absolute paths for uploaded files. For relative ones
116+
// additional flags must be passed and Postgres config must match those flags.
117+
// For some reason it doesn't so we ensure only absolute paths are returned.
118+
createReadyFile := func(walName string) string {
116119
path := filepath.Join(archiveStatusDir, walName+".ready")
117120
err := os.WriteFile(path, []byte{}, 0644)
118121
Expect(err).ToNot(HaveOccurred())
122+
return filepath.Join(tempPgData, "pg_wal", walName)
119123
}
120124

121125
Context("when parallel=1", func() {
122126
It("should gather only the requested file", func(ctx SpecContext) {
123127
// Create .ready files for multiple WAL files
124-
createReadyFile("000000010000000000000001")
128+
wal1 := createReadyFile("000000010000000000000001")
125129
createReadyFile("000000010000000000000002")
126130
createReadyFile("000000010000000000000003")
127131

128132
walList := archiver.GatherWALFilesToArchive(ctx, "pg_wal/000000010000000000000001", 1)
129133

130-
Expect(walList).To(ConsistOf("pg_wal/000000010000000000000001"))
134+
Expect(walList).To(ConsistOf(wal1))
135+
})
136+
137+
It("should handle an absolute path as input", func(ctx SpecContext) {
138+
// Create .ready files for multiple WAL files
139+
wal1 := createReadyFile("000000010000000000000001")
140+
createReadyFile("000000010000000000000002")
141+
createReadyFile("000000010000000000000003")
142+
143+
walList := archiver.GatherWALFilesToArchive(ctx, filepath.Join(tempPgData, "pg_wal", "000000010000000000000001"), 1)
144+
145+
Expect(walList).To(ConsistOf(wal1))
131146
})
132147

133148
It("should handle when no other .ready files exist", func(ctx SpecContext) {
134149
// Only create the requested file
135-
createReadyFile("000000010000000000000001")
150+
wal1 := createReadyFile("000000010000000000000001")
136151

137152
walList := archiver.GatherWALFilesToArchive(ctx, "pg_wal/000000010000000000000001", 1)
138153

139-
Expect(walList).To(ConsistOf("pg_wal/000000010000000000000001"))
154+
Expect(walList).To(ConsistOf(wal1))
140155
})
141156
})
142157

143158
Context("when parallel>1", func() {
144159
It("should gather multiple files when parallel=4", func(ctx SpecContext) {
145160
// Create .ready files for multiple WAL files
146-
createReadyFile("000000010000000000000001")
147-
createReadyFile("000000010000000000000002")
148-
createReadyFile("000000010000000000000003")
149-
createReadyFile("000000010000000000000004")
161+
walFiles := []any{
162+
createReadyFile("000000010000000000000001"),
163+
createReadyFile("000000010000000000000002"),
164+
createReadyFile("000000010000000000000003"),
165+
createReadyFile("000000010000000000000004"),
166+
}
150167

151168
walList := archiver.GatherWALFilesToArchive(ctx, "pg_wal/000000010000000000000001", 4)
152169

153-
Expect(walList).To(ConsistOf(
154-
"pg_wal/000000010000000000000001",
155-
"pg_wal/000000010000000000000002",
156-
"pg_wal/000000010000000000000003",
157-
"pg_wal/000000010000000000000004",
158-
))
170+
Expect(walList).To(ConsistOf(walFiles...))
159171
})
160172

161173
It("should not exceed parallel limit even when more files are ready", func(ctx SpecContext) {
174+
requested := createReadyFile("000000010000000000000001")
162175
// Create many .ready files
163-
for i := 1; i <= 10; i++ {
176+
for i := 2; i <= 10; i++ {
164177
createReadyFile("00000001000000000000000" + string(rune('0'+i)))
165178
}
166179

167180
walList := archiver.GatherWALFilesToArchive(ctx, "pg_wal/000000010000000000000001", 3)
168181

169182
Expect(walList).To(HaveLen(3))
183+
Expect(walList).To(ContainElement(requested))
170184
})
171185

172186
It("should handle when fewer files exist than parallel limit", func(ctx SpecContext) {
173187
// Create only 2 .ready files but request parallel=5
174-
createReadyFile("000000010000000000000001")
175-
createReadyFile("000000010000000000000002")
188+
walFiles := []any{
189+
createReadyFile("000000010000000000000001"),
190+
createReadyFile("000000010000000000000002"),
191+
}
176192

177193
walList := archiver.GatherWALFilesToArchive(ctx, "pg_wal/000000010000000000000001", 5)
178194

179195
// Should only get the files that exist
180-
Expect(walList).To(ConsistOf(
181-
"pg_wal/000000010000000000000001",
182-
"pg_wal/000000010000000000000002",
183-
))
196+
Expect(walList).To(ConsistOf(walFiles...))
184197
})
185198
})
186199

187200
Context("edge cases", func() {
188201
It("should handle empty archive_status directory", func(ctx SpecContext) {
189202
// Don't create any .ready files
203+
expectedWal := filepath.Join(tempPgData, "pg_wal", "000000010000000000000001")
190204

191205
walList := archiver.GatherWALFilesToArchive(ctx, "pg_wal/000000010000000000000001", 3)
192206

193207
// Should still return the requested file
194-
Expect(walList).To(ConsistOf("pg_wal/000000010000000000000001"))
208+
Expect(walList).To(ConsistOf(expectedWal))
195209
})
196210

197211
})
198212

199213
Context("other files in directory", func() {
200214
It("should ignore non-.ready files in archive_status", func(ctx SpecContext) {
201215
// Create .ready files
202-
createReadyFile("000000010000000000000001")
203-
createReadyFile("000000010000000000000002")
216+
walFiles := []any{
217+
createReadyFile("000000010000000000000001"),
218+
createReadyFile("000000010000000000000002"),
219+
}
204220

205221
// Create .done files (should be ignored)
206222
donePath := filepath.Join(archiveStatusDir, "000000010000000000000003.done")
@@ -215,22 +231,17 @@ var _ = Describe("GatherWALFilesToArchive", func() {
215231
walList := archiver.GatherWALFilesToArchive(ctx, "pg_wal/000000010000000000000001", 5)
216232

217233
// Should only get .ready files, not .done or other files
218-
Expect(walList).To(ConsistOf(
219-
"pg_wal/000000010000000000000001",
220-
"pg_wal/000000010000000000000002",
221-
))
234+
Expect(walList).To(ConsistOf(walFiles...))
222235
})
236+
223237
It("should handle timeline history files", func(ctx SpecContext) {
224238
// Create timeline history file
225-
createReadyFile("00000002.history")
226-
createReadyFile("000000010000000000000001")
239+
history := createReadyFile("00000002.history")
240+
wal1 := createReadyFile("000000010000000000000001")
227241

228242
walList := archiver.GatherWALFilesToArchive(ctx, "pg_wal/00000002.history", 2)
229243

230-
Expect(walList).To(ConsistOf(
231-
"pg_wal/00000002.history",
232-
"pg_wal/000000010000000000000001",
233-
))
244+
Expect(walList).To(ConsistOf(history, wal1))
234245
})
235246
})
236247
})

0 commit comments

Comments
 (0)