Current section

Files

Jump to
ra src ra_log_sync.erl
Raw

src/ra_log_sync.erl

%% This Source Code Form is subject to the terms of the Mozilla Public
%% License, v. 2.0. If a copy of the MPL was not distributed with this
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
%%
%% Copyright (c) 2017-2025 Broadcom. All Rights Reserved. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries.
%%
%% @hidden
-module(ra_log_sync).
-behaviour(gen_batch_server).
-export([start_link/1,
pool_size/0,
worker_name/2,
sync/2]).
-export([init/1,
handle_batch/2,
terminate/2,
format_status/1]).
-type sync_fun() :: fun(() -> ok | {error, term()}).
-type pool_ref() :: {pool, BaseName :: atom(), PoolSize :: pos_integer()}.
-export_type([pool_ref/0]).
-record(state, {}).
%%%===================================================================
%%% API
%%%===================================================================
-spec pool_size() -> pos_integer().
pool_size() ->
max(1, erlang:system_info(schedulers) div 4).
-spec worker_name(atom(), non_neg_integer()) -> atom().
worker_name(BaseName, Idx) ->
% elp:ignore W0023 (atoms_exhaustion)
list_to_atom(atom_to_list(BaseName) ++ "_" ++ integer_to_list(Idx)).
-spec start_link(#{name := atom()}) ->
{ok, pid()} | {error, term()}.
start_link(#{name := Name} = _Config) ->
gen_batch_server:start_link({local, Name}, ?MODULE, {},
[{reversed_batch, true}]).
-spec sync(pool_ref(), sync_fun()) ->
ok | {error, term()}.
sync({pool, BaseName, PoolSize}, SyncFun) when is_function(SyncFun, 0) ->
Idx = rand:uniform(PoolSize) - 1,
Worker = worker_name(BaseName, Idx),
gen_batch_server:call(Worker, {sync, SyncFun}, infinity).
%%%===================================================================
%%% gen_batch_server callbacks
%%%===================================================================
init({}) ->
{ok, #state{}}.
handle_batch(Ops, State) ->
%% Ops arrive in reverse arrival order (most recent first) because
%% reversed_batch = true. On journaling filesystems (ext4, XFS),
%% syncing the most recently written file first flushes the journal,
%% which tends to make subsequent syncs near-instant since the earlier
%% files are already durable.
lists:foreach(
fun({call, From, {sync, SyncFun}}) ->
Result = try SyncFun()
catch _:Err -> {error, Err}
end,
gen:reply(From, Result);
(_) ->
ok
end, Ops),
{ok, [], State}.
terminate(_Reason, _State) ->
ok.
format_status(State) ->
State.