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
|
-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
-export
(
[
fetch/3,
update/4,
invalidate/3
]
).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec add_to_cache (atom(), any(), any()) -> any().
add_to_cache (DB, Owner, ObjectID) ->
{ok, TimerPID} = gen_server:start(?MODULE, {DB, {Owner, ObjectID}}, []),
{ok, Data} = shr_database:fetch(DB, ObjectID, Owner),
ets:insert(DB, {{Owner, ObjectID}, TimerPID, Data}),
Data.
-spec add_update_to_cache (atom(), any(), any(), 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(), any(), any()) -> 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(), any(), any(), 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(), any(), any()) -> '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.
|