Skip to content

Commit 5838c2b

Browse files
authored
fix: Handle connection errors during read operations. (#163)
This PR adds error handling for read operations from the redis persistent store. This is an incremental step in addressing #161. In the case we cannot read from redis, we will treat that as a flag not being found. If we cannot read from redis when doing an initialization check, then we will consider the store not to have been initialized. This addresses the primary issues with daemon mode, but it doesn't address persistent store errors in non-daemon mode, or other cases that could cause the redis process to exit. Subsequent PRs will address these issues. The detailed variation interface does not report why a flag cannot be found, and isn't the correct avenue for doing so, and as such we may have to consider supporting the data store status interface. This is also not tackled in this PR.
1 parent 0961922 commit 5838c2b

File tree

3 files changed

+48
-25
lines changed

3 files changed

+48
-25
lines changed

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ run:
2222
doc:
2323
@$(REBAR3) edoc
2424

25-
tests:
26-
docker run --name ld-test-redis -p 6379:6379 -d redis
25+
tests: clean-redis
26+
docker run --name ld-test-redis -p 6379:6379 -d redis:7.2
2727
@$(REBAR3) ct --dir="test,test-redis" --logdir logs/ct
2828
docker rm --force ld-test-redis
2929

@@ -40,7 +40,7 @@ tls-tests:
4040

4141
#This is for local debugging if your tests fail and the Redis Docker container is not torn down properly
4242
clean-redis:
43-
docker rm --force ld-test-redis
43+
docker rm --force ld-test-redis 2> /dev/null || true
4444

4545
colon := :
4646
build-contract-tests:

rebar.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
{test, [
1515
{deps, [
1616
{bookish_spork, "0.3.5"},
17-
{cowboy, "2.8.0"},
17+
{cowboy, "2.10.0"},
1818
{meck, "0.9.2"}
1919
]},
2020
{extra_src_dirs, [{"test", [{recursive, true}]}]}

src/ldclient_storage_redis_server.erl

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,16 @@ empty_bucket(true, Bucket, Client, Prefix) ->
236236
all_items(false, Bucket, _Client, _Prefix) ->
237237
{error, bucket_not_found, "Redis hash " ++ atom_to_list(Bucket) ++ " does not exist."};
238238
all_items(true, Bucket, Client, Prefix) ->
239-
{ok, Values} = eredis:q(Client, ["HGETALL", bucket_name(Prefix, Bucket)]),
240-
NullFilter = [<<"null">>],
241-
NewValues = lists:filter(fun(Elem) ->
242-
not lists:member(Elem, NullFilter) end, Values), %This removes the initial null key and value
243-
pairs(NewValues, Bucket).
239+
case eredis:q(Client, ["HGETALL", bucket_name(Prefix, Bucket)]) of
240+
{ok, Values} ->
241+
NullFilter = [<<"null">>],
242+
NewValues = lists:filter(fun(Elem) ->
243+
not lists:member(Elem, NullFilter) end, Values), %This removes the initial null key and value
244+
pairs(NewValues, Bucket);
245+
{error, Reason} ->
246+
error_logger:error_msg("Redis error listing all items in bucket ~p: ~s", [Bucket, format_error(Reason)]),
247+
[]
248+
end.
244249

245250
pairs([A, B | L], Bucket) ->
246251
Decoded = jsx:decode(B, [return_maps]),
@@ -265,19 +270,24 @@ pairs([], _Bucket) -> [].
265270
lookup_key(false, _Key, Bucket, _Client, _Prefix) ->
266271
{error, bucket_not_found, "Redis hash " ++ atom_to_list(Bucket) ++ " does not exist."};
267272
lookup_key(true, Key, Bucket, Client, Prefix) ->
268-
{ok, Value} = eredis:q(Client, ["HGET", bucket_name(Prefix, Bucket), Key]),
269-
if
270-
(Value == undefined) -> [];
271-
true ->
272-
Decoded = jsx:decode(Value, [return_maps]),
273+
case eredis:q(Client, ["HGET", bucket_name(Prefix, Bucket), Key]) of
274+
{ok, Value} ->
273275
if
274-
Bucket == features ->
275-
Parsed = ldclient_flag:new(Decoded),
276-
[{Key, Parsed}];
277-
Bucket == segments ->
278-
Parsed = ldclient_segment:new(Decoded),
279-
[{Key, Parsed}]
280-
end
276+
(Value == undefined) -> [];
277+
true ->
278+
Decoded = jsx:decode(Value, [return_maps]),
279+
if
280+
Bucket == features ->
281+
Parsed = ldclient_flag:new(Decoded),
282+
[{Key, Parsed}];
283+
Bucket == segments ->
284+
Parsed = ldclient_segment:new(Decoded),
285+
[{Key, Parsed}]
286+
end
287+
end;
288+
{error, Reason} ->
289+
error_logger:error_msg("Redis error looking up key ~p in bucket ~p: ~s", [Key, Bucket, format_error(Reason)]),
290+
[]
281291
end.
282292

283293
%% @doc Upsert key value pairs in bucket
@@ -330,15 +340,28 @@ delete_key(true, Key, Bucket, Client, Prefix) ->
330340
bucket_name(Prefix, Bucket) ->
331341
lists:concat([Prefix, ":", Bucket]).
332342

343+
%% @doc Format Redis error for logging
344+
%% @private
345+
%%
346+
%% @end
347+
-spec format_error(Reason :: no_connection | binary()) -> string().
348+
format_error(no_connection) -> "no connection to Redis";
349+
format_error(Reason) when is_binary(Reason) -> binary_to_list(Reason).
350+
333351
-spec set_init(Client :: client_pid(), Prefix :: string()) -> ok.
334352
set_init(Client, Prefix) ->
335353
{ok, _} = eredis:q(Client, ["SET", lists:concat([Prefix, ":$inited"]), ""]),
336354
ok.
337355

338356
-spec get_init(Client :: client_pid(), Prefix :: string()) -> boolean().
339357
get_init(Client, Prefix) ->
340-
{ok, Value} = eredis:q(Client, ["GET", lists:concat([Prefix, ":$inited"])]),
341-
case Value of
342-
undefined -> false;
343-
_ -> true
358+
case eredis:q(Client, ["GET", lists:concat([Prefix, ":$inited"])]) of
359+
{ok, Value} ->
360+
case Value of
361+
undefined -> false;
362+
_ -> true
363+
end;
364+
{error, Reason} ->
365+
error_logger:error_msg("Redis error getting init flag: ~s", [format_error(Reason)]),
366+
false
344367
end.

0 commit comments

Comments
 (0)