-
Notifications
You must be signed in to change notification settings - Fork 240
/
Copy pathaof.c
2961 lines (2642 loc) · 114 KB
/
aof.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "server.h"
#include "bio.h"
#include "rio.h"
#include "functions.h"
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/param.h>
/* redis7 的 aof 功能和之前版本的 aof 功能在文件管理方面有了很大的改变
* redis7 对 aof 文件进行了分类,分为以下 4 类文件(临时文件不用管)
* 1) manifest: 清单文件,该文件的内容对应 aofManifest 结构,记录所有其他类型文件的文件信息,并进行了分类
* 2) base: 基础文件,就是在执行 bgrewrite 的时候,当前数据的 RDB 快照或者 AOF,该文件的文件名后缀也是以 rdb 或者 aof 结尾,取决于 aof-use-rdb-preamble 配置项
* 3) incr: 增量命令文件,执行 bgrewrite 之后到的写命令,会存入该文件
* 4) history: 历史文件,执行 bgrewrite 之后,之前存在的 incr,base 文件就是历史文件,会通过 bio 关闭文件或者删除文件
*/
void freeClientArgv(client *c);
off_t getAppendOnlyFileSize(sds filename, int *status);
off_t getBaseAndIncrAppendOnlyFilesSize(aofManifest *am, int *status);
int getBaseAndIncrAppendOnlyFilesNum(aofManifest *am);
int aofFileExist(char *filename);
int rewriteAppendOnlyFile(char *filename);
aofManifest *aofLoadManifestFromFile(sds am_filepath);
void aofManifestFreeAndUpdate(aofManifest *am);
void aof_background_fsync_and_close(int fd);
/* ----------------------------------------------------------------------------
* AOF Manifest file implementation.
*
* The following code implements the read/write logic of AOF manifest file, which
* is used to track and manage all AOF files.
*
* Append-only files consist of three types:
*
* BASE: Represents a Redis snapshot from the time of last AOF rewrite. The manifest
* file contains at most a single BASE file, which will always be the first file in the
* list.
*
* INCR: Represents all write commands executed by Redis following the last successful
* AOF rewrite. In some cases it is possible to have several ordered INCR files. For
* example:
* - During an on-going AOF rewrite
* - After an AOF rewrite was aborted/failed, and before the next one succeeded.
*
* HISTORY: After a successful rewrite, the previous BASE and INCR become HISTORY files.
* They will be automatically removed unless garbage collection is disabled.
*
* The following is a possible AOF manifest file content:
*
* file appendonly.aof.2.base.rdb seq 2 type b
* file appendonly.aof.1.incr.aof seq 1 type h
* file appendonly.aof.2.incr.aof seq 2 type h
* file appendonly.aof.3.incr.aof seq 3 type h
* file appendonly.aof.4.incr.aof seq 4 type i
* file appendonly.aof.5.incr.aof seq 5 type i
* ------------------------------------------------------------------------- */
/* Naming rules. */
/* AOF 各种类型的文件命名规则 */
/* 基本文件,做 bgrewrite 的时候 redis 数据快照文件 */
#define BASE_FILE_SUFFIX ".base"
/* 增量命令文件,做 bgrewrite 之后到达的命令写入该文件 */
#define INCR_FILE_SUFFIX ".incr"
/* 使用 rdb 编码格式在 bgrewrite 的时候存储 base 文件的文件后缀标识 */
#define RDB_FORMAT_SUFFIX ".rdb"
/* 不使用 rdb 编码格式在 bgrewrite 的时候存储 base 文件的文件后缀标识,和 incr 文件使用的后缀标识 */
#define AOF_FORMAT_SUFFIX ".aof"
/* 清单文件后缀,该类型的文件会存储所有其他类型文件的文件名 */
#define MANIFEST_NAME_SUFFIX ".manifest"
/* 临时文件前缀 */
#define TEMP_FILE_NAME_PREFIX "temp-"
/* AOF manifest key. */
/* 下面三个宏在生成 aof 文件信息字符串的时候被使用 */
/* 文件名 */
#define AOF_MANIFEST_KEY_FILE_NAME "file"
/* 文件的序列号 */
#define AOF_MANIFEST_KEY_FILE_SEQ "seq"
/* 文件的类型 */
#define AOF_MANIFEST_KEY_FILE_TYPE "type"
/* Create an empty aofInfo. */
aofInfo *aofInfoCreate(void) {
return zcalloc(sizeof(aofInfo));
}
/* Free the aofInfo structure (pointed to by ai) and its embedded file_name. */
/* 释放 aofInfo 实例空间 */
void aofInfoFree(aofInfo *ai) {
serverAssert(ai != NULL);
/* 释放其文件名空间 */
if (ai->file_name) sdsfree(ai->file_name);
zfree(ai);
}
/* Deep copy an aofInfo. */
/* 深拷贝一份 aofInfo 实例 */
aofInfo *aofInfoDup(aofInfo *orig) {
serverAssert(orig != NULL);
aofInfo *ai = aofInfoCreate();
ai->file_name = sdsdup(orig->file_name);
ai->file_seq = orig->file_seq;
ai->file_type = orig->file_type;
return ai;
}
/* Format aofInfo as a string and it will be a line in the manifest. */
/* 将 aofInfo 实例格式化成字符串,
* 这个主要用于将内存中的清单信息写入文件的时候使用 */
sds aofInfoFormat(sds buf, aofInfo *ai) {
sds filename_repr = NULL;
if (sdsneedsrepr(ai->file_name))
filename_repr = sdscatrepr(sdsempty(), ai->file_name, sdslen(ai->file_name));
/* 格式:file xxx seq xxx type xxx */
sds ret = sdscatprintf(buf, "%s %s %s %lld %s %c\n",
AOF_MANIFEST_KEY_FILE_NAME, filename_repr ? filename_repr : ai->file_name,
AOF_MANIFEST_KEY_FILE_SEQ, ai->file_seq,
AOF_MANIFEST_KEY_FILE_TYPE, ai->file_type);
sdsfree(filename_repr);
return ret;
}
/* Method to free AOF list elements. */
/* 释放 aofInfo 列表中的一个元素 */
void aofListFree(void *item) {
aofInfo *ai = (aofInfo *)item;
aofInfoFree(ai);
}
/* Method to duplicate AOF list elements. */
void *aofListDup(void *item) {
return aofInfoDup(item);
}
/* Create an empty aofManifest, which will be called in `aofLoadManifestFromDisk`. */
/* 创建一个 aofManifest 结构 */
aofManifest *aofManifestCreate(void) {
aofManifest *am = zcalloc(sizeof(aofManifest));
am->incr_aof_list = listCreate();
am->history_aof_list = listCreate();
/* 给 incr aof 信息列表和历史 aof 文件列表绑定释放函数和复制函数 */
listSetFreeMethod(am->incr_aof_list, aofListFree);
listSetDupMethod(am->incr_aof_list, aofListDup);
listSetFreeMethod(am->history_aof_list, aofListFree);
listSetDupMethod(am->history_aof_list, aofListDup);
return am;
}
/* Free the aofManifest structure (pointed to by am) and its embedded members. */
/* 释放 aofManifest 实例 */
void aofManifestFree(aofManifest *am) {
if (am->base_aof_info) aofInfoFree(am->base_aof_info);
if (am->incr_aof_list) listRelease(am->incr_aof_list);
if (am->history_aof_list) listRelease(am->history_aof_list);
zfree(am);
}
/* 获取 AOF 清单文件名 appendonly.aof.manifest */
sds getAofManifestFileName() {
return sdscatprintf(sdsempty(), "%s%s", server.aof_filename,
MANIFEST_NAME_SUFFIX);
}
sds getTempAofManifestFileName() {
return sdscatprintf(sdsempty(), "%s%s%s", TEMP_FILE_NAME_PREFIX,
server.aof_filename, MANIFEST_NAME_SUFFIX);
}
/* Returns the string representation of aofManifest pointed to by am.
*
* The string is multiple lines separated by '\n', and each line represents
* an AOF file.
*
* Each line is space delimited and contains 6 fields, as follows:
* "file" [filename] "seq" [sequence] "type" [type]
*
* Where "file", "seq" and "type" are keywords that describe the next value,
* [filename] and [sequence] describe file name and order, and [type] is one
* of 'b' (base), 'h' (history) or 'i' (incr).
*
* The base file, if exists, will always be first, followed by history files,
* and incremental files.
*/
/* 将 aofManifest 清单格式化成字符串 */
sds getAofManifestAsString(aofManifest *am) {
serverAssert(am != NULL);
sds buf = sdsempty();
listNode *ln;
listIter li;
/* 1. Add BASE File information, it is always at the beginning
* of the manifest file. */
/* 格式化 base 文件信息,其实就是调用 aofInfoFormat 函数,后面两种文件信息也一样 */
if (am->base_aof_info) {
buf = aofInfoFormat(buf, am->base_aof_info);
}
/* 2. Add HISTORY type AOF information. */
listRewind(am->history_aof_list, &li);
while ((ln = listNext(&li)) != NULL) {
aofInfo *ai = (aofInfo*)ln->value;
buf = aofInfoFormat(buf, ai);
}
/* 3. Add INCR type AOF information. */
listRewind(am->incr_aof_list, &li);
while ((ln = listNext(&li)) != NULL) {
aofInfo *ai = (aofInfo*)ln->value;
buf = aofInfoFormat(buf, ai);
}
return buf;
}
/* Load the manifest information from the disk to `server.aof_manifest`
* when the Redis server start.
*
* During loading, this function does strict error checking and will abort
* the entire Redis server process on error (I/O error, invalid format, etc.)
*
* If the AOF directory or manifest file do not exist, this will be ignored
* in order to support seamless upgrades from previous versions which did not
* use them.
*/
/* 从磁盘中加载 aof 清单文件 */
void aofLoadManifestFromDisk(void) {
server.aof_manifest = aofManifestCreate();
if (!dirExists(server.aof_dirname)) {
serverLog(LL_DEBUG, "The AOF directory %s doesn't exist", server.aof_dirname);
return;
}
/* 获取 aof manifest 文件名 */
sds am_name = getAofManifestFileName();
/* 获取 aof manifest 文件路径 */
sds am_filepath = makePath(server.aof_dirname, am_name);
if (!fileExist(am_filepath)) {
serverLog(LL_DEBUG, "The AOF manifest file %s doesn't exist", am_name);
sdsfree(am_name);
sdsfree(am_filepath);
return;
}
/* 从磁盘中加载 aof manifest 文件,并根据其内容生成 aofManifest 结构 */
aofManifest *am = aofLoadManifestFromFile(am_filepath);
/* 释放之前的 aofManifest 结构实例,更改为现在的 am */
if (am) aofManifestFreeAndUpdate(am);
sdsfree(am_name);
sdsfree(am_filepath);
}
/* Generic manifest loading function, used in `aofLoadManifestFromDisk` and redis-check-aof tool. */
#define MANIFEST_MAX_LINE 1024
aofManifest *aofLoadManifestFromFile(sds am_filepath) {
const char *err = NULL;
long long maxseq = 0;
aofManifest *am = aofManifestCreate();
FILE *fp = fopen(am_filepath, "r");
if (fp == NULL) {
serverLog(LL_WARNING, "Fatal error: can't open the AOF manifest "
"file %s for reading: %s", am_filepath, strerror(errno));
exit(1);
}
char buf[MANIFEST_MAX_LINE+1];
sds *argv = NULL;
int argc;
aofInfo *ai = NULL;
sds line = NULL;
int linenum = 0;
while (1) {
if (fgets(buf, MANIFEST_MAX_LINE+1, fp) == NULL) {
if (feof(fp)) {
if (linenum == 0) {
err = "Found an empty AOF manifest";
goto loaderr;
} else {
break;
}
} else {
err = "Read AOF manifest failed";
goto loaderr;
}
}
linenum++;
/* Skip comments lines */
if (buf[0] == '#') continue;
if (strchr(buf, '\n') == NULL) {
err = "The AOF manifest file contains too long line";
goto loaderr;
}
line = sdstrim(sdsnew(buf), " \t\r\n");
if (!sdslen(line)) {
err = "Invalid AOF manifest file format";
goto loaderr;
}
argv = sdssplitargs(line, &argc);
/* 'argc < 6' was done for forward compatibility. */
if (argv == NULL || argc < 6 || (argc % 2)) {
err = "Invalid AOF manifest file format";
goto loaderr;
}
ai = aofInfoCreate();
for (int i = 0; i < argc; i += 2) {
if (!strcasecmp(argv[i], AOF_MANIFEST_KEY_FILE_NAME)) {
ai->file_name = sdsnew(argv[i+1]);
if (!pathIsBaseName(ai->file_name)) {
err = "File can't be a path, just a filename";
goto loaderr;
}
} else if (!strcasecmp(argv[i], AOF_MANIFEST_KEY_FILE_SEQ)) {
ai->file_seq = atoll(argv[i+1]);
} else if (!strcasecmp(argv[i], AOF_MANIFEST_KEY_FILE_TYPE)) {
ai->file_type = (argv[i+1])[0];
}
/* else if (!strcasecmp(argv[i], AOF_MANIFEST_KEY_OTHER)) {} */
}
/* We have to make sure we load all the information. */
if (!ai->file_name || !ai->file_seq || !ai->file_type) {
err = "Invalid AOF manifest file format";
goto loaderr;
}
sdsfreesplitres(argv, argc);
argv = NULL;
if (ai->file_type == AOF_FILE_TYPE_BASE) {
if (am->base_aof_info) {
err = "Found duplicate base file information";
goto loaderr;
}
am->base_aof_info = ai;
am->curr_base_file_seq = ai->file_seq;
} else if (ai->file_type == AOF_FILE_TYPE_HIST) {
listAddNodeTail(am->history_aof_list, ai);
} else if (ai->file_type == AOF_FILE_TYPE_INCR) {
if (ai->file_seq <= maxseq) {
err = "Found a non-monotonic sequence number";
goto loaderr;
}
listAddNodeTail(am->incr_aof_list, ai);
am->curr_incr_file_seq = ai->file_seq;
maxseq = ai->file_seq;
} else {
err = "Unknown AOF file type";
goto loaderr;
}
sdsfree(line);
line = NULL;
ai = NULL;
}
fclose(fp);
return am;
loaderr:
/* Sanitizer suppression: may report a false positive if we goto loaderr
* and exit(1) without freeing these allocations. */
if (argv) sdsfreesplitres(argv, argc);
if (ai) aofInfoFree(ai);
serverLog(LL_WARNING, "\n*** FATAL AOF MANIFEST FILE ERROR ***\n");
if (line) {
serverLog(LL_WARNING, "Reading the manifest file, at line %d\n", linenum);
serverLog(LL_WARNING, ">>> '%s'\n", line);
}
serverLog(LL_WARNING, "%s\n", err);
exit(1);
}
/* Deep copy an aofManifest from orig.
*
* In `backgroundRewriteDoneHandler` and `openNewIncrAofForAppend`, we will
* first deep copy a temporary AOF manifest from the `server.aof_manifest` and
* try to modify it. Once everything is modified, we will atomically make the
* `server.aof_manifest` point to this temporary aof_manifest.
*/
/* 复制给定的 aofManifest 实例,生成一个新的 aofManifest 实例 */
aofManifest *aofManifestDup(aofManifest *orig) {
serverAssert(orig != NULL);
aofManifest *am = zcalloc(sizeof(aofManifest));
am->curr_base_file_seq = orig->curr_base_file_seq;
am->curr_incr_file_seq = orig->curr_incr_file_seq;
am->dirty = orig->dirty;
if (orig->base_aof_info) {
am->base_aof_info = aofInfoDup(orig->base_aof_info);
}
am->incr_aof_list = listDup(orig->incr_aof_list);
am->history_aof_list = listDup(orig->history_aof_list);
serverAssert(am->incr_aof_list != NULL);
serverAssert(am->history_aof_list != NULL);
return am;
}
/* Change the `server.aof_manifest` pointer to 'am' and free the previous
* one if we have. */
/* 释放之前旧的 aofManifest 实例,使用新的 am 设置 server.aof_manifest 属性 */
void aofManifestFreeAndUpdate(aofManifest *am) {
serverAssert(am != NULL);
if (server.aof_manifest) aofManifestFree(server.aof_manifest);
server.aof_manifest = am;
}
/* Called in `backgroundRewriteDoneHandler` to get a new BASE file
* name, and mark the previous (if we have) BASE file as HISTORY type.
*
* BASE file naming rules: `server.aof_filename`.seq.base.format
*
* for example:
* appendonly.aof.1.base.aof (server.aof_use_rdb_preamble is no)
* appendonly.aof.1.base.rdb (server.aof_use_rdb_preamble is yes)
*/
/* 获取新的 base 文件名,并标记之前的 base 文件为历史文件
* 新的 base 文件名会根据 server.aof_use_rdb_preamble 来进行判断,该属性默认值是 1,认为是 rdb 这种就行了 */
sds getNewBaseFileNameAndMarkPreAsHistory(aofManifest *am) {
serverAssert(am != NULL);
if (am->base_aof_info) {
serverAssert(am->base_aof_info->file_type == AOF_FILE_TYPE_BASE);
/* 标记之前的 base 文件为历史类型 */
am->base_aof_info->file_type = AOF_FILE_TYPE_HIST;
listAddNodeHead(am->history_aof_list, am->base_aof_info);
}
/* 选择后缀是 .aof 还是 .rdb */
char *format_suffix = server.aof_use_rdb_preamble ?
RDB_FORMAT_SUFFIX:AOF_FORMAT_SUFFIX;
aofInfo *ai = aofInfoCreate();
/* 设置文件名 */
ai->file_name = sdscatprintf(sdsempty(), "%s.%lld%s%s", server.aof_filename,
++am->curr_base_file_seq, BASE_FILE_SUFFIX, format_suffix);
/* 设置序列号 */
ai->file_seq = am->curr_base_file_seq;
/* 设置文件类型 */
ai->file_type = AOF_FILE_TYPE_BASE;
am->base_aof_info = ai;
am->dirty = 1;
return am->base_aof_info->file_name;
}
/* Get a new INCR type AOF name.
*
* INCR AOF naming rules: `server.aof_filename`.seq.incr.aof
*
* for example:
* appendonly.aof.1.incr.aof
*/
/* 获取新的 incr aof 文件信息,添加进清单中,返回新的 incr aof 文件名 */
sds getNewIncrAofName(aofManifest *am) {
aofInfo *ai = aofInfoCreate();
ai->file_type = AOF_FILE_TYPE_INCR;
ai->file_name = sdscatprintf(sdsempty(), "%s.%lld%s%s", server.aof_filename,
++am->curr_incr_file_seq, INCR_FILE_SUFFIX, AOF_FORMAT_SUFFIX);
ai->file_seq = am->curr_incr_file_seq;
listAddNodeTail(am->incr_aof_list, ai);
am->dirty = 1;
return ai->file_name;
}
/* Get temp INCR type AOF name. */
/* 获取临时 incr 文件名 temp-xxx.incr */
sds getTempIncrAofName() {
return sdscatprintf(sdsempty(), "%s%s%s", TEMP_FILE_NAME_PREFIX, server.aof_filename,
INCR_FILE_SUFFIX);
}
/* Get the last INCR AOF name or create a new one. */
/* 获取最后的 incr 文件名,若不存在 incr 文件则创建一个 */
sds getLastIncrAofName(aofManifest *am) {
serverAssert(am != NULL);
/* If 'incr_aof_list' is empty, just create a new one. */
if (!listLength(am->incr_aof_list)) {
return getNewIncrAofName(am);
}
/* Or return the last one. */
listNode *lastnode = listIndex(am->incr_aof_list, -1);
aofInfo *ai = listNodeValue(lastnode);
return ai->file_name;
}
/* Called in `backgroundRewriteDoneHandler`. when AOFRW success, This
* function will change the AOF file type in 'incr_aof_list' from
* AOF_FILE_TYPE_INCR to AOF_FILE_TYPE_HIST, and move them to the
* 'history_aof_list'.
*/
/* 标记 incr aof 文件为历史文件 */
void markRewrittenIncrAofAsHistory(aofManifest *am) {
serverAssert(am != NULL);
if (!listLength(am->incr_aof_list)) {
return;
}
listNode *ln;
listIter li;
/* 根据清单信息中的 incr aof 文件信息列表从链表尾部向头部生成迭代器 */
listRewindTail(am->incr_aof_list, &li);
/* "server.aof_fd != -1" means AOF enabled, then we must skip the
* last AOF, because this file is our currently writing. */
/* 如果之前存在 incr aof 文件,需要忽略最后一个,因为它是当前写入的 incr aof */
if (server.aof_fd != -1) {
/* 获取尾结点的前一个结点 */
ln = listNext(&li);
serverAssert(ln != NULL);
}
/* Move aofInfo from 'incr_aof_list' to 'history_aof_list'. */
/* 从尾结点开始,如果上一个节点不为空 */
while ((ln = listNext(&li)) != NULL) {
/* 获取 incr aof 文件信息 */
aofInfo *ai = (aofInfo*)ln->value;
serverAssert(ai->file_type == AOF_FILE_TYPE_INCR);
/* 复制一份 incr aof 信息 */
aofInfo *hai = aofInfoDup(ai);
/* 设置文件类型为历史文件 */
hai->file_type = AOF_FILE_TYPE_HIST;
/* 将新复制的 aofInfo 实例加入历史文件列表 */
listAddNodeHead(am->history_aof_list, hai);
/* 从 incr aof 信息列表移除当前节点 */
listDelNode(am->incr_aof_list, ln);
}
/* 标记清单信息修改了,需要持久化 */
am->dirty = 1;
}
/* Write the formatted manifest string to disk. */
/* 把清单信息的格式化字符串写入清单文件中 */
int writeAofManifestFile(sds buf) {
int ret = C_OK;
ssize_t nwritten;
int len;
sds am_name = getAofManifestFileName();
sds am_filepath = makePath(server.aof_dirname, am_name);
sds tmp_am_name = getTempAofManifestFileName();
sds tmp_am_filepath = makePath(server.aof_dirname, tmp_am_name);
/* 打开临时文件 */
int fd = open(tmp_am_filepath, O_WRONLY|O_TRUNC|O_CREAT, 0644);
if (fd == -1) {
serverLog(LL_WARNING, "Can't open the AOF manifest file %s: %s",
tmp_am_name, strerror(errno));
ret = C_ERR;
goto cleanup;
}
/* 循环将清单字符串写入临时清单文件中 */
len = sdslen(buf);
while(len) {
nwritten = write(fd, buf, len);
if (nwritten < 0) {
if (errno == EINTR) continue;
serverLog(LL_WARNING, "Error trying to write the temporary AOF manifest file %s: %s",
tmp_am_name, strerror(errno));
ret = C_ERR;
goto cleanup;
}
len -= nwritten;
buf += nwritten;
}
/* 刷盘操作 */
if (redis_fsync(fd) == -1) {
serverLog(LL_WARNING, "Fail to fsync the temp AOF file %s: %s.",
tmp_am_name, strerror(errno));
ret = C_ERR;
goto cleanup;
}
/* 将临时文件重命名为新的清单文件 */
if (rename(tmp_am_filepath, am_filepath) != 0) {
serverLog(LL_WARNING,
"Error trying to rename the temporary AOF manifest file %s into %s: %s",
tmp_am_name, am_name, strerror(errno));
ret = C_ERR;
goto cleanup;
}
/* Also sync the AOF directory as new AOF files may be added in the directory */
/* 同时也对 AOF 目录执行刷盘操作 */
if (fsyncFileDir(am_filepath) == -1) {
serverLog(LL_WARNING, "Fail to fsync AOF directory %s: %s.",
am_filepath, strerror(errno));
ret = C_ERR;
goto cleanup;
}
cleanup:
if (fd != -1) close(fd);
sdsfree(am_name);
sdsfree(am_filepath);
sdsfree(tmp_am_name);
sdsfree(tmp_am_filepath);
return ret;
}
/* Persist the aofManifest information pointed to by am to disk. */
/* 将 aofManifest 实例持久化到磁盘中 */
int persistAofManifest(aofManifest *am) {
if (am->dirty == 0) {
return C_OK;
}
/* 将 aofManifest 清单实例格式化成字符串 */
sds amstr = getAofManifestAsString(am);
/* 将格式化后的字符串写入清单文件 */
int ret = writeAofManifestFile(amstr);
sdsfree(amstr);
if (ret == C_OK) am->dirty = 0;
return ret;
}
/* Called in `loadAppendOnlyFiles` when we upgrade from a old version redis.
*
* 1) Create AOF directory use 'server.aof_dirname' as the name.
* 2) Use 'server.aof_filename' to construct a BASE type aofInfo and add it to
* aofManifest, then persist the manifest file to AOF directory.
* 3) Move the old AOF file (server.aof_filename) to AOF directory.
*
* If any of the above steps fails or crash occurs, this will not cause any
* problems, and redis will retry the upgrade process when it restarts.
*/
/* 将旧 redis 版本的 aof 文件进行升级 */
void aofUpgradePrepare(aofManifest *am) {
serverAssert(!aofFileExist(server.aof_filename));
/* Create AOF directory use 'server.aof_dirname' as the name. */
/* 使用 'server.aof_dirname' 变量(默认值 appendonlydir)作为目录名创建 AOF 目录 */
if (dirCreateIfMissing(server.aof_dirname) == -1) {
serverLog(LL_WARNING, "Can't open or create append-only dir %s: %s",
server.aof_dirname, strerror(errno));
exit(1);
}
/* Manually construct a BASE type aofInfo and add it to aofManifest. */
/* 构造 Base 类型 aofInfo 并将其添加到 aofManifest */
if (am->base_aof_info) aofInfoFree(am->base_aof_info);
aofInfo *ai = aofInfoCreate();
ai->file_name = sdsnew(server.aof_filename);
ai->file_seq = 1;
ai->file_type = AOF_FILE_TYPE_BASE;
am->base_aof_info = ai;
am->curr_base_file_seq = 1;
am->dirty = 1;
/* Persist the manifest file to AOF directory. */
/* 持久化 AOF 清单文件 */
if (persistAofManifest(am) != C_OK) {
exit(1);
}
/* Move the old AOF file to AOF directory. */
/* 将旧 AOF 文件移动至 AOF 目录 */
sds aof_filepath = makePath(server.aof_dirname, server.aof_filename);
if (rename(server.aof_filename, aof_filepath) == -1) {
serverLog(LL_WARNING,
"Error trying to move the old AOF file %s into dir %s: %s",
server.aof_filename,
server.aof_dirname,
strerror(errno));
sdsfree(aof_filepath);
exit(1);
}
sdsfree(aof_filepath);
serverLog(LL_NOTICE, "Successfully migrated an old-style AOF file (%s) into the AOF directory (%s).",
server.aof_filename, server.aof_dirname);
}
/* When AOFRW success, the previous BASE and INCR AOFs will
* become HISTORY type and be moved into 'history_aof_list'.
*
* The function will traverse the 'history_aof_list' and submit
* the delete task to the bio thread.
*/
/* 该方法是删除内存中历史文件信息,然后提交一个关闭历史文件的任务给 bio 线程 */
int aofDelHistoryFiles(void) {
if (server.aof_manifest == NULL ||
server.aof_disable_auto_gc == 1 ||
!listLength(server.aof_manifest->history_aof_list))
{
return C_OK;
}
listNode *ln;
listIter li;
/* 遍历清单中所有历史文件信息 */
listRewind(server.aof_manifest->history_aof_list, &li);
while ((ln = listNext(&li)) != NULL) {
aofInfo *ai = (aofInfo*)ln->value;
serverAssert(ai->file_type == AOF_FILE_TYPE_HIST);
serverLog(LL_NOTICE, "Removing the history file %s in the background", ai->file_name);
sds aof_filepath = makePath(server.aof_dirname, ai->file_name);
/* 提交关闭文件的任务 */
bg_unlink(aof_filepath);
sdsfree(aof_filepath);
/* 删除列表中的节点 */
listDelNode(server.aof_manifest->history_aof_list, ln);
}
/* 标记清单改变了 */
server.aof_manifest->dirty = 1;
/* 持久化 AOF 清单文件 */
return persistAofManifest(server.aof_manifest);
}
/* Used to clean up temp INCR AOF when AOFRW fails. */
/* 关闭临时文件 */
void aofDelTempIncrAofFile() {
sds aof_filename = getTempIncrAofName();
sds aof_filepath = makePath(server.aof_dirname, aof_filename);
serverLog(LL_NOTICE, "Removing the temp incr aof file %s in the background", aof_filename);
bg_unlink(aof_filepath);
sdsfree(aof_filepath);
sdsfree(aof_filename);
return;
}
/* Called after `loadDataFromDisk` when redis start. If `server.aof_state` is
* 'AOF_ON', It will do three things:
* 1. Force create a BASE file when redis starts with an empty dataset
* 2. Open the last opened INCR type AOF for writing, If not, create a new one
* 3. Synchronously update the manifest file to the disk
*
* If any of the above steps fails, the redis process will exit.
*/
/* 开启 AOF 模式时打开 AOF 文件,不存在则创建,在服务器启动时调用 */
void aofOpenIfNeededOnServerStart(void) {
if (server.aof_state != AOF_ON) {
return;
}
serverAssert(server.aof_manifest != NULL);
serverAssert(server.aof_fd == -1);
if (dirCreateIfMissing(server.aof_dirname) == -1) {
serverLog(LL_WARNING, "Can't open or create append-only dir %s: %s",
server.aof_dirname, strerror(errno));
exit(1);
}
/* If we start with an empty dataset, we will force create a BASE file. */
size_t incr_aof_len = listLength(server.aof_manifest->incr_aof_list);
if (!server.aof_manifest->base_aof_info && !incr_aof_len) {
sds base_name = getNewBaseFileNameAndMarkPreAsHistory(server.aof_manifest);
sds base_filepath = makePath(server.aof_dirname, base_name);
if (rewriteAppendOnlyFile(base_filepath) != C_OK) {
exit(1);
}
sdsfree(base_filepath);
serverLog(LL_NOTICE, "Creating AOF base file %s on server start",
base_name);
}
/* Because we will 'exit(1)' if open AOF or persistent manifest fails, so
* we don't need atomic modification here. */
sds aof_name = getLastIncrAofName(server.aof_manifest);
/* Here we should use 'O_APPEND' flag. */
sds aof_filepath = makePath(server.aof_dirname, aof_name);
server.aof_fd = open(aof_filepath, O_WRONLY|O_APPEND|O_CREAT, 0644);
sdsfree(aof_filepath);
if (server.aof_fd == -1) {
serverLog(LL_WARNING, "Can't open the append-only file %s: %s",
aof_name, strerror(errno));
exit(1);
}
/* Persist our changes. */
int ret = persistAofManifest(server.aof_manifest);
if (ret != C_OK) {
exit(1);
}
server.aof_last_incr_size = getAppendOnlyFileSize(aof_name, NULL);
if (incr_aof_len) {
serverLog(LL_NOTICE, "Opening AOF incr file %s on server start", aof_name);
} else {
serverLog(LL_NOTICE, "Creating AOF incr file %s on server start", aof_name);
}
}
int aofFileExist(char *filename) {
sds file_path = makePath(server.aof_dirname, filename);
int ret = fileExist(file_path);
sdsfree(file_path);
return ret;
}
/* Called in `rewriteAppendOnlyFileBackground`. If `server.aof_state`
* is 'AOF_ON', It will do two things:
* 1. Open a new INCR type AOF for writing
* 2. Synchronously update the manifest file to the disk
*
* The above two steps of modification are atomic, that is, if
* any step fails, the entire operation will rollback and returns
* C_ERR, and if all succeeds, it returns C_OK.
*
* If `server.aof_state` is 'AOF_WAIT_REWRITE', It will open a temporary INCR AOF
* file to accumulate data during AOF_WAIT_REWRITE, and it will eventually be
* renamed in the `backgroundRewriteDoneHandler` and written to the manifest file.
* */
/* 打开一个新的 aof 文件 */
/* 若执行时 aof 正在进行重写,则会先创建临时 incr 文件并用于在 aof 重写期间记录数据 */
/* 最后临时文件会在 'backgroundRewriteDoneHandler' 函数中重命名并写入清单文件 */
int openNewIncrAofForAppend(void) {
serverAssert(server.aof_manifest != NULL);
int newfd = -1;
aofManifest *temp_am = NULL;
sds new_aof_name = NULL;
/* Only open new INCR AOF when AOF enabled. */
if (server.aof_state == AOF_OFF) return C_OK;
/* Open new AOF. */
if (server.aof_state == AOF_WAIT_REWRITE) {
/* Use a temporary INCR AOF file to accumulate data during AOF_WAIT_REWRITE. */
new_aof_name = getTempIncrAofName();
} else {
/* Dup a temp aof_manifest to modify. */
temp_am = aofManifestDup(server.aof_manifest);
new_aof_name = sdsdup(getNewIncrAofName(temp_am));
}
sds new_aof_filepath = makePath(server.aof_dirname, new_aof_name);
newfd = open(new_aof_filepath, O_WRONLY|O_TRUNC|O_CREAT, 0644);
sdsfree(new_aof_filepath);
if (newfd == -1) {
serverLog(LL_WARNING, "Can't open the append-only file %s: %s",
new_aof_name, strerror(errno));
goto cleanup;
}
if (temp_am) {
/* Persist AOF Manifest. */
if (persistAofManifest(temp_am) == C_ERR) {
goto cleanup;
}
}
serverLog(LL_NOTICE, "Creating AOF incr file %s on background rewrite",
new_aof_name);
sdsfree(new_aof_name);
/* If reaches here, we can safely modify the `server.aof_manifest`
* and `server.aof_fd`. */
/* fsync and close old aof_fd if needed. In fsync everysec it's ok to delay
* the fsync as long as we grantee it happens, and in fsync always the file
* is already synced at this point so fsync doesn't matter. */
if (server.aof_fd != -1) {
aof_background_fsync_and_close(server.aof_fd);
server.aof_fsync_offset = server.aof_current_size;
server.aof_last_fsync = server.unixtime;
}
server.aof_fd = newfd;
/* Reset the aof_last_incr_size. */
server.aof_last_incr_size = 0;
/* Update `server.aof_manifest`. */
if (temp_am) aofManifestFreeAndUpdate(temp_am);
return C_OK;
cleanup:
if (new_aof_name) sdsfree(new_aof_name);
if (newfd != -1) close(newfd);
if (temp_am) aofManifestFree(temp_am);
return C_ERR;
}
/* Whether to limit the execution of Background AOF rewrite.
*
* At present, if AOFRW fails, redis will automatically retry. If it continues
* to fail, we may get a lot of very small INCR files. so we need an AOFRW
* limiting measure.
*
* We can't directly use `server.aof_current_size` and `server.aof_last_incr_size`,
* because there may be no new writes after AOFRW fails.
*
* So, we use time delay to achieve our goal. When AOFRW fails, we delay the execution
* of the next AOFRW by 1 minute. If the next AOFRW also fails, it will be delayed by 2
* minutes. The next is 4, 8, 16, the maximum delay is 60 minutes (1 hour).
*
* During the limit period, we can still use the 'bgrewriteaof' command to execute AOFRW
* immediately.
*
* Return 1 means that AOFRW is limited and cannot be executed. 0 means that we can execute
* AOFRW, which may be that we have reached the 'next_rewrite_time' or the number of INCR
* AOFs has not reached the limit threshold.
* */
/* 是否限制了执行 AOFRW
*
* 当前,如果 AOFRW 失败,redis 将会自动重试 AOFRW,如果重试后还是一直失败,将会存在大量的非常小的
* incr 文件,因此需要采取措施限制 AOFRW 执行
*
* 为了解决上面的问题,使用了时间限制。当 AOFRW 失败,将会延迟 1 分钟再执行下一次 AOFRW,如果之后还
* 失败,就延迟 2 分钟,接着是 4,8,16,.... 最大是 60 分钟
*
* 在 AOFRW 限制期间,我们仍然可以使用 bgrewriteaof 命令主动并且立即触发 AOFRW
*
* 该函数返回 1 表示 AOFRW 被限制了,不能执行,如果是 0 则可以执行 AOFRW
*/
/* 开始限制的阈值,即 AOFRW 连续失败 3 次,就开始进行限制 */
#define AOF_REWRITE_LIMITE_THRESHOLD 3
/* AOFRW 限制的最大延迟时间是 60 分钟 */
#define AOF_REWRITE_LIMITE_MAX_MINUTES 60 /* 1 hour */
int aofRewriteLimited(void) {
static int next_delay_minutes = 0;
static time_t next_rewrite_time = 0;
/* 如果 AOFRW 连续失败次数小于 3 次,可以执行 AOFRW */
if (server.stat_aofrw_consecutive_failures < AOF_REWRITE_LIMITE_THRESHOLD) {
/* We may be recovering from limited state, so reset all states. */
next_delay_minutes = 0;
next_rewrite_time = 0;
return 0;
}
/* if it is in the limiting state, then check if the next_rewrite_time is reached */
if (next_rewrite_time != 0) {
/* 如果当前时间还没超过指定的限制时间,不能执行 AOFRW */
if (server.unixtime < next_rewrite_time) {
return 1;
} else {
/* 超过了,将 next_rewrite_time 置 0,并且返回 0 */
next_rewrite_time = 0;
return 0;
}
}
/* 随着连续失败次数的增加,需要调整限制时间,每次增长之前的两倍时间,知道达到最大时间限制 */
next_delay_minutes = (next_delay_minutes == 0) ? 1 : (next_delay_minutes * 2);
if (next_delay_minutes > AOF_REWRITE_LIMITE_MAX_MINUTES) {