]> git.saurik.com Git - redis.git/blob - src/replication.c
more replication info in logs
[redis.git] / src / replication.c
1 #include "redis.h"
2
3 #include <sys/time.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <sys/stat.h>
7
8 /* ---------------------------------- MASTER -------------------------------- */
9
10 void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
11 listNode *ln;
12 listIter li;
13 int outc = 0, j;
14 robj **outv;
15 /* We need 1+(ARGS*3) objects since commands are using the new protocol
16 * and we one 1 object for the first "*<count>\r\n" multibulk count, then
17 * for every additional object we have "$<count>\r\n" + object + "\r\n". */
18 robj *static_outv[REDIS_STATIC_ARGS*3+1];
19 robj *lenobj;
20
21 if (argc <= REDIS_STATIC_ARGS) {
22 outv = static_outv;
23 } else {
24 outv = zmalloc(sizeof(robj*)*(argc*3+1));
25 }
26
27 lenobj = createObject(REDIS_STRING,
28 sdscatprintf(sdsempty(), "*%d\r\n", argc));
29 lenobj->refcount = 0;
30 outv[outc++] = lenobj;
31 for (j = 0; j < argc; j++) {
32 lenobj = createObject(REDIS_STRING,
33 sdscatprintf(sdsempty(),"$%lu\r\n",
34 (unsigned long) stringObjectLen(argv[j])));
35 lenobj->refcount = 0;
36 outv[outc++] = lenobj;
37 outv[outc++] = argv[j];
38 outv[outc++] = shared.crlf;
39 }
40
41 /* Increment all the refcounts at start and decrement at end in order to
42 * be sure to free objects if there is no slave in a replication state
43 * able to be feed with commands */
44 for (j = 0; j < outc; j++) incrRefCount(outv[j]);
45 listRewind(slaves,&li);
46 while((ln = listNext(&li))) {
47 redisClient *slave = ln->value;
48
49 /* Don't feed slaves that are still waiting for BGSAVE to start */
50 if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) continue;
51
52 /* Feed all the other slaves, MONITORs and so on */
53 if (slave->slaveseldb != dictid) {
54 robj *selectcmd;
55
56 switch(dictid) {
57 case 0: selectcmd = shared.select0; break;
58 case 1: selectcmd = shared.select1; break;
59 case 2: selectcmd = shared.select2; break;
60 case 3: selectcmd = shared.select3; break;
61 case 4: selectcmd = shared.select4; break;
62 case 5: selectcmd = shared.select5; break;
63 case 6: selectcmd = shared.select6; break;
64 case 7: selectcmd = shared.select7; break;
65 case 8: selectcmd = shared.select8; break;
66 case 9: selectcmd = shared.select9; break;
67 default:
68 selectcmd = createObject(REDIS_STRING,
69 sdscatprintf(sdsempty(),"select %d\r\n",dictid));
70 selectcmd->refcount = 0;
71 break;
72 }
73 addReply(slave,selectcmd);
74 slave->slaveseldb = dictid;
75 }
76 for (j = 0; j < outc; j++) addReply(slave,outv[j]);
77 }
78 for (j = 0; j < outc; j++) decrRefCount(outv[j]);
79 if (outv != static_outv) zfree(outv);
80 }
81
82 void replicationFeedMonitors(list *monitors, int dictid, robj **argv, int argc) {
83 listNode *ln;
84 listIter li;
85 int j;
86 sds cmdrepr = sdsnew("+");
87 robj *cmdobj;
88 struct timeval tv;
89
90 gettimeofday(&tv,NULL);
91 cmdrepr = sdscatprintf(cmdrepr,"%ld.%ld ",(long)tv.tv_sec,(long)tv.tv_usec);
92 if (dictid != 0) cmdrepr = sdscatprintf(cmdrepr,"(db %d) ", dictid);
93
94 for (j = 0; j < argc; j++) {
95 if (argv[j]->encoding == REDIS_ENCODING_INT) {
96 cmdrepr = sdscatprintf(cmdrepr, "\"%ld\"", (long)argv[j]->ptr);
97 } else {
98 cmdrepr = sdscatrepr(cmdrepr,(char*)argv[j]->ptr,
99 sdslen(argv[j]->ptr));
100 }
101 if (j != argc-1)
102 cmdrepr = sdscatlen(cmdrepr," ",1);
103 }
104 cmdrepr = sdscatlen(cmdrepr,"\r\n",2);
105 cmdobj = createObject(REDIS_STRING,cmdrepr);
106
107 listRewind(monitors,&li);
108 while((ln = listNext(&li))) {
109 redisClient *monitor = ln->value;
110 addReply(monitor,cmdobj);
111 }
112 decrRefCount(cmdobj);
113 }
114
115 void syncCommand(redisClient *c) {
116 /* ignore SYNC if aleady slave or in monitor mode */
117 if (c->flags & REDIS_SLAVE) return;
118
119 /* Refuse SYNC requests if we are a slave but the link with our master
120 * is not ok... */
121 if (server.masterhost && server.replstate != REDIS_REPL_CONNECTED) {
122 addReplyError(c,"Can't SYNC while not connected with my master");
123 return;
124 }
125
126 /* SYNC can't be issued when the server has pending data to send to
127 * the client about already issued commands. We need a fresh reply
128 * buffer registering the differences between the BGSAVE and the current
129 * dataset, so that we can copy to other slaves if needed. */
130 if (listLength(c->reply) != 0) {
131 addReplyError(c,"SYNC is invalid with pending input");
132 return;
133 }
134
135 redisLog(REDIS_NOTICE,"Slave ask for synchronization");
136 /* Here we need to check if there is a background saving operation
137 * in progress, or if it is required to start one */
138 if (server.bgsavechildpid != -1) {
139 /* Ok a background save is in progress. Let's check if it is a good
140 * one for replication, i.e. if there is another slave that is
141 * registering differences since the server forked to save */
142 redisClient *slave;
143 listNode *ln;
144 listIter li;
145
146 listRewind(server.slaves,&li);
147 while((ln = listNext(&li))) {
148 slave = ln->value;
149 if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) break;
150 }
151 if (ln) {
152 /* Perfect, the server is already registering differences for
153 * another slave. Set the right state, and copy the buffer. */
154 listRelease(c->reply);
155 c->reply = listDup(slave->reply);
156 c->replstate = REDIS_REPL_WAIT_BGSAVE_END;
157 redisLog(REDIS_NOTICE,"Waiting for end of BGSAVE for SYNC");
158 } else {
159 /* No way, we need to wait for the next BGSAVE in order to
160 * register differences */
161 c->replstate = REDIS_REPL_WAIT_BGSAVE_START;
162 redisLog(REDIS_NOTICE,"Waiting for next BGSAVE for SYNC");
163 }
164 } else {
165 /* Ok we don't have a BGSAVE in progress, let's start one */
166 redisLog(REDIS_NOTICE,"Starting BGSAVE for SYNC");
167 if (rdbSaveBackground(server.dbfilename) != REDIS_OK) {
168 redisLog(REDIS_NOTICE,"Replication failed, can't BGSAVE");
169 addReplyError(c,"Unable to perform background save");
170 return;
171 }
172 c->replstate = REDIS_REPL_WAIT_BGSAVE_END;
173 }
174 c->repldbfd = -1;
175 c->flags |= REDIS_SLAVE;
176 c->slaveseldb = 0;
177 listAddNodeTail(server.slaves,c);
178 return;
179 }
180
181 void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
182 redisClient *slave = privdata;
183 REDIS_NOTUSED(el);
184 REDIS_NOTUSED(mask);
185 char buf[REDIS_IOBUF_LEN];
186 ssize_t nwritten, buflen;
187
188 if (slave->repldboff == 0) {
189 /* Write the bulk write count before to transfer the DB. In theory here
190 * we don't know how much room there is in the output buffer of the
191 * socket, but in pratice SO_SNDLOWAT (the minimum count for output
192 * operations) will never be smaller than the few bytes we need. */
193 sds bulkcount;
194
195 bulkcount = sdscatprintf(sdsempty(),"$%lld\r\n",(unsigned long long)
196 slave->repldbsize);
197 if (write(fd,bulkcount,sdslen(bulkcount)) != (signed)sdslen(bulkcount))
198 {
199 sdsfree(bulkcount);
200 freeClient(slave);
201 return;
202 }
203 sdsfree(bulkcount);
204 }
205 lseek(slave->repldbfd,slave->repldboff,SEEK_SET);
206 buflen = read(slave->repldbfd,buf,REDIS_IOBUF_LEN);
207 if (buflen <= 0) {
208 redisLog(REDIS_WARNING,"Read error sending DB to slave: %s",
209 (buflen == 0) ? "premature EOF" : strerror(errno));
210 freeClient(slave);
211 return;
212 }
213 if ((nwritten = write(fd,buf,buflen)) == -1) {
214 redisLog(REDIS_VERBOSE,"Write error sending DB to slave: %s",
215 strerror(errno));
216 freeClient(slave);
217 return;
218 }
219 slave->repldboff += nwritten;
220 if (slave->repldboff == slave->repldbsize) {
221 close(slave->repldbfd);
222 slave->repldbfd = -1;
223 aeDeleteFileEvent(server.el,slave->fd,AE_WRITABLE);
224 slave->replstate = REDIS_REPL_ONLINE;
225 if (aeCreateFileEvent(server.el, slave->fd, AE_WRITABLE,
226 sendReplyToClient, slave) == AE_ERR) {
227 freeClient(slave);
228 return;
229 }
230 addReplySds(slave,sdsempty());
231 redisLog(REDIS_NOTICE,"Synchronization with slave succeeded");
232 }
233 }
234
235 /* This function is called at the end of every backgrond saving.
236 * The argument bgsaveerr is REDIS_OK if the background saving succeeded
237 * otherwise REDIS_ERR is passed to the function.
238 *
239 * The goal of this function is to handle slaves waiting for a successful
240 * background saving in order to perform non-blocking synchronization. */
241 void updateSlavesWaitingBgsave(int bgsaveerr) {
242 listNode *ln;
243 int startbgsave = 0;
244 listIter li;
245
246 listRewind(server.slaves,&li);
247 while((ln = listNext(&li))) {
248 redisClient *slave = ln->value;
249
250 if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) {
251 startbgsave = 1;
252 slave->replstate = REDIS_REPL_WAIT_BGSAVE_END;
253 } else if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) {
254 struct redis_stat buf;
255
256 if (bgsaveerr != REDIS_OK) {
257 freeClient(slave);
258 redisLog(REDIS_WARNING,"SYNC failed. BGSAVE child returned an error");
259 continue;
260 }
261 if ((slave->repldbfd = open(server.dbfilename,O_RDONLY)) == -1 ||
262 redis_fstat(slave->repldbfd,&buf) == -1) {
263 freeClient(slave);
264 redisLog(REDIS_WARNING,"SYNC failed. Can't open/stat DB after BGSAVE: %s", strerror(errno));
265 continue;
266 }
267 slave->repldboff = 0;
268 slave->repldbsize = buf.st_size;
269 slave->replstate = REDIS_REPL_SEND_BULK;
270 aeDeleteFileEvent(server.el,slave->fd,AE_WRITABLE);
271 if (aeCreateFileEvent(server.el, slave->fd, AE_WRITABLE, sendBulkToSlave, slave) == AE_ERR) {
272 freeClient(slave);
273 continue;
274 }
275 }
276 }
277 if (startbgsave) {
278 if (rdbSaveBackground(server.dbfilename) != REDIS_OK) {
279 listIter li;
280
281 listRewind(server.slaves,&li);
282 redisLog(REDIS_WARNING,"SYNC failed. BGSAVE failed");
283 while((ln = listNext(&li))) {
284 redisClient *slave = ln->value;
285
286 if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START)
287 freeClient(slave);
288 }
289 }
290 }
291 }
292
293 /* ----------------------------------- SLAVE -------------------------------- */
294
295 /* Abort the async download of the bulk dataset while SYNC-ing with master */
296 void replicationAbortSyncTransfer(void) {
297 redisAssert(server.replstate == REDIS_REPL_TRANSFER);
298
299 aeDeleteFileEvent(server.el,server.repl_transfer_s,AE_READABLE);
300 close(server.repl_transfer_s);
301 close(server.repl_transfer_fd);
302 unlink(server.repl_transfer_tmpfile);
303 zfree(server.repl_transfer_tmpfile);
304 server.replstate = REDIS_REPL_CONNECT;
305 }
306
307 /* Asynchronously read the SYNC payload we receive from a master */
308 void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
309 char buf[4096];
310 ssize_t nread, readlen;
311 REDIS_NOTUSED(el);
312 REDIS_NOTUSED(privdata);
313 REDIS_NOTUSED(mask);
314
315 /* If repl_transfer_left == -1 we still have to read the bulk length
316 * from the master reply. */
317 if (server.repl_transfer_left == -1) {
318 if (syncReadLine(fd,buf,1024,3600) == -1) {
319 redisLog(REDIS_WARNING,
320 "I/O error reading bulk count from MASTER: %s",
321 strerror(errno));
322 replicationAbortSyncTransfer();
323 return;
324 }
325 if (buf[0] == '-') {
326 redisLog(REDIS_WARNING,
327 "MASTER aborted replication with an error: %s",
328 buf+1);
329 replicationAbortSyncTransfer();
330 return;
331 } else if (buf[0] != '$') {
332 redisLog(REDIS_WARNING,"Bad protocol from MASTER, the first byte is not '$', are you sure the host and port are right?");
333 replicationAbortSyncTransfer();
334 return;
335 }
336 server.repl_transfer_left = strtol(buf+1,NULL,10);
337 redisLog(REDIS_NOTICE,
338 "MASTER <-> SLAVE sync: receiving %ld bytes from master",
339 server.repl_transfer_left);
340 return;
341 }
342
343 /* Read bulk data */
344 readlen = (server.repl_transfer_left < (signed)sizeof(buf)) ?
345 server.repl_transfer_left : (signed)sizeof(buf);
346 nread = read(fd,buf,readlen);
347 if (nread <= 0) {
348 redisLog(REDIS_WARNING,"I/O error trying to sync with MASTER: %s",
349 (nread == -1) ? strerror(errno) : "connection lost");
350 replicationAbortSyncTransfer();
351 return;
352 }
353 server.repl_transfer_lastio = time(NULL);
354 if (write(server.repl_transfer_fd,buf,nread) != nread) {
355 redisLog(REDIS_WARNING,"Write error or short write writing to the DB dump file needed for MASTER <-> SLAVE synchrnonization: %s", strerror(errno));
356 replicationAbortSyncTransfer();
357 return;
358 }
359 server.repl_transfer_left -= nread;
360 /* Check if the transfer is now complete */
361 if (server.repl_transfer_left == 0) {
362 if (rename(server.repl_transfer_tmpfile,server.dbfilename) == -1) {
363 redisLog(REDIS_WARNING,"Failed trying to rename the temp DB into dump.rdb in MASTER <-> SLAVE synchronization: %s", strerror(errno));
364 replicationAbortSyncTransfer();
365 return;
366 }
367 redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Loading DB in memory");
368 emptyDb();
369 if (rdbLoad(server.dbfilename) != REDIS_OK) {
370 redisLog(REDIS_WARNING,"Failed trying to load the MASTER synchronization DB from disk");
371 replicationAbortSyncTransfer();
372 return;
373 }
374 /* Final setup of the connected slave <- master link */
375 aeDeleteFileEvent(server.el,server.repl_transfer_s,AE_READABLE);
376 zfree(server.repl_transfer_tmpfile);
377 close(server.repl_transfer_fd);
378 server.master = createClient(server.repl_transfer_s);
379 server.master->flags |= REDIS_MASTER;
380 server.master->authenticated = 1;
381 server.replstate = REDIS_REPL_CONNECTED;
382 redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Finished with success");
383 }
384 }
385
386 int syncWithMaster(void) {
387 char buf[1024], tmpfile[256], authcmd[1024];
388 int fd = anetTcpConnect(NULL,server.masterhost,server.masterport);
389 int dfd, maxtries = 5;
390
391 if (fd == -1) {
392 redisLog(REDIS_WARNING,"Unable to connect to MASTER: %s",
393 strerror(errno));
394 return REDIS_ERR;
395 }
396
397 /* AUTH with the master if required. */
398 if(server.masterauth) {
399 snprintf(authcmd, 1024, "AUTH %s\r\n", server.masterauth);
400 if (syncWrite(fd, authcmd, strlen(server.masterauth)+7, 5) == -1) {
401 close(fd);
402 redisLog(REDIS_WARNING,"Unable to AUTH to MASTER: %s",
403 strerror(errno));
404 return REDIS_ERR;
405 }
406 /* Read the AUTH result. */
407 if (syncReadLine(fd,buf,1024,3600) == -1) {
408 close(fd);
409 redisLog(REDIS_WARNING,"I/O error reading auth result from MASTER: %s",
410 strerror(errno));
411 return REDIS_ERR;
412 }
413 if (buf[0] != '+') {
414 close(fd);
415 redisLog(REDIS_WARNING,"Cannot AUTH to MASTER, is the masterauth password correct?");
416 return REDIS_ERR;
417 }
418 }
419
420 /* Issue the SYNC command */
421 if (syncWrite(fd,"SYNC \r\n",7,5) == -1) {
422 close(fd);
423 redisLog(REDIS_WARNING,"I/O error writing to MASTER: %s",
424 strerror(errno));
425 return REDIS_ERR;
426 }
427
428 /* Prepare a suitable temp file for bulk transfer */
429 while(maxtries--) {
430 snprintf(tmpfile,256,
431 "temp-%d.%ld.rdb",(int)time(NULL),(long int)getpid());
432 dfd = open(tmpfile,O_CREAT|O_WRONLY|O_EXCL,0644);
433 if (dfd != -1) break;
434 sleep(1);
435 }
436 if (dfd == -1) {
437 close(fd);
438 redisLog(REDIS_WARNING,"Opening the temp file needed for MASTER <-> SLAVE synchronization: %s",strerror(errno));
439 return REDIS_ERR;
440 }
441
442 /* Setup the non blocking download of the bulk file. */
443 if (aeCreateFileEvent(server.el, fd, AE_READABLE, readSyncBulkPayload, NULL)
444 == AE_ERR)
445 {
446 close(fd);
447 redisLog(REDIS_WARNING,"Can't create readable event for SYNC");
448 return REDIS_ERR;
449 }
450 server.replstate = REDIS_REPL_TRANSFER;
451 server.repl_transfer_left = -1;
452 server.repl_transfer_s = fd;
453 server.repl_transfer_fd = dfd;
454 server.repl_transfer_lastio = time(NULL);
455 server.repl_transfer_tmpfile = zstrdup(tmpfile);
456 return REDIS_OK;
457 }
458
459 void slaveofCommand(redisClient *c) {
460 if (!strcasecmp(c->argv[1]->ptr,"no") &&
461 !strcasecmp(c->argv[2]->ptr,"one")) {
462 if (server.masterhost) {
463 sdsfree(server.masterhost);
464 server.masterhost = NULL;
465 if (server.master) freeClient(server.master);
466 if (server.replstate == REDIS_REPL_TRANSFER)
467 replicationAbortSyncTransfer();
468 server.replstate = REDIS_REPL_NONE;
469 redisLog(REDIS_NOTICE,"MASTER MODE enabled (user request)");
470 }
471 } else {
472 sdsfree(server.masterhost);
473 server.masterhost = sdsdup(c->argv[1]->ptr);
474 server.masterport = atoi(c->argv[2]->ptr);
475 if (server.master) freeClient(server.master);
476 if (server.replstate == REDIS_REPL_TRANSFER)
477 replicationAbortSyncTransfer();
478 server.replstate = REDIS_REPL_CONNECT;
479 redisLog(REDIS_NOTICE,"SLAVE OF %s:%d enabled (user request)",
480 server.masterhost, server.masterport);
481 }
482 addReply(c,shared.ok);
483 }
484
485 /* --------------------------- REPLICATION CRON ---------------------------- */
486
487 #define REDIS_REPL_TRANSFER_TIMEOUT 60
488
489 void replicationCron(void) {
490 /* Bulk transfer I/O timeout? */
491 if (server.masterhost && server.replstate == REDIS_REPL_TRANSFER &&
492 (time(NULL)-server.repl_transfer_lastio) > REDIS_REPL_TRANSFER_TIMEOUT)
493 {
494 redisLog(REDIS_WARNING,"Timeout receiving bulk data from MASTER...");
495 replicationAbortSyncTransfer();
496 }
497
498 /* Check if we should connect to a MASTER */
499 if (server.replstate == REDIS_REPL_CONNECT) {
500 redisLog(REDIS_NOTICE,"Connecting to MASTER...");
501 if (syncWithMaster() == REDIS_OK) {
502 redisLog(REDIS_NOTICE,"MASTER <-> SLAVE sync started: SYNC sent");
503 if (server.appendonly) rewriteAppendOnlyFileBackground();
504 }
505 }
506 }