~ubuntu-branches/ubuntu/saucy/db/saucy-proposed

« back to all changes in this revision

Viewing changes to rep/rep_log.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson
  • Date: 2010-11-05 15:02:09 UTC
  • mfrom: (13.1.12 sid)
  • Revision ID: james.westby@ubuntu.com-20101105150209-ppvyn0619pu014xo
Tags: 5.1.19-1ubuntu1
* Resynchronise with Debian.  Remaining changes:
  - Pass --build/--host to configure to support cross-building, and don't
    override CC.
  - Disable the Java build when cross-building, for now.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*-
2
 
 * See the file LICENSE for redistribution information.
3
 
 *
4
 
 * Copyright (c) 2004, 2010 Oracle and/or its affiliates.  All rights reserved.
5
 
 *
6
 
 * $Id$
7
 
 */
8
 
 
9
 
#include "db_config.h"
10
 
 
11
 
#include "db_int.h"
12
 
#include "dbinc/log.h"
13
 
 
14
 
static int __rep_chk_newfile __P((ENV *, DB_LOGC *, REP *,
15
 
    __rep_control_args *, int));
16
 
 
17
 
/*
18
 
 * __rep_allreq --
19
 
 *      Handle a REP_ALL_REQ message.
20
 
 *
21
 
 * PUBLIC: int __rep_allreq __P((ENV *, __rep_control_args *, int));
22
 
 */
23
 
int
24
 
__rep_allreq(env, rp, eid)
25
 
        ENV *env;
26
 
        __rep_control_args *rp;
27
 
        int eid;
28
 
{
29
 
        DBT data_dbt, newfiledbt;
30
 
        DB_LOGC *logc;
31
 
        DB_LSN log_end, oldfilelsn;
32
 
        DB_REP *db_rep;
33
 
        REP *rep;
34
 
        REP_BULK bulk;
35
 
        REP_THROTTLE repth;
36
 
        __rep_newfile_args nf_args;
37
 
        uintptr_t bulkoff;
38
 
        u_int32_t bulkflags, end_flag, flags, use_bulk;
39
 
        int arch_flag, ret, t_ret;
40
 
        u_int8_t buf[__REP_NEWFILE_SIZE];
41
 
        size_t len;
42
 
 
43
 
        ret = 0;
44
 
        db_rep = env->rep_handle;
45
 
        rep = db_rep->region;
46
 
        end_flag = 0;
47
 
        arch_flag = 0;
48
 
 
49
 
        if ((ret = __log_cursor(env, &logc)) != 0)
50
 
                return (ret);
51
 
        memset(&data_dbt, 0, sizeof(data_dbt));
52
 
        /*
53
 
         * If we're doing bulk transfer, allocate a bulk buffer to put our
54
 
         * log records in.  We still need to initialize the throttle info
55
 
         * because if we encounter a log record larger than our entire bulk
56
 
         * buffer, we need to send it as a singleton and also we want to
57
 
         * support throttling with bulk.
58
 
         *
59
 
         * Use a local var so we don't need to worry if someone else turns
60
 
         * on/off bulk in the middle of our call.
61
 
         */
62
 
        use_bulk = FLD_ISSET(rep->config, REP_C_BULK);
63
 
        bulk.addr = NULL;
64
 
        if (use_bulk && (ret = __rep_bulk_alloc(env, &bulk, eid,
65
 
            &bulkoff, &bulkflags, REP_BULK_LOG)) != 0)
66
 
                goto err;
67
 
        memset(&repth, 0, sizeof(repth));
68
 
        REP_SYSTEM_LOCK(env);
69
 
        if ((ret = __rep_lockout_archive(env, rep)) != 0) {
70
 
                REP_SYSTEM_UNLOCK(env);
71
 
                goto err;
72
 
        }
73
 
        arch_flag = 1;
74
 
        repth.gbytes = rep->gbytes;
75
 
        repth.bytes = rep->bytes;
76
 
        oldfilelsn = repth.lsn = rp->lsn;
77
 
        repth.type = REP_LOG;
78
 
        repth.data_dbt = &data_dbt;
79
 
        REP_SYSTEM_UNLOCK(env);
80
 
 
81
 
        /*
82
 
         * Get the LSN of the end of the log, so that in our reading loop
83
 
         * (below), we can recognize when we get there, and set the
84
 
         * REPCTL_LOG_END flag.
85
 
         */
86
 
        if ((ret = __logc_get(logc, &log_end, &data_dbt, DB_LAST)) != 0) {
87
 
                if (ret == DB_NOTFOUND && F_ISSET(rep, REP_F_MASTER))
88
 
                        ret = 0;
89
 
                goto err;
90
 
        }
91
 
 
92
 
        flags = IS_ZERO_LSN(rp->lsn) ||
93
 
            IS_INIT_LSN(rp->lsn) ?  DB_FIRST : DB_SET;
94
 
        /*
95
 
         * We get the first item so that a client servicing requests
96
 
         * can distinguish between not having the records and reaching
97
 
         * the end of its log.  Return the DB_NOTFOUND if the client
98
 
         * cannot get the record.  Return 0 if we finish the loop and
99
 
         * sent all that we have.
100
 
         */
101
 
        ret = __logc_get(logc, &repth.lsn, &data_dbt, flags);
102
 
        /*
103
 
         * If the client is asking for all records
104
 
         * because it doesn't have any, and our first
105
 
         * record is not in the first log file, then
106
 
         * the client is outdated and needs to get a
107
 
         * VERIFY_FAIL.
108
 
         */
109
 
        if (ret == 0 && repth.lsn.file != 1 && flags == DB_FIRST) {
110
 
                if (F_ISSET(rep, REP_F_CLIENT))
111
 
                        ret = DB_NOTFOUND;
112
 
                else
113
 
                        (void)__rep_send_message(env, eid,
114
 
                            REP_VERIFY_FAIL, &repth.lsn, NULL, 0, 0);
115
 
                goto err;
116
 
        }
117
 
        /*
118
 
         * If we got DB_NOTFOUND it could be because the LSN we were
119
 
         * given is at the end of the log file and we need to switch
120
 
         * log files.  Reinitialize and get the current record when we return.
121
 
         */
122
 
        if (ret == DB_NOTFOUND) {
123
 
                ret = __rep_chk_newfile(env, logc, rep, rp, eid);
124
 
                /*
125
 
                 * If we still get DB_NOTFOUND the client gave us a
126
 
                 * bad or unknown LSN.  Ignore it if we're the master.
127
 
                 * Any other error is returned.
128
 
                 */
129
 
                if (ret == 0)
130
 
                        ret = __logc_get(logc, &repth.lsn,
131
 
                            &data_dbt, DB_CURRENT);
132
 
                if (ret == DB_NOTFOUND && F_ISSET(rep, REP_F_MASTER)) {
133
 
                        ret = 0;
134
 
                        goto err;
135
 
                }
136
 
                if (ret != 0)
137
 
                        goto err;
138
 
        }
139
 
 
140
 
        /*
141
 
         * For singleton log records, we break when we get a REP_LOG_MORE.
142
 
         * Or if we're not using throttling, or we are using bulk, we stop
143
 
         * when we reach the end (i.e. ret != 0).
144
 
         */
145
 
        for (end_flag = 0;
146
 
            ret == 0 && repth.type != REP_LOG_MORE && end_flag == 0;
147
 
            ret = __logc_get(logc, &repth.lsn, &data_dbt, DB_NEXT)) {
148
 
                /*
149
 
                 * If we just changed log files, we need to send the
150
 
                 * version of this log file to the client.
151
 
                 */
152
 
                if (repth.lsn.file != oldfilelsn.file) {
153
 
                        if ((ret = __logc_version(logc, &nf_args.version)) != 0)
154
 
                                break;
155
 
                        memset(&newfiledbt, 0, sizeof(newfiledbt));
156
 
                        if (rep->version < DB_REPVERSION_47)
157
 
                                DB_INIT_DBT(newfiledbt, &nf_args.version,
158
 
                                    sizeof(nf_args.version));
159
 
                        else {
160
 
                                if ((ret = __rep_newfile_marshal(env, &nf_args,
161
 
                                    buf, __REP_NEWFILE_SIZE, &len)) != 0)
162
 
                                        goto err;
163
 
                                DB_INIT_DBT(newfiledbt, buf, len);
164
 
                        }
165
 
                        (void)__rep_send_message(env,
166
 
                            eid, REP_NEWFILE, &oldfilelsn, &newfiledbt,
167
 
                            REPCTL_RESEND, 0);
168
 
                }
169
 
 
170
 
                /*
171
 
                 * Mark the end of the ALL_REQ response to show that the
172
 
                 * receiving client should now be "caught up" with the
173
 
                 * replication group.  If we're the master, then our log end is
174
 
                 * certainly authoritative.  If we're another client, only if we
175
 
                 * ourselves have reached STARTUPDONE.
176
 
                 */
177
 
                end_flag = (LOG_COMPARE(&repth.lsn, &log_end) >= 0 &&
178
 
                    (F_ISSET(rep, REP_F_MASTER) ||
179
 
                    rep->stat.st_startup_complete)) ?
180
 
                    REPCTL_LOG_END : 0;
181
 
                /*
182
 
                 * If we are configured for bulk, try to send this as a bulk
183
 
                 * request.  If not configured, or it is too big for bulk
184
 
                 * then just send normally.
185
 
                 */
186
 
                if (use_bulk)
187
 
                        ret = __rep_bulk_message(env, &bulk, &repth,
188
 
                            &repth.lsn, &data_dbt, (REPCTL_RESEND | end_flag));
189
 
                if (!use_bulk || ret == DB_REP_BULKOVF)
190
 
                        ret = __rep_send_throttle(env,
191
 
                            eid, &repth, 0, end_flag);
192
 
                if (ret != 0)
193
 
                        break;
194
 
                /*
195
 
                 * If we are about to change files, then we'll need the
196
 
                 * last LSN in the previous file.  Save it here.
197
 
                 */
198
 
                oldfilelsn = repth.lsn;
199
 
                oldfilelsn.offset += logc->len;
200
 
        }
201
 
 
202
 
        if (ret == DB_NOTFOUND || ret == DB_REP_UNAVAIL)
203
 
                ret = 0;
204
 
        /*
205
 
         * We're done, force out whatever remains in the bulk buffer and
206
 
         * free it.
207
 
         */
208
 
err:
209
 
        /*
210
 
         * We could have raced an unlink from an earlier log_archive
211
 
         * and the user is removing the files themselves, now.  If
212
 
         * we get an error indicating the log file might no longer
213
 
         * exist, ignore it.
214
 
         */
215
 
        if (ret == ENOENT)
216
 
                ret = 0;
217
 
        if (bulk.addr != NULL && (t_ret = __rep_bulk_free(env, &bulk,
218
 
            (REPCTL_RESEND | end_flag))) != 0 && ret == 0 &&
219
 
            t_ret != DB_REP_UNAVAIL)
220
 
                ret = t_ret;
221
 
        if (arch_flag) {
222
 
                REP_SYSTEM_LOCK(env);
223
 
                FLD_CLR(rep->lockout_flags, REP_LOCKOUT_ARCHIVE);
224
 
                REP_SYSTEM_UNLOCK(env);
225
 
        }
226
 
        if ((t_ret = __logc_close(logc)) != 0 && ret == 0)
227
 
                ret = t_ret;
228
 
        return (ret);
229
 
}
230
 
 
231
 
/*
232
 
 * __rep_log --
233
 
 *      Handle a REP_LOG/REP_LOG_MORE message.
234
 
 *
235
 
 * PUBLIC: int __rep_log __P((ENV *, DB_THREAD_INFO *,
236
 
 * PUBLIC:     __rep_control_args *, DBT *, time_t, DB_LSN *));
237
 
 */
238
 
int
239
 
__rep_log(env, ip, rp, rec, savetime, ret_lsnp)
240
 
        ENV *env;
241
 
        DB_THREAD_INFO *ip;
242
 
        __rep_control_args *rp;
243
 
        DBT *rec;
244
 
        time_t savetime;
245
 
        DB_LSN *ret_lsnp;
246
 
{
247
 
        DB_LOG *dblp;
248
 
        DB_LSN last_lsn, lsn;
249
 
        DB_REP *db_rep;
250
 
        LOG *lp;
251
 
        REP *rep;
252
 
        int is_dup, master, ret;
253
 
 
254
 
        is_dup = ret = 0;
255
 
        db_rep = env->rep_handle;
256
 
        rep = db_rep->region;
257
 
        dblp = env->lg_handle;
258
 
        lp = dblp->reginfo.primary;
259
 
 
260
 
        ret = __rep_apply(env, ip, rp, rec, ret_lsnp, &is_dup, &last_lsn);
261
 
        switch (ret) {
262
 
        /*
263
 
         * We're in an internal backup and we've gotten
264
 
         * all the log we need to run recovery.  Do so now.
265
 
         */
266
 
        case DB_REP_LOGREADY:
267
 
                if ((ret =
268
 
                    __rep_logready(env, rep, savetime, &last_lsn)) != 0)
269
 
                        goto out;
270
 
                break;
271
 
        /*
272
 
         * If we get any of the "normal" returns, we only process
273
 
         * LOG_MORE if this is not a duplicate record.  If the
274
 
         * record is a duplicate we don't want to handle LOG_MORE
275
 
         * and request a multiple data stream (or trigger internal
276
 
         * initialization) since this could be a very old record
277
 
         * that no longer exists on the master.
278
 
         */
279
 
        case DB_REP_ISPERM:
280
 
        case DB_REP_NOTPERM:
281
 
        case 0:
282
 
                if (is_dup)
283
 
                        goto out;
284
 
                else
285
 
                        break;
286
 
        /*
287
 
         * Any other return (errors), we're done.
288
 
         */
289
 
        default:
290
 
                goto out;
291
 
        }
292
 
        if (rp->rectype == REP_LOG_MORE) {
293
 
                master = rep->master_id;
294
 
 
295
 
                /*
296
 
                 * Keep the cycle from stalling: In case we got the LOG_MORE out
297
 
                 * of order, before some preceding log records, we want to make
298
 
                 * sure our follow-up request resumes from where the LOG_MORE
299
 
                 * said it should.  (If the preceding log records never arrive,
300
 
                 * normal gap processing should take care of asking for them.)
301
 
                 * But if we already have this record and/or more, we need to
302
 
                 * ask to resume from what we need.  The upshot is we need the
303
 
                 * max of lp->lsn and the lsn from the message.
304
 
                 */
305
 
                MUTEX_LOCK(env, rep->mtx_clientdb);
306
 
                lsn = lp->ready_lsn;
307
 
                if (LOG_COMPARE(&rp->lsn, &lsn) > 0)
308
 
                        lsn = rp->lsn;
309
 
 
310
 
                /*
311
 
                 * If the master_id is invalid, this means that since
312
 
                 * the last record was sent, somebody declared an
313
 
                 * election and we may not have a master to request
314
 
                 * things of.
315
 
                 *
316
 
                 * This is not an error;  when we find a new master,
317
 
                 * we'll re-negotiate where the end of the log is and
318
 
                 * try to bring ourselves up to date again anyway.
319
 
                 */
320
 
                if (master == DB_EID_INVALID) {
321
 
                        ret = 0;
322
 
                        MUTEX_UNLOCK(env, rep->mtx_clientdb);
323
 
                        goto out;
324
 
                }
325
 
                /*
326
 
                 * If we're waiting for records, set the wait_ts
327
 
                 * high so that we avoid re-requesting too soon and
328
 
                 * end up with multiple data streams.
329
 
                 */
330
 
                if (IS_ZERO_LSN(lp->waiting_lsn))
331
 
                        lp->wait_ts = rep->max_gap;
332
 
                ret = __rep_loggap_req(env, rep, &lsn, REP_GAP_FORCE);
333
 
                MUTEX_UNLOCK(env, rep->mtx_clientdb);
334
 
        }
335
 
out:
336
 
        return (ret);
337
 
}
338
 
 
339
 
/*
340
 
 * __rep_bulk_log --
341
 
 *      Handle a REP_BULK_LOG message.
342
 
 *
343
 
 * PUBLIC: int __rep_bulk_log __P((ENV *, DB_THREAD_INFO *,
344
 
 * PUBLIC:     __rep_control_args *, DBT *, time_t, DB_LSN *));
345
 
 */
346
 
int
347
 
__rep_bulk_log(env, ip, rp, rec, savetime, ret_lsnp)
348
 
        ENV *env;
349
 
        DB_THREAD_INFO *ip;
350
 
        __rep_control_args *rp;
351
 
        DBT *rec;
352
 
        time_t savetime;
353
 
        DB_LSN *ret_lsnp;
354
 
{
355
 
        DB_LSN last_lsn;
356
 
        DB_REP *db_rep;
357
 
        REP *rep;
358
 
        int ret;
359
 
 
360
 
        db_rep = env->rep_handle;
361
 
        rep = db_rep->region;
362
 
 
363
 
        ret = __log_rep_split(env, ip, rp, rec, ret_lsnp, &last_lsn);
364
 
        switch (ret) {
365
 
        /*
366
 
         * We're in an internal backup and we've gotten
367
 
         * all the log we need to run recovery.  Do so now.
368
 
         */
369
 
        case DB_REP_LOGREADY:
370
 
                ret = __rep_logready(env, rep, savetime, &last_lsn);
371
 
                break;
372
 
        /*
373
 
         * Any other return (errors), we're done.
374
 
         */
375
 
        default:
376
 
                break;
377
 
        }
378
 
        return (ret);
379
 
}
380
 
 
381
 
/*
382
 
 * __rep_log_req --
383
 
 *      Handle a REP_LOG_REQ message.
384
 
 *
385
 
 * PUBLIC: int __rep_logreq __P((ENV *, __rep_control_args *, DBT *, int));
386
 
 */
387
 
int
388
 
__rep_logreq(env, rp, rec, eid)
389
 
        ENV *env;
390
 
        __rep_control_args *rp;
391
 
        DBT *rec;
392
 
        int eid;
393
 
{
394
 
        DBT data_dbt, newfiledbt;
395
 
        DB_LOGC *logc;
396
 
        DB_LSN firstlsn, lsn, oldfilelsn;
397
 
        DB_REP *db_rep;
398
 
        REP *rep;
399
 
        REP_BULK bulk;
400
 
        REP_THROTTLE repth;
401
 
        __rep_logreq_args lr_args;
402
 
        __rep_newfile_args nf_args;
403
 
        uintptr_t bulkoff;
404
 
        u_int32_t bulkflags, use_bulk;
405
 
        int count, ret, t_ret;
406
 
        u_int8_t buf[__REP_NEWFILE_SIZE];
407
 
        size_t len;
408
 
 
409
 
        ret = 0;
410
 
        db_rep = env->rep_handle;
411
 
        rep = db_rep->region;
412
 
 
413
 
        /* COMPQUIET_LSN is what this is...  */
414
 
        ZERO_LSN(lr_args.endlsn);
415
 
 
416
 
        if (rec != NULL && rec->size != 0) {
417
 
                if (rp->rep_version < DB_REPVERSION_47)
418
 
                        lr_args.endlsn = *(DB_LSN *)rec->data;
419
 
                else if ((ret = __rep_logreq_unmarshal(env, &lr_args,
420
 
                    rec->data, rec->size, NULL)) != 0)
421
 
                        return (ret);
422
 
                RPRINT(env, (env, DB_VERB_REP_MISC,
423
 
                    "[%lu][%lu]: LOG_REQ max lsn: [%lu][%lu]",
424
 
                    (u_long) rp->lsn.file, (u_long)rp->lsn.offset,
425
 
                    (u_long)lr_args.endlsn.file,
426
 
                    (u_long)lr_args.endlsn.offset));
427
 
        }
428
 
        /*
429
 
         * There are several different cases here.
430
 
         * 1. We asked logc_get for a particular LSN and got it.
431
 
         * 2. We asked logc_get for an LSN and it's not found because it is
432
 
         *      beyond the end of a log file and we need a NEWFILE msg.
433
 
         *      and then the record that was requested.
434
 
         * 3. We asked logc_get for an LSN and it is already archived.
435
 
         * 4. We asked logc_get for an LSN and it simply doesn't exist, but
436
 
         *    doesn't meet any of those other criteria, in which case
437
 
         *    it's an error (that should never happen on a master).
438
 
         *
439
 
         * If we have a valid LSN and the request has a data_dbt with
440
 
         * it, the sender is asking for a chunk of log records.
441
 
         * Then we need to send all records up to the LSN in the data dbt.
442
 
         */
443
 
        memset(&data_dbt, 0, sizeof(data_dbt));
444
 
        oldfilelsn = lsn = rp->lsn;
445
 
        if ((ret = __log_cursor(env, &logc)) != 0)
446
 
                return (ret);
447
 
        REP_SYSTEM_LOCK(env);
448
 
        if ((ret = __rep_lockout_archive(env, rep)) != 0) {
449
 
                REP_SYSTEM_UNLOCK(env);
450
 
                goto err;
451
 
        }
452
 
        REP_SYSTEM_UNLOCK(env);
453
 
        if ((ret = __logc_get(logc, &lsn, &data_dbt, DB_SET)) == 0) {
454
 
                /* Case 1 */
455
 
                (void)__rep_send_message(env,
456
 
                   eid, REP_LOG, &lsn, &data_dbt, REPCTL_RESEND, 0);
457
 
                oldfilelsn.offset += logc->len;
458
 
        } else if (ret == DB_NOTFOUND) {
459
 
                /*
460
 
                 * If logc_get races with log_archive, it might return
461
 
                 * DB_NOTFOUND.  We expect there to be some log record
462
 
                 * that is the first one.  Loop until we either get
463
 
                 * a log record or some error.  Since we only expect
464
 
                 * to get this racing log_archive, bound it to a few
465
 
                 * tries.
466
 
                 */
467
 
                count = 0;
468
 
                do {
469
 
                        ret = __logc_get(logc, &firstlsn, &data_dbt, DB_FIRST);
470
 
                        count++;
471
 
                } while (ret == DB_NOTFOUND && count < 10);
472
 
                if (ret != 0)
473
 
                        goto err;
474
 
                if (LOG_COMPARE(&firstlsn, &rp->lsn) > 0) {
475
 
                        /* Case 3 */
476
 
                        if (F_ISSET(rep, REP_F_CLIENT)) {
477
 
                                ret = DB_NOTFOUND;
478
 
                                goto err;
479
 
                        }
480
 
                        (void)__rep_send_message(env, eid,
481
 
                            REP_VERIFY_FAIL, &rp->lsn, NULL, 0, 0);
482
 
                        ret = 0;
483
 
                        goto err;
484
 
                }
485
 
                ret = __rep_chk_newfile(env, logc, rep, rp, eid);
486
 
                if (ret == DB_NOTFOUND) {
487
 
                        /* Case 4 */
488
 
                        /*
489
 
                         * If we still get DB_NOTFOUND the client gave us an
490
 
                         * unknown LSN, perhaps at the end of the log.  Ignore
491
 
                         * it if we're the master.  Return DB_NOTFOUND if
492
 
                         * we are the client.
493
 
                         */
494
 
                        if (F_ISSET(rep, REP_F_MASTER)) {
495
 
                                __db_errx(env,
496
 
                                    "Request for LSN [%lu][%lu] not found",
497
 
                                    (u_long)rp->lsn.file,
498
 
                                    (u_long)rp->lsn.offset);
499
 
                                ret = 0;
500
 
                                goto err;
501
 
                        } else
502
 
                                ret = DB_NOTFOUND;
503
 
                }
504
 
        }
505
 
 
506
 
        if (ret != 0)
507
 
                goto err;
508
 
 
509
 
        /*
510
 
         * If the user requested a gap, send the whole thing, while observing
511
 
         * the limits from rep_set_limit.
512
 
         *
513
 
         * If we're doing bulk transfer, allocate a bulk buffer to put our
514
 
         * log records in.  We still need to initialize the throttle info
515
 
         * because if we encounter a log record larger than our entire bulk
516
 
         * buffer, we need to send it as a singleton.
517
 
         *
518
 
         * Use a local var so we don't need to worry if someone else turns
519
 
         * on/off bulk in the middle of our call.
520
 
         */
521
 
        use_bulk = FLD_ISSET(rep->config, REP_C_BULK);
522
 
        if (use_bulk && (ret = __rep_bulk_alloc(env, &bulk, eid,
523
 
            &bulkoff, &bulkflags, REP_BULK_LOG)) != 0)
524
 
                goto err;
525
 
        memset(&repth, 0, sizeof(repth));
526
 
        REP_SYSTEM_LOCK(env);
527
 
        repth.gbytes = rep->gbytes;
528
 
        repth.bytes = rep->bytes;
529
 
        repth.type = REP_LOG;
530
 
        repth.data_dbt = &data_dbt;
531
 
        REP_SYSTEM_UNLOCK(env);
532
 
        while (ret == 0 && rec != NULL && rec->size != 0 &&
533
 
            repth.type == REP_LOG) {
534
 
                if ((ret =
535
 
                    __logc_get(logc, &repth.lsn, &data_dbt, DB_NEXT)) != 0) {
536
 
                        /*
537
 
                         * If we're a client and we only have part of the gap,
538
 
                         * return DB_NOTFOUND so that we send a REREQUEST
539
 
                         * back to the requester and it can ask for more.
540
 
                         */
541
 
                        if (ret == DB_NOTFOUND && F_ISSET(rep, REP_F_MASTER))
542
 
                                ret = 0;
543
 
                        break;
544
 
                }
545
 
                if (LOG_COMPARE(&repth.lsn, &lr_args.endlsn) >= 0)
546
 
                        break;
547
 
                if (repth.lsn.file != oldfilelsn.file) {
548
 
                        if ((ret = __logc_version(logc, &nf_args.version)) != 0)
549
 
                                break;
550
 
                        memset(&newfiledbt, 0, sizeof(newfiledbt));
551
 
                        if (rep->version < DB_REPVERSION_47)
552
 
                                DB_INIT_DBT(newfiledbt, &nf_args.version,
553
 
                                    sizeof(nf_args.version));
554
 
                        else {
555
 
                                if ((ret = __rep_newfile_marshal(env, &nf_args,
556
 
                                    buf, __REP_NEWFILE_SIZE, &len)) != 0)
557
 
                                        goto err;
558
 
                                DB_INIT_DBT(newfiledbt, buf, len);
559
 
                        }
560
 
                        (void)__rep_send_message(env,
561
 
                            eid, REP_NEWFILE, &oldfilelsn, &newfiledbt,
562
 
                            REPCTL_RESEND, 0);
563
 
                }
564
 
                /*
565
 
                 * If we are configured for bulk, try to send this as a bulk
566
 
                 * request.  If not configured, or it is too big for bulk
567
 
                 * then just send normally.
568
 
                 */
569
 
                if (use_bulk)
570
 
                        ret = __rep_bulk_message(env, &bulk, &repth,
571
 
                            &repth.lsn, &data_dbt, REPCTL_RESEND);
572
 
                if (!use_bulk || ret == DB_REP_BULKOVF)
573
 
                        ret = __rep_send_throttle(env, eid, &repth, 0, 0);
574
 
                if (ret != 0) {
575
 
                        /* Ignore send failure, except to break the loop. */
576
 
                        if (ret == DB_REP_UNAVAIL)
577
 
                                ret = 0;
578
 
                        break;
579
 
                }
580
 
                /*
581
 
                 * If we are about to change files, then we'll need the
582
 
                 * last LSN in the previous file.  Save it here.
583
 
                 */
584
 
                oldfilelsn = repth.lsn;
585
 
                oldfilelsn.offset += logc->len;
586
 
        }
587
 
 
588
 
        /*
589
 
         * We're done, force out whatever remains in the bulk buffer and
590
 
         * free it.
591
 
         */
592
 
        if (use_bulk && (t_ret = __rep_bulk_free(env, &bulk,
593
 
            REPCTL_RESEND)) != 0 && ret == 0 &&
594
 
            t_ret != DB_REP_UNAVAIL)
595
 
                ret = t_ret;
596
 
err:
597
 
        /*
598
 
         * We could have raced an unlink from an earlier log_archive
599
 
         * and the user is removing the files themselves, now.  If
600
 
         * we get an error indicating the log file might no longer
601
 
         * exist, ignore it.
602
 
         */
603
 
        if (ret == ENOENT)
604
 
                ret = 0;
605
 
        REP_SYSTEM_LOCK(env);
606
 
        FLD_CLR(rep->lockout_flags, REP_LOCKOUT_ARCHIVE);
607
 
        REP_SYSTEM_UNLOCK(env);
608
 
        if ((t_ret = __logc_close(logc)) != 0 && ret == 0)
609
 
                ret = t_ret;
610
 
        return (ret);
611
 
}
612
 
 
613
 
/*
614
 
 * __rep_loggap_req -
615
 
 *      Request a log gap.  Assumes the caller holds the REP->mtx_clientdb.
616
 
 *
617
 
 * lsnp is the current LSN we're handling.  It is used to help decide
618
 
 *      if we ask for a gap or singleton.
619
 
 * gapflags are flags that may override the algorithm or control the
620
 
 *      processing in some way.
621
 
 *
622
 
 * PUBLIC: int __rep_loggap_req __P((ENV *, REP *, DB_LSN *, u_int32_t));
623
 
 */
624
 
int
625
 
__rep_loggap_req(env, rep, lsnp, gapflags)
626
 
        ENV *env;
627
 
        REP *rep;
628
 
        DB_LSN *lsnp;
629
 
        u_int32_t gapflags;
630
 
{
631
 
        DBT max_lsn_dbt, *max_lsn_dbtp;
632
 
        DB_LOG *dblp;
633
 
        DB_LSN next_lsn;
634
 
        LOG *lp;
635
 
        __rep_logreq_args lr_args;
636
 
        size_t len;
637
 
        u_int32_t ctlflags, flags, type;
638
 
        int master, ret;
639
 
        u_int8_t buf[__REP_LOGREQ_SIZE];
640
 
 
641
 
        dblp = env->lg_handle;
642
 
        lp = dblp->reginfo.primary;
643
 
        if (FLD_ISSET(gapflags, REP_GAP_FORCE))
644
 
                next_lsn = *lsnp;
645
 
        else
646
 
                next_lsn = lp->ready_lsn;
647
 
        ctlflags = flags = 0;
648
 
        type = REP_LOG_REQ;
649
 
        ret = 0;
650
 
 
651
 
        /*
652
 
         * Check if we need to ask for the gap.
653
 
         * We ask for the gap if:
654
 
         *      We are forced to with gapflags.
655
 
         *      If max_wait_lsn is ZERO_LSN - we've never asked for
656
 
         *        records before.
657
 
         *      If we asked for a single record and received it.
658
 
         *
659
 
         * If we want a gap, but don't have an ending LSN (waiting_lsn)
660
 
         * send an ALL_REQ.  This is primarily used by REP_REREQUEST when
661
 
         * an ALL_REQ was not able to be fulfilled by another client.
662
 
         */
663
 
        if (FLD_ISSET(gapflags, (REP_GAP_FORCE | REP_GAP_REREQUEST)) ||
664
 
            IS_ZERO_LSN(lp->max_wait_lsn) ||
665
 
            (lsnp != NULL && LOG_COMPARE(lsnp, &lp->max_wait_lsn) == 0)) {
666
 
                lp->max_wait_lsn = lp->waiting_lsn;
667
 
                /*
668
 
                 * If we are forcing a gap, we need to send a max_wait_lsn
669
 
                 * that may be beyond the current gap/waiting_lsn (but
670
 
                 * it may not be).  If we cannot determine any future
671
 
                 * waiting LSN, then it should be zero.  If we're in
672
 
                 * internal init, it should be our ending LSN.
673
 
                 */
674
 
                if (FLD_ISSET(gapflags, REP_GAP_FORCE)) {
675
 
                        if (LOG_COMPARE(&lp->max_wait_lsn, lsnp) <= 0) {
676
 
                                if (rep->sync_state == SYNC_LOG) {
677
 
                                        DB_ASSERT(env, LOG_COMPARE(lsnp,
678
 
                                            &rep->last_lsn) <= 0);
679
 
                                        lp->max_wait_lsn = rep->last_lsn;
680
 
                                } else
681
 
                                        ZERO_LSN(lp->max_wait_lsn);
682
 
                        }
683
 
                }
684
 
                if (IS_ZERO_LSN(lp->max_wait_lsn))
685
 
                        type = REP_ALL_REQ;
686
 
                memset(&max_lsn_dbt, 0, sizeof(max_lsn_dbt));
687
 
                lr_args.endlsn = lp->max_wait_lsn;
688
 
                if (rep->version < DB_REPVERSION_47)
689
 
                        DB_INIT_DBT(max_lsn_dbt, &lp->max_wait_lsn,
690
 
                            sizeof(DB_LSN));
691
 
                else {
692
 
                        if ((ret = __rep_logreq_marshal(env, &lr_args, buf,
693
 
                            __REP_LOGREQ_SIZE, &len)) != 0)
694
 
                                goto err;
695
 
                        DB_INIT_DBT(max_lsn_dbt, buf, len);
696
 
                }
697
 
                max_lsn_dbtp = &max_lsn_dbt;
698
 
                /*
699
 
                 * Gap requests are "new" and can go anywhere, unless
700
 
                 * this is already a re-request.
701
 
                 */
702
 
                if (FLD_ISSET(gapflags, REP_GAP_REREQUEST))
703
 
                        flags = DB_REP_REREQUEST;
704
 
                else
705
 
                        flags = DB_REP_ANYWHERE;
706
 
        } else {
707
 
                max_lsn_dbtp = NULL;
708
 
                lp->max_wait_lsn = next_lsn;
709
 
                /*
710
 
                 * If we're dropping to singletons, this is a re-request.
711
 
                 */
712
 
                flags = DB_REP_REREQUEST;
713
 
        }
714
 
        if ((master = rep->master_id) != DB_EID_INVALID) {
715
 
                STAT(rep->stat.st_log_requested++);
716
 
                if (rep->sync_state == SYNC_LOG)
717
 
                        ctlflags = REPCTL_INIT;
718
 
                (void)__rep_send_message(env, master,
719
 
                    type, &next_lsn, max_lsn_dbtp, ctlflags, flags);
720
 
        } else
721
 
                (void)__rep_send_message(env, DB_EID_BROADCAST,
722
 
                    REP_MASTER_REQ, NULL, NULL, 0, 0);
723
 
err:
724
 
        return (ret);
725
 
}
726
 
 
727
 
/*
728
 
 * __rep_logready -
729
 
 *      Handle getting back REP_LOGREADY.  Any call to __rep_apply
730
 
 * can return it.
731
 
 *
732
 
 * PUBLIC: int __rep_logready __P((ENV *, REP *, time_t, DB_LSN *));
733
 
 */
734
 
int
735
 
__rep_logready(env, rep, savetime, last_lsnp)
736
 
        ENV *env;
737
 
        REP *rep;
738
 
        time_t savetime;
739
 
        DB_LSN *last_lsnp;
740
 
{
741
 
        int ret;
742
 
 
743
 
        if ((ret = __log_flush(env, NULL)) != 0)
744
 
                goto err;
745
 
        if ((ret = __rep_verify_match(env, last_lsnp, savetime)) != 0)
746
 
                goto err;
747
 
 
748
 
        REP_SYSTEM_LOCK(env);
749
 
        ZERO_LSN(rep->first_lsn);
750
 
 
751
 
        if (rep->originfo != NULL) {
752
 
                __os_free(env, rep->originfo);
753
 
                rep->originfo = NULL;
754
 
        }
755
 
 
756
 
        rep->sync_state = SYNC_OFF;
757
 
        F_SET(rep, REP_F_NIMDBS_LOADED);
758
 
        ret = __rep_notify_threads(env, AWAIT_NIMDB);
759
 
        REP_SYSTEM_UNLOCK(env);
760
 
        if (ret != 0)
761
 
                goto err;
762
 
 
763
 
        return (0);
764
 
 
765
 
err:
766
 
        __db_errx(env,
767
 
            "Client initialization failed.  Need to manually restore client");
768
 
        return (__env_panic(env, ret));
769
 
}
770
 
 
771
 
/*
772
 
 * __rep_chk_newfile --
773
 
 *     Determine if getting DB_NOTFOUND is because we're at the
774
 
 * end of a log file and need to send a NEWFILE message.
775
 
 *
776
 
 * This function handles these cases:
777
 
 * [Case 1 was that we found the record we were looking for - it
778
 
 * is already handled by the caller.]
779
 
 * 2. We asked logc_get for an LSN and it's not found because it is
780
 
 *      beyond the end of a log file and we need a NEWFILE msg.
781
 
 * 3. We asked logc_get for an LSN and it simply doesn't exist, but
782
 
 *    doesn't meet any of those other criteria, in which case
783
 
 *    we return DB_NOTFOUND and the caller decides if it's an error.
784
 
 *
785
 
 * This function returns 0 if we had to send a message and the bad
786
 
 * LSN is dealt with and DB_NOTFOUND if this really is an unknown LSN
787
 
 * (on a client) and errors if it isn't found on the master.
788
 
 */
789
 
static int
790
 
__rep_chk_newfile(env, logc, rep, rp, eid)
791
 
        ENV *env;
792
 
        DB_LOGC *logc;
793
 
        REP *rep;
794
 
        __rep_control_args *rp;
795
 
        int eid;
796
 
{
797
 
        DBT data_dbt, newfiledbt;
798
 
        DB_LOG *dblp;
799
 
        DB_LSN endlsn;
800
 
        LOG *lp;
801
 
        __rep_newfile_args nf_args;
802
 
        int ret;
803
 
        u_int8_t buf[__REP_NEWFILE_SIZE];
804
 
        size_t len;
805
 
 
806
 
        ret = 0;
807
 
        dblp = env->lg_handle;
808
 
        lp = dblp->reginfo.primary;
809
 
        memset(&data_dbt, 0, sizeof(data_dbt));
810
 
        LOG_SYSTEM_LOCK(env);
811
 
        endlsn = lp->lsn;
812
 
        LOG_SYSTEM_UNLOCK(env);
813
 
        if (endlsn.file > rp->lsn.file) {
814
 
                /*
815
 
                 * Case 2:
816
 
                 * Need to find the LSN of the last record in
817
 
                 * file lsn.file so that we can send it with
818
 
                 * the NEWFILE call.  In order to do that, we
819
 
                 * need to try to get {lsn.file + 1, 0} and
820
 
                 * then backup.
821
 
                 */
822
 
                endlsn.file = rp->lsn.file + 1;
823
 
                endlsn.offset = 0;
824
 
                if ((ret = __logc_get(logc,
825
 
                    &endlsn, &data_dbt, DB_SET)) != 0 ||
826
 
                    (ret = __logc_get(logc,
827
 
                        &endlsn, &data_dbt, DB_PREV)) != 0) {
828
 
                        RPRINT(env, (env, DB_VERB_REP_MISC,
829
 
                            "Unable to get prev of [%lu][%lu]",
830
 
                            (u_long)rp->lsn.file,
831
 
                            (u_long)rp->lsn.offset));
832
 
                        /*
833
 
                         * We want to push the error back
834
 
                         * to the client so that the client
835
 
                         * does an internal backup.  The
836
 
                         * client asked for a log record
837
 
                         * we no longer have and it is
838
 
                         * outdated.
839
 
                         * XXX - This could be optimized by
840
 
                         * having the master perform and
841
 
                         * send a REP_UPDATE message.  We
842
 
                         * currently want the client to set
843
 
                         * up its 'update' state prior to
844
 
                         * requesting REP_UPDATE_REQ.
845
 
                         *
846
 
                         * If we're a client servicing a request
847
 
                         * just return DB_NOTFOUND.
848
 
                         */
849
 
                        if (F_ISSET(rep, REP_F_MASTER)) {
850
 
                                ret = 0;
851
 
                                (void)__rep_send_message(env, eid,
852
 
                                    REP_VERIFY_FAIL, &rp->lsn,
853
 
                                    NULL, 0, 0);
854
 
                        } else
855
 
                                ret = DB_NOTFOUND;
856
 
                } else {
857
 
                        endlsn.offset += logc->len;
858
 
                        if ((ret = __logc_version(logc,
859
 
                            &nf_args.version)) == 0) {
860
 
                                memset(&newfiledbt, 0,
861
 
                                    sizeof(newfiledbt));
862
 
                                if (rep->version < DB_REPVERSION_47)
863
 
                                        DB_INIT_DBT(newfiledbt,
864
 
                                            &nf_args.version,
865
 
                                            sizeof(nf_args.version));
866
 
                                else {
867
 
                                        if ((ret = __rep_newfile_marshal(env,
868
 
                                            &nf_args, buf, __REP_NEWFILE_SIZE,
869
 
                                            &len)) != 0)
870
 
                                                return (ret);
871
 
                                        DB_INIT_DBT(newfiledbt, buf, len);
872
 
                                }
873
 
                                (void)__rep_send_message(env, eid,
874
 
                                    REP_NEWFILE, &endlsn,
875
 
                                    &newfiledbt, REPCTL_RESEND, 0);
876
 
                        }
877
 
                }
878
 
        } else
879
 
                ret = DB_NOTFOUND;
880
 
 
881
 
        return (ret);
882
 
}