|
30 | 30 | fold/5, |
31 | 31 | fold/6, |
32 | 32 |
|
| 33 | + fold_terms/5, |
33 | 34 |
|
34 | 35 | term/2, |
35 | 36 | get/2, |
36 | 37 | get/3, |
37 | 38 | get/4, |
| 39 | + get_terms/3, |
38 | 40 |
|
39 | 41 | config/1 |
40 | 42 | ]). |
@@ -434,6 +436,45 @@ fold_impl(Log, First, Last, SizeLimit, Func, AccIn) -> |
434 | 436 | {error, Reason} |
435 | 437 | end. |
436 | 438 |
|
| 439 | +%% Folds over the terms in the log view of raw entries from the log provider |
| 440 | +%% between the provided first and last log indices (inclusive). |
| 441 | +%% If there exists a log term between the provided first and last indices then |
| 442 | +%% the accumulator function will be called on at least that term. |
| 443 | +%% This API provides no validation of the log indices and term passed by the |
| 444 | +%% provider to the callback function. |
| 445 | +-spec fold_terms(LogOrView :: log() | view(), |
| 446 | + First :: log_index(), |
| 447 | + Last :: log_index(), |
| 448 | + Func :: fun((Index :: log_index(), Term :: log_term(), Acc) -> Acc), |
| 449 | + Acc) -> |
| 450 | + {ok, Acc} | wa_raft:error(). |
| 451 | +fold_terms(#log_view{log = Log, first = LogFirst, last = LogLast}, First, Last, Func, Acc) -> |
| 452 | + fold_terms_impl(Log, max(First, LogFirst), min(Last, LogLast), Func, Acc); |
| 453 | +fold_terms(Log, First, Last, Func, Acc) -> |
| 454 | + Provider = provider(Log), |
| 455 | + LogFirst = Provider:first_index(Log), |
| 456 | + LogLast = Provider:last_index(Log), |
| 457 | + fold_terms_impl(Log, max(First, LogFirst), min(Last, LogLast), Func, Acc). |
| 458 | + |
| 459 | +-spec fold_terms_impl( |
| 460 | + Log :: log(), |
| 461 | + First :: log_index(), |
| 462 | + Last :: log_index(), |
| 463 | + Func :: fun((Index :: log_index(), Term :: log_term(), Acc) -> Acc), |
| 464 | + Acc :: term() |
| 465 | +) -> {ok, Acc} | wa_raft:error(). |
| 466 | +fold_terms_impl(Log, First, Last, Func, AccIn) -> |
| 467 | + ?RAFT_COUNT('raft.log.fold_terms'), |
| 468 | + ?RAFT_COUNTV('raft.log.fold_terms.total', Last - First + 1), |
| 469 | + Provider = provider(Log), |
| 470 | + case Provider:fold_terms(Log, First, Last, Func, AccIn) of |
| 471 | + {ok, AccOut} -> |
| 472 | + {ok, AccOut}; |
| 473 | + {error, Reason} -> |
| 474 | + ?RAFT_COUNT('raft.log.fold_terms.error'), |
| 475 | + {error, Reason} |
| 476 | + end. |
| 477 | + |
437 | 478 | %% Gets the term of entry at the provided log index. When using a log view |
438 | 479 | %% this function may return 'not_found' even if the underlying log entry still |
439 | 480 | %% exists if the entry is outside of the log view. |
@@ -492,6 +533,25 @@ get(LogOrView, First, CountLimit, SizeLimit) -> |
492 | 533 | {error, corruption} |
493 | 534 | end. |
494 | 535 |
|
| 536 | +-spec get_terms(LogOrView :: log() | view(), First :: log_index(), Limit :: non_neg_integer()) -> |
| 537 | + {ok, Terms :: [wa_raft_log:log_term()]} | wa_raft:error(). |
| 538 | +get_terms(LogOrView, First, Limit) -> |
| 539 | + try |
| 540 | + fold_terms(LogOrView, First, First + Limit - 1, |
| 541 | + fun |
| 542 | + (Index, Term, {Index, Acc}) -> {Index + 1, [Term | Acc]}; |
| 543 | + (_Index, _Term, {ExpectedIndex, _Acc}) -> throw({missing, ExpectedIndex}) |
| 544 | + end, {First, []}) |
| 545 | + of |
| 546 | + {ok, {_, TermsRev}} -> {ok, lists:reverse(TermsRev)}; |
| 547 | + {error, Reason} -> {error, Reason} |
| 548 | + catch |
| 549 | + throw:{missing, Index} -> |
| 550 | + ?LOG_WARNING("[~p] detected log is missing index ~p during get of ~p ~~ ~p", |
| 551 | + [log_name(LogOrView), Index, First, First + Limit - 1], #{domain => [whatsapp, wa_raft]}), |
| 552 | + {error, corruption} |
| 553 | + end. |
| 554 | + |
495 | 555 | -spec config(LogOrView :: log() | view()) -> {ok, Index :: log_index(), Config :: wa_raft_server:config() | undefined} | not_found. |
496 | 556 | config(#log_view{config_index = undefined}) -> |
497 | 557 | not_found; |
|
0 commit comments