Packages
ra
1.0.7
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
%% @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 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.