summaryrefslogtreecommitdiff
blob: 1f784d39cb594c0d8f2c61a8765fd36a70629cb7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
-module(shr_timed_cache).
-behavior(gen_server).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% 'gen_server' Exports
-export
(
   [
      init/1,
      handle_cast/2,
      handle_call/3, %% No reply will ever be given.
      terminate/2,
      code_change/3,
      format_status/2,
      handle_info/2
   ]
).

%%%% Actual Interface
% TODO: include all fetch operations of ataxia_client.
-export
(
   [
      fetch/3,
      update/4,
      invalidate/3
   ]
).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec add_to_cache
   (
      atom(),
      ataxia_security:user(),
      ataxia_id:type()
   )
   -> any().
add_to_cache (DB, Owner, ObjectID) ->
   {ok, TimerPID} = gen_server:start(?MODULE, {DB, {Owner, ObjectID}}, []),
   {ok, Data} = ataxia_client:fetch(DB, Owner, ObjectID),
   ets:insert(DB, {{Owner, ObjectID}, TimerPID, Data}),
   Data.

-spec add_update_to_cache
   (
      atom(),
      ataxia_security:user(),
      ataxia_id:type(),
      any()
   )
   -> 'ok'.
add_update_to_cache (DB, Owner, ObjectID, Data) ->
   {ok, TimerPID} = gen_server:start(?MODULE, {DB, {Owner, ObjectID}}, []),
   ets:insert(DB, {{Owner, ObjectID}, TimerPID, Data}),
   ok.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% 'gen_server' functions
init ({DB, ObjectID}) ->
   io:format("~nCache entry added: ~p.~n", [{DB, ObjectID}]),
   {ok, {DB, ObjectID}, shr_timed_caches_manager:get_timeout()}.

handle_call (invalidate, _, State) ->
   {stop, normal, State};
handle_call (ping, _, State) ->
   {noreply, State, shr_timed_caches_manager:get_timeout()}.

handle_cast (invalidate, State) ->
   {stop, normal, State};
handle_cast (ping, State) ->
   {noreply, State, shr_timed_caches_manager:get_timeout()}.

terminate (_, {DB, ObjectID}) ->
   io:format
   (
      "~nCache entry timed out or was invalidated: ~p.~n",
      [{DB, ObjectID}]
   ),
   ets:delete(DB, ObjectID).

code_change (_, State, _) ->
   {ok, State}.

format_status (_, [_, State]) ->
   [{data, [{"State", State}]}].

handle_info(timeout, State) ->
   {stop, normal, State};
handle_info(_, {DB, ObjectID}) ->
   {noreply, {DB, ObjectID}, shr_timed_caches_manager:get_timeout()}.

%%%% Interface Functions
-spec fetch (atom(), ataxia_security:user(), ataxia_id:type()) -> any().
fetch (DB, Owner, ObjectID) ->
   io:format("~nfetch from cache: ~p.~n", [{DB, {Owner, ObjectID}}]),
   case ets:lookup(DB, {Owner, ObjectID}) of
      [] -> add_to_cache(DB, Owner, ObjectID);

      [{_, TimerPID, Data}] ->
         gen_server:cast(TimerPID, ping),
         Data
   end.

-spec update (atom(), ataxia_security:user(), ataxia_id:type(), any()) -> 'ok'.
update (DB, Owner, ObjectID, Data) ->
   io:format("~nUpdating cache: ~p.~n", [{DB, {Owner, ObjectID}}]),
   case ets:lookup(DB, {Owner, ObjectID}) of
      [] -> ok;

      [{_OwnerID, TimerPID, _Data}] ->
         gen_server:stop(TimerPID)
   end,
   add_update_to_cache(DB, Owner, ObjectID, Data).

-spec invalidate (atom(), ataxia_security:user(), ataxia_id:type()) -> 'ok'.
invalidate (DB, Owner, ObjectID) ->
   case ets:lookup(DB, {Owner, ObjectID}) of
      [] ->
         io:format
         (
            "~nInvalidation request on non-stored entry: ~p.~n",
            [{DB, Owner, ObjectID}]
         ),
         ok;

      [{_, TimerPID, _}] ->
         io:format
         (
            "~nInvalidation request on stored entry: ~p.~n",
            [{DB, Owner, ObjectID}]
         ),
         gen_server:stop(TimerPID),
         ok
   end.