42
42
sprintf(error, "0x%16llx: %s", (long long)epos, __buf); \
43
43
}
44
44
45
+ // 保存错误信息
45
46
static char error [1024 ];
47
+
48
+ // 文件读取的当前偏移量
46
49
static off_t epos ;
47
50
51
+ /*
52
+ * 确认 buf 是以 \r\n 结尾的新行。
53
+ *
54
+ * 确认成功返回 1 ,读入失败返回 0 ,并打印错误信息。
55
+ */
48
56
int consumeNewline (char * buf ) {
49
57
if (strncmp (buf ,"\r\n" ,2 )!= 0 ) {
50
58
ERROR ("Expected \\r\\n, got: %02x%02x" ,buf [0 ],buf [1 ]);
@@ -53,32 +61,62 @@ int consumeNewline(char *buf) {
53
61
return 1 ;
54
62
}
55
63
64
+ /*
65
+ * 从 fp 中读入一个以 prefix 为前缀的 long 值,并将它保存到 *target 中。
66
+ *
67
+ * 读入成功返回 1 ,读入出错返回 0 ,并打印错误信息。
68
+ */
56
69
int readLong (FILE * fp ,char prefix ,long * target ) {
57
70
char buf [128 ],* eptr ;
71
+
58
72
epos = ftello (fp );
73
+
74
+ // 读入行
59
75
if (fgets (buf ,sizeof (buf ),fp )== NULL ) {
60
76
return 0 ;
61
77
}
78
+
79
+ // 确保前缀相同
62
80
if (buf [0 ]!= prefix ) {
63
81
ERROR ("Expected prefix '%c', got: '%c'" ,buf [0 ],prefix );
64
82
return 0 ;
65
83
}
84
+
85
+ // 将字符串转换成 long 值
66
86
* target = strtol (buf + 1 ,& eptr ,10 );
87
+
67
88
return consumeNewline (eptr );
68
89
}
69
90
91
+ /*
92
+ * 从 fp 中读取指定的字节,并将值保存到 *target 中。
93
+ *
94
+ * 如果读取的量和 length 参数不相同,那么返回 0 ,并打印错误信息。
95
+ * 读取成功则返回 1 。
96
+ */
70
97
int readBytes (FILE * fp ,char * target ,long length ) {
71
98
long real ;
99
+
72
100
epos = ftello (fp );
101
+
73
102
real = fread (target ,1 ,length ,fp );
74
103
if (real != length ) {
75
104
ERROR ("Expected to read %ld bytes, got %ld bytes" ,length ,real );
76
105
return 0 ;
77
106
}
107
+
78
108
return 1 ;
79
109
}
80
110
111
+ /*
112
+ * 读取字符串
113
+ *
114
+ * 读取成功函数返回 1 ,并将值保存在 target 指针中。
115
+ * 失败返回 0 。
116
+ */
81
117
int readString (FILE * fp ,char * * target ) {
118
+
119
+ // 读取字符串的长度
82
120
long len ;
83
121
* target = NULL ;
84
122
if (!readLong (fp ,'$' ,& len )) {
@@ -87,69 +125,117 @@ int readString(FILE *fp, char** target) {
87
125
88
126
/* Increase length to also consume \r\n */
89
127
len += 2 ;
128
+
129
+ // 为字符串分配空间
90
130
* target = (char * )malloc (len );
131
+
132
+ // 读取内容
91
133
if (!readBytes (fp ,* target ,len )) {
92
134
return 0 ;
93
135
}
136
+
137
+ // 确认 \r\n
94
138
if (!consumeNewline (* target + len - 2 )) {
95
139
return 0 ;
96
140
}
141
+
97
142
(* target )[len - 2 ]= '\0' ;
143
+
98
144
return 1 ;
99
145
}
100
146
147
+ /*
148
+ * 读取参数数量
149
+ *
150
+ * 读取成功函数返回 1 ,并将参数数量保存到 target 中。
151
+ * 读取失败返回 0 。
152
+ */
101
153
int readArgc (FILE * fp ,long * target ) {
102
154
return readLong (fp ,'*' ,target );
103
155
}
104
156
157
+ /*
158
+ * 返回一个偏移量,这个偏移量可能是:
159
+ *
160
+ * 1)文件的末尾
161
+ * 2)文件首次出现读入错误的地方
162
+ * 3)文件第一个没有 EXEC 匹配的 MULTI 的位置
163
+ */
105
164
off_t process (FILE * fp ) {
106
165
long argc ;
107
166
off_t pos = 0 ;
108
167
int i ,multi = 0 ;
109
168
char * str ;
110
169
111
170
while (1 ) {
171
+
172
+ // 定位到最后一个 MULTI 出现的偏移量
112
173
if (!multi )pos = ftello (fp );
174
+
175
+ // 读取参数的个数
113
176
if (!readArgc (fp ,& argc ))break ;
114
177
178
+ // 遍历各个参数
179
+ // 参数包括命令以及命令参数
180
+ // 比如 SET key value
181
+ // SET 就是第一个参数,而 key 和 value 就是第二和第三个参数
115
182
for (i = 0 ;i < argc ;i ++ ) {
183
+
184
+ // 读取参数
116
185
if (!readString (fp ,& str ))break ;
186
+
187
+ // 检查命令是否 MULTI 或者 EXEC
117
188
if (i == 0 ) {
118
189
if (strcasecmp (str ,"multi" )== 0 ) {
190
+ // 记录一个 MULTI
191
+ // 如果前面已经有一个 MULTI ,那么报错(MULTI 不应该嵌套)
119
192
if (multi ++ ) {
120
193
ERROR ("Unexpected MULTI" );
121
194
break ;
122
195
}
123
196
}else if (strcasecmp (str ,"exec" )== 0 ) {
197
+ // 清除一个 MULTI 记录
198
+ // 如果前面没有 MULTI ,那么报错(MULTI 和 EXEC 应该一对对出现)
124
199
if (-- multi ) {
125
200
ERROR ("Unexpected EXEC" );
126
201
break ;
127
202
}
128
203
}
129
204
}
205
+
206
+ // 释放
130
207
free (str );
131
208
}
132
209
133
- /* Stop if the loop did not finish */
210
+ /* Stop if the loop did not finish
211
+ *
212
+ * 如果 for 循环没有正常结束,那么跳出 while
213
+ */
134
214
if (i < argc ) {
135
215
if (str )free (str );
136
216
break ;
137
217
}
138
218
}
139
219
220
+ // 文件读取完了,但是没有找到和 MULTI 对应的 EXEC
140
221
if (feof (fp )&& multi && strlen (error )== 0 ) {
141
222
ERROR ("Reached EOF before reading EXEC for MULTI" );
142
223
}
224
+
225
+ // 如果有错误出现,那么打印错误
143
226
if (strlen (error )> 0 ) {
144
227
printf ("%s\n" ,error );
145
228
}
229
+
230
+ // 返回偏移量
146
231
return pos ;
147
232
}
148
233
149
234
int main (int argc ,char * * argv ) {
150
235
char * filename ;
151
236
int fix = 0 ;
152
237
238
+ // 选项,如果不带 --fix 就只检查,不进行修复
153
239
if (argc < 2 ) {
154
240
printf ("Usage: %s [--fix] <file.aof>\n" ,argv [0 ]);
155
241
exit (1 );
@@ -167,30 +253,45 @@ int main(int argc, char **argv) {
167
253
exit (1 );
168
254
}
169
255
256
+ // 打开指定文件
170
257
FILE * fp = fopen (filename ,"r+" );
171
258
if (fp == NULL ) {
172
259
printf ("Cannot open file: %s\n" ,filename );
173
260
exit (1 );
174
261
}
175
262
263
+ // 读取文件信息
176
264
struct redis_stat sb ;
177
265
if (redis_fstat (fileno (fp ),& sb )== -1 ) {
178
266
printf ("Cannot stat file: %s\n" ,filename );
179
267
exit (1 );
180
268
}
181
269
270
+ // 取出文件的大小
182
271
off_t size = sb .st_size ;
183
272
if (size == 0 ) {
184
273
printf ("Empty file: %s\n" ,filename );
185
274
exit (1 );
186
275
}
187
276
277
+ // 如果文件出错,那么这个偏移量指向:
278
+ // 1) 第一个不符合格式的位置
279
+ // 2) 第一个没有 EXEC 对应的 MULTI 的位置
280
+ // 如果文件没有出错,那么这个偏移量指向:
281
+ // 3) 文件末尾
188
282
off_t pos = process (fp );
283
+ // 计算偏移量距离文件末尾有多远
189
284
off_t diff = size - pos ;
190
285
printf ("AOF analyzed: size=%lld, ok_up_to=%lld, diff=%lld\n" ,
191
286
(long long )size , (long long )pos , (long long )diff );
287
+
288
+ // 大于 0 表示未到达文件末尾,出错
192
289
if (diff > 0 ) {
290
+
291
+ // fix 模式:尝试修复文件
193
292
if (fix ) {
293
+
294
+ // 尝试从出错的位置开始,一直删除到文件的末尾
194
295
char buf [2 ];
195
296
printf ("This will shrink the AOF from %lld bytes, with %lld bytes, to %lld bytes\n" ,(long long )size ,(long long )diff ,(long long )pos );
196
297
printf ("Continue? [y/N]: " );
@@ -199,20 +300,28 @@ int main(int argc, char **argv) {
199
300
printf ("Aborting...\n" );
200
301
exit (1 );
201
302
}
303
+
304
+ // 删除不正确的内容
202
305
if (ftruncate (fileno (fp ),pos )== -1 ) {
203
306
printf ("Failed to truncate AOF\n" );
204
307
exit (1 );
205
308
}else {
206
309
printf ("Successfully truncated AOF\n" );
207
310
}
311
+
312
+ // 非 fix 模式:只报告文件不合法
208
313
}else {
209
314
printf ("AOF is not valid\n" );
210
315
exit (1 );
211
316
}
317
+
318
+ // 等于 0 表示文件已经顺利读完,无错
212
319
}else {
213
320
printf ("AOF is valid\n" );
214
321
}
215
322
323
+ // 关闭文件
216
324
fclose (fp );
325
+
217
326
return 0 ;
218
327
}