14 // Reads w->dir for files matching binlog.NNN,
15 // sets w->next to the next unused number, and
16 // returns the minimum number.
17 // If no files are found, sets w->next to 1 and
18 // returns a large number.
22 static char base
[] = "binlog.";
23 static const int len
= sizeof(base
) - 1;
34 while ((e
= readdir(d
))) {
35 if (strncmp(e
->d_name
, base
, len
) == 0) {
36 n
= strtol(e
->d_name
+len
, &p
, 10);
37 if (p
&& *p
== '\0') {
55 while (w
->head
&& !w
->head
->refs
) {
59 w
->tail
= f
->next
; // also, f->next == NULL
69 // returns 1 on success, 0 on error.
77 twarnx("there is no next wal file");
93 if (w
->wantsync
&& now
>= w
->lastsync
+w
->syncrate
) {
95 if (fsync(w
->cur
->fd
) == -1) {
102 // Walwrite writes j to the log w (if w is enabled).
103 // On failure, walwrite disables w and returns 0; on success, it returns 1.
104 // Unlke walresv*, walwrite should never fail because of a full disk.
105 // If w is disabled, then walwrite takes no action and returns 1.
107 walwrite(Wal
*w
, job j
)
111 if (!w
->use
) return 1;
112 if (w
->cur
->resv
> 0 || usenext(w
)) {
114 r
= filewrjobshort(w
->cur
, j
);
116 r
= filewrjobfull(w
->cur
, j
);
141 if (!fileinit(f
, w
, w
->next
)) {
161 moveresv(File
*to
, File
*from
, int n
)
171 needfree(Wal
*w
, int n
)
173 if (w
->tail
->free
>= n
) return n
;
174 if (makenextfile(w
)) return n
;
180 // 1. b->resv is congruent to n (mod z).
181 // 2. x->resv is congruent to 0 (mod z) for each future file x.
182 // Assumes (and preserves) that b->resv >= n.
183 // Reserved space is conserved (neither created nor destroyed);
184 // we just move it around to preserve the invariant.
185 // We might have to allocate a new file.
186 // Returns 1 on success, otherwise 0. If there was a failure,
187 // w->tail is not updated.
189 balancerest(Wal
*w
, File
*b
, int n
)
192 static const int z
= sizeof(int) + sizeof(Jobrec
);
198 if (r
== 0) return balancerest(w
, b
->next
, 0);
201 if (w
->tail
->resv
>= c
&& b
->free
>= c
) {
202 moveresv(b
, w
->tail
, c
);
203 return balancerest(w
, b
->next
, 0);
206 if (needfree(w
, r
) != r
) {
210 moveresv(w
->tail
, b
, r
);
211 return balancerest(w
, b
->next
, 0);
216 // 1. w->cur->resv >= n.
217 // 2. w->cur->resv is congruent to n (mod z).
218 // 3. x->resv is congruent to 0 (mod z) for each future file x.
219 // (where z is the size of a delete record in the wal).
220 // Reserved space is conserved (neither created nor destroyed);
221 // we just move it around to preserve the invariant.
222 // We might have to allocate a new file.
223 // Returns 1 on success, otherwise 0. If there was a failure,
224 // w->tail is not updated.
226 balance(Wal
*w
, int n
)
231 // (this loop will run at most once)
232 while (w
->cur
->resv
< n
) {
233 int m
= w
->cur
->resv
;
241 moveresv(w
->tail
, w
->cur
, m
);
245 // Invariants 2 and 3
246 return balancerest(w
, w
->cur
, n
);
250 // Returns the number of bytes successfully reserved: either 0 or n.
252 reserve(Wal
*w
, int n
)
256 // return value must be nonzero but is otherwise ignored
257 if (!w
->use
) return 1;
259 if (w
->cur
->free
>= n
) {
273 if (!balance(w
, n
)) {
274 // error; undo the reservation
284 // Returns the number of bytes reserved or 0 on error.
286 walresvput(Wal
*w
, job j
)
290 // reserve space for the initial job record
292 z
+= strlen(j
->tube
->name
);
296 // plus space for a delete to come later
300 return reserve(w
, z
);
304 // Returns the number of bytes reserved or 0 on error.
306 walresvupdate(Wal
*w
, job j
)
312 return reserve(w
, z
);
316 // Returns the number of locks acquired: either 0 or 1.
325 r
= snprintf(path
, PATH_MAX
, "%s/lock", w
->dir
);
327 twarnx("path too long: %s/lock", w
->dir
);
331 fd
= open(path
, O_WRONLY
|O_CREAT
, 0600);
338 lk
.l_whence
= SEEK_SET
;
341 r
= fcntl(fd
, F_SETLK
, &lk
);
347 // intentionally leak fd, since we never want to close it
348 // and we'll never need it again
354 walread(Wal
*w
, job list
, int min
, int max
)
360 for (i
= min
; i
< w
->next
; i
++) {
367 if (!fileinit(f
, w
, i
)) {
373 fd
= open(f
->path
, O_RDONLY
);
375 twarn("%s", f
->path
);
383 err
|= fileread(f
, list
);
388 warnx("Errors reading one or more WAL files.");
389 warnx("Continuing. You may be missing data.");
395 walinit(Wal
*w
, job list
)
400 walread(w
, list
, min
, w
->next
);
402 // first writable file
403 if (!makenextfile(w
)) {
404 twarnx("makenextfile");