Current section
Files
Jump to
Current section
Files
src/timestamp.erl
%%%---------------------------------------------------------------------------
%%% Copyright (C) 2021, Fix Solution All Rights Reserved
%%% Unauthorized copy of this file is through any medium strictly not allowed.
%%%
%%% Authors : Alpha Shaw <shawalpha5@gmail.com>
%%% Created : 20 Apr 2021 by <shawalpha5@gmail.com>
%%% Purpose :
%%%
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
%%% You may obtain a copy of the License at
%%%
%%% http://www.apache.org/licenses/LICENSE-2.0
%%%
%%% Unless required by applicable law or agreed to in writing, software
%%% distributed under the License is distributed on an "AS IS" BASIS,
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%%% See the License for the specific language governing permissions and
%%% limitations under the License.
%%%---------------------------------------------------------------------------
-module(timestamp).
-author("Alpha Umaru Shaw").
-behaviour(gen_server).
%% API
-export([start_link/0]).
-export([since_epoch/0, time_units/0, last_timestamp/0]).
%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-define(SERVER, ?MODULE).
-define(TS_TABLE, ts_table).
-define(LAST_TS, last_ts).
-define(TIMESTAMP_SAVE_INTERVAL, 100).
-record(state, {
dets_table,
dets_file
}).
%%%===================================================================
%%% API
%%%===================================================================
time_units() ->
Ms = ew_time:micros(),
{Ms, erlang:round(Ms / 1000)}.
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
last_timestamp() ->
gen_server:call(?SERVER, read_last_ts, 5000).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([]) ->
process_flag(trap_exit, true),
File = "/tmp/" ++ atom_to_list(node())
++ "/" ++ "flake-timestamp",
case touch_dirs(File) of
{error, Reason} ->
{error, Reason};
_ ->
{ok, Table} =
dets:open_file(?TS_TABLE, [
{estimated_no_objects, 10},
{type, set},
{file, File}
]),
case read_last_timestamp(Table) of
{ok, LTS} ->
CurrentTs = ew_time:millis(),
if LTS > CurrentTs ->
{stop, clock_is_backward};
true ->
erlang:send_after(?TIMESTAMP_SAVE_INTERVAL, self(), write_ts),
{ok, #state{dets_table = Table, dets_file = File}}
end
end
end.
handle_call(read_last_ts, _From, #state{dets_table = Table} = State) ->
{reply, read_last_timestamp(Table), State};
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(_Request, State) ->
{noreply, State}.
handle_info(write_ts, #state{dets_table = Table} = State) ->
{ok, _} = write_last_ts(Table),
erlang:send_after(?TIMESTAMP_SAVE_INTERVAL, self(), write_ts),
{noreply, State};
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, #state{dets_table = Table}) ->
catch dets:close(Table),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
read_last_timestamp(Table) ->
case dets:lookup(Table, ?LAST_TS) of
[{?LAST_TS, TS}] when is_integer(TS) ->
{ok, TS};
_ -> write_last_ts(Table)
end.
write_last_ts(Table) ->
TS = ew_time:millis(),
ok = dets:insert(Table, {?LAST_TS, TS}),
{ok, TS}.
touch_dirs(File) when is_list(File) ->
case filelib:ensure_dir(File) of
ok ->
ok;
{error, Reason} ->
{error, Reason}
end.
since_epoch() ->
erlang:error(not_implemented).