Packages
ra
2.0.12
3.1.9
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.2
3.0.1
3.0.0
3.0.0-beta.1
2.17.3
2.17.2
2.17.1
2.17.0
2.16.13
2.16.12
2.16.11
2.16.10
2.16.9
2.16.8
2.16.7
2.16.6
2.16.5
2.16.4
2.16.3
2.16.2
2.16.1
2.16.0
2.16.0-pre.12
2.16.0-pre.11
2.16.0-pre.10
2.16.0-pre.9
2.16.0-pre.8
2.16.0-pre.7
2.16.0-pre.6
2.16.0-pre.5
2.16.0-pre.4
2.16.0-pre.3
2.16.0-pre.2
2.16.0-pre.1
2.15.4
2.15.3
2.15.2
2.15.1
2.15.0
2.14.0
2.13.6
2.13.5
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.13.0-pre.1
2.12.0
2.11.0
2.11.0-pre.1
2.10.2-pre.2
2.10.2-pre.1
2.10.1
2.10.0
2.10.0-pre.3
2.10.0-pre.2
2.10.0-pre.1
2.9.10-pre.1
2.9.1
2.9.1-pre.2
2.9.1-pre.1
2.9.0
2.8.0
retired
2.7.3
2.7.2
2.7.1
2.7.0
2.7.0-pre.3
2.7.0-pre.2
2.7.0-pre.1
2.6.3
2.6.2
2.6.1
2.6.0-pre.1
2.5.1
2.5.1-pre.1
2.5.0
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
retired
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.13
2.0.12
2.0.11
2.0.10
2.0.9
2.0.8
2.0.7
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.9.6
0.9.5
0.9.4
0.9.2
0.3.3
retired
0.3.2
retired
0.3.1
retired
Raft library
Current section
Files
Jump to
Current section
Files
src/ra_log_snapshot.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-2022 VMware, Inc. or its affiliates. All rights reserved.
%%
%% @hidden
-module(ra_log_snapshot).
-behaviour(ra_snapshot).
-export([
prepare/2,
write/3,
begin_accept/2,
accept_chunk/2,
complete_accept/2,
begin_read/1,
read_chunk/3,
recover/1,
validate/1,
read_meta/1
]).
-include("ra.hrl").
-define(MAGIC, "RASN").
-define(VERSION, 1).
-type file_err() :: ra_snapshot:file_err().
-type meta() :: ra_snapshot:meta().
%% DO nothing. There is no preparation for snapshotting
prepare(_Index, State) -> State.
%% @doc
%% Snapshot file format:
%% "RASN"
%% Version (byte)
%% Checksum (unsigned 32)
%% MetaData Len (unsigned 32)
%% MetaData (binary)
%% Snapshot Data (binary)
%% @end
-spec write(file:filename(), meta(), term()) ->
ok | {error, file_err()}.
write(Dir, Meta, MacState) ->
%% no compression on meta data to make sure reading it is as fast
%% as possible
MetaBin = term_to_binary(Meta),
%% the data can however be compressed
Bin = term_to_binary(MacState, [{compressed, 9}]),
Data = [<<(size(MetaBin)):32/unsigned>>, MetaBin, Bin],
Checksum = erlang:crc32(Data),
File = filename(Dir),
ra_lib:write_file(File, [<<?MAGIC,
?VERSION:8/unsigned,
Checksum:32/integer>>,
Data]).
begin_accept(SnapDir, Meta) ->
File = filename(SnapDir),
{ok, Fd} = file:open(File, [write, binary, raw]),
MetaBin = term_to_binary(Meta),
Data = [<<(size(MetaBin)):32/unsigned>>, MetaBin],
PartialCrc = erlang:crc32(Data),
ok = file:write(Fd, [<<?MAGIC,
?VERSION:8/unsigned,
0:32/integer>>,
Data]),
{ok, {PartialCrc, Fd}}.
accept_chunk(Chunk, {PartialCrc, Fd}) ->
<<Crc:32/integer, Rest/binary>> = Chunk,
accept_chunk(Rest, {PartialCrc, Crc, Fd});
accept_chunk(Chunk, {PartialCrc0, Crc, Fd}) ->
ok = file:write(Fd, Chunk),
PartialCrc = erlang:crc32(PartialCrc0, Chunk),
{ok, {PartialCrc, Crc, Fd}}.
complete_accept(Chunk, {PartialCrc, Fd}) ->
<<Crc:32/integer, Rest/binary>> = Chunk,
complete_accept(Rest, {PartialCrc, Crc, Fd});
complete_accept(Chunk, {PartialCrc0, Crc, Fd}) ->
ok = file:write(Fd, Chunk),
{ok, 5} = file:position(Fd, 5),
ok = file:write(Fd, <<Crc:32/integer>>),
Crc = erlang:crc32(PartialCrc0, Chunk),
ok = file:sync(Fd),
ok = file:close(Fd),
ok.
begin_read(Dir) ->
File = filename(Dir),
case file:open(File, [read, binary, raw]) of
{ok, Fd} ->
case read_meta_internal(Fd) of
{ok, Meta, Crc} ->
{ok, DataStart} = file:position(Fd, cur),
{ok, Eof} = file:position(Fd, eof),
{ok, Meta, {Crc, {DataStart, Eof, Fd}}};
{error, _} = Err ->
_ = file:close(Fd),
Err
end;
Err ->
Err
end.
read_chunk({Crc, ReadState}, Size, Dir) when is_integer(Crc) ->
case read_chunk(ReadState, Size - 4, Dir) of
{ok, Data, ReadState1} ->
{ok, <<Crc:32/integer, Data/binary>>, ReadState1};
{error, _} = Err ->
Err
end;
read_chunk({Pos, Eof, Fd}, Size, _Dir) ->
{ok, _} = file:position(Fd, Pos),
case file:read(Fd, Size) of
{ok, Data} ->
case Pos + Size >= Eof of
true ->
_ = file:close(Fd),
{ok, Data, last};
false ->
{ok, Data, {next, {Pos + Size, Eof, Fd}}}
end;
{error, _} = Err ->
Err;
eof ->
{error, unexpected_eof}
end.
-spec recover(file:filename()) ->
{ok, meta(), term()} |
{error, invalid_format |
{invalid_version, integer()} |
checksum_error |
file_err()}.
recover(Dir) ->
File = filename(Dir),
case prim_file:read_file(File) of
{ok, <<?MAGIC, ?VERSION:8/unsigned, Crc:32/integer, Data/binary>>} ->
validate(Crc, Data);
{ok, <<?MAGIC, Version:8/unsigned, _:32/integer, _/binary>>} ->
{error, {invalid_version, Version}};
{ok, _} ->
{error, invalid_format};
{error, _} = Err ->
Err
end.
validate(Dir) ->
case recover(Dir) of
{ok, _, _} -> ok;
Err -> Err
end.
%% @doc reads the index and term from the snapshot file without reading the
%% entire binary body. NB: this does not do checksum validation.
-spec read_meta(file:filename()) ->
{ok, meta()} | {error, invalid_format |
{invalid_version, integer()} |
checksum_error |
file_err()}.
read_meta(Dir) ->
File = filename(Dir),
case file:open(File, [read, binary, raw]) of
{ok, Fd} ->
case read_meta_internal(Fd) of
{ok, Meta, _} ->
_ = file:close(Fd),
{ok, Meta};
{error, _} = Err ->
_ = file:close(Fd),
Err
end;
Err ->
Err
end.
%% Internal
read_meta_internal(Fd) ->
HeaderSize = 9 + 4,
case file:read(Fd, HeaderSize) of
{ok, <<?MAGIC, ?VERSION:8/unsigned, Crc:32/integer,
MetaSize:32/unsigned>>} ->
case file:read(Fd, MetaSize) of
{ok, MetaBin} ->
{ok, binary_to_term(MetaBin), Crc};
Err ->
Err
end;
{ok, <<?MAGIC, Version:8/unsigned, _:32/integer, _/binary>>} ->
{error, {invalid_version, Version}};
{ok, _} ->
{error, invalid_format};
eof ->
{error, unexpected_eof_when_parsing_header};
Err ->
Err
end.
validate(Crc, Data) ->
case erlang:crc32(Data) of
Crc ->
parse_snapshot(Data);
_ ->
{error, checksum_error}
end.
parse_snapshot(<<MetaSize:32/unsigned, MetaBin:MetaSize/binary,
Rest/binary>>) ->
Meta = binary_to_term(MetaBin),
{ok, Meta, binary_to_term(Rest)}.
filename(Dir) ->
filename:join(Dir, "snapshot.dat").
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.