riak-client 1.4.5 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/Gemfile +0 -1
  4. data/{LICENSE → LICENSE.md} +0 -0
  5. data/README.markdown +211 -66
  6. data/RELEASE_NOTES.md +22 -47
  7. data/Rakefile +45 -0
  8. data/lib/riak.rb +1 -1
  9. data/lib/riak/bucket.rb +2 -2
  10. data/lib/riak/client.rb +22 -195
  11. data/lib/riak/client/beefcake/crdt_loader.rb +127 -0
  12. data/lib/riak/client/beefcake/crdt_operator.rb +222 -0
  13. data/lib/riak/client/beefcake/footer +4 -0
  14. data/lib/riak/client/beefcake/header +6 -0
  15. data/lib/riak/client/beefcake/message_codes.rb +29 -0
  16. data/lib/riak/client/beefcake/message_overlay.rb +61 -0
  17. data/lib/riak/client/beefcake/messages.rb +733 -371
  18. data/lib/riak/client/beefcake/object_methods.rb +1 -1
  19. data/lib/riak/client/beefcake/protocol.rb +105 -0
  20. data/lib/riak/client/beefcake/socket.rb +243 -0
  21. data/lib/riak/client/beefcake_protobuffs_backend.rb +262 -122
  22. data/lib/riak/client/node.rb +4 -75
  23. data/lib/riak/client/protobuffs_backend.rb +6 -14
  24. data/lib/riak/client/search.rb +0 -64
  25. data/lib/riak/client/yokozuna.rb +52 -0
  26. data/lib/riak/counter.rb +1 -1
  27. data/lib/riak/crdt.rb +21 -0
  28. data/lib/riak/crdt/base.rb +97 -0
  29. data/lib/riak/crdt/batch_counter.rb +19 -0
  30. data/lib/riak/crdt/batch_map.rb +41 -0
  31. data/lib/riak/crdt/counter.rb +71 -0
  32. data/lib/riak/crdt/inner_counter.rb +74 -0
  33. data/lib/riak/crdt/inner_flag.rb +42 -0
  34. data/lib/riak/crdt/inner_map.rb +53 -0
  35. data/lib/riak/crdt/inner_register.rb +26 -0
  36. data/lib/riak/crdt/inner_set.rb +95 -0
  37. data/lib/riak/crdt/map.rb +88 -0
  38. data/lib/riak/crdt/operation.rb +19 -0
  39. data/lib/riak/crdt/set.rb +156 -0
  40. data/lib/riak/crdt/typed_collection.rb +131 -0
  41. data/lib/riak/errors/base.rb +9 -0
  42. data/lib/riak/errors/connection_error.rb +44 -0
  43. data/lib/riak/errors/crdt_error.rb +18 -0
  44. data/lib/riak/errors/failed_request.rb +56 -0
  45. data/lib/riak/errors/protobuffs_error.rb +11 -0
  46. data/lib/riak/i18n.rb +2 -0
  47. data/lib/riak/json.rb +1 -1
  48. data/lib/riak/locale/en.yml +26 -1
  49. data/lib/riak/locale/fr.yml +0 -1
  50. data/lib/riak/map_reduce.rb +1 -1
  51. data/lib/riak/map_reduce/results.rb +1 -1
  52. data/lib/riak/multiget.rb +1 -2
  53. data/lib/riak/rcontent.rb +8 -3
  54. data/lib/riak/robject.rb +2 -8
  55. data/lib/riak/secondary_index.rb +4 -4
  56. data/lib/riak/serializers.rb +1 -1
  57. data/lib/riak/util/escape.rb +3 -5
  58. data/lib/riak/version.rb +1 -1
  59. data/lib/riak/walk_spec.rb +7 -3
  60. data/riak-client.gemspec +10 -8
  61. data/spec/fixtures/bitcask.txt +25 -0
  62. data/spec/integration/riak/bucket_types_spec.rb +61 -0
  63. data/spec/integration/riak/counters_spec.rb +17 -32
  64. data/spec/integration/riak/crdt_spec.rb +181 -0
  65. data/spec/integration/riak/crdt_validation/map_spec.rb +63 -0
  66. data/spec/integration/riak/crdt_validation/set_spec.rb +122 -0
  67. data/spec/integration/riak/protobuffs_backends_spec.rb +9 -26
  68. data/spec/integration/riak/security_spec.rb +94 -0
  69. data/spec/integration/riak/threading_spec.rb +24 -67
  70. data/spec/integration/yokozuna/index_spec.rb +61 -0
  71. data/spec/integration/yokozuna/queries_spec.rb +116 -0
  72. data/spec/integration/yokozuna/schema_spec.rb +49 -0
  73. data/spec/riak/beefcake_protobuffs_backend/crdt_operator_spec.rb +222 -0
  74. data/spec/riak/beefcake_protobuffs_backend/object_methods_spec.rb +4 -4
  75. data/spec/riak/beefcake_protobuffs_backend/protocol_spec.rb +189 -0
  76. data/spec/riak/beefcake_protobuffs_backend/socket_spec.rb +151 -0
  77. data/spec/riak/beefcake_protobuffs_backend_spec.rb +68 -106
  78. data/spec/riak/bucket_spec.rb +81 -77
  79. data/spec/riak/client_spec.rb +43 -340
  80. data/spec/riak/core_ext/to_param_spec.rb +2 -2
  81. data/spec/riak/counter_spec.rb +20 -20
  82. data/spec/riak/crdt/counter_spec.rb +52 -0
  83. data/spec/riak/crdt/inner_counter_spec.rb +21 -0
  84. data/spec/riak/crdt/inner_flag_spec.rb +39 -0
  85. data/spec/riak/crdt/inner_map_spec.rb +47 -0
  86. data/spec/riak/crdt/inner_register_spec.rb +40 -0
  87. data/spec/riak/crdt/inner_set_spec.rb +33 -0
  88. data/spec/riak/crdt/map_spec.rb +77 -0
  89. data/spec/riak/crdt/set_spec.rb +58 -0
  90. data/spec/riak/crdt/shared_examples.rb +74 -0
  91. data/spec/riak/crdt/typed_collection_spec.rb +231 -0
  92. data/spec/riak/escape_spec.rb +33 -37
  93. data/spec/riak/feature_detection_spec.rb +45 -45
  94. data/spec/riak/index_collection_spec.rb +12 -12
  95. data/spec/riak/link_spec.rb +34 -34
  96. data/spec/riak/list_buckets_spec.rb +7 -7
  97. data/spec/riak/map_reduce/filter_builder_spec.rb +6 -6
  98. data/spec/riak/map_reduce/phase_spec.rb +35 -35
  99. data/spec/riak/map_reduce_spec.rb +89 -87
  100. data/spec/riak/multiget_spec.rb +20 -15
  101. data/spec/riak/node_spec.rb +5 -152
  102. data/spec/riak/robject_spec.rb +95 -108
  103. data/spec/riak/search_spec.rb +17 -139
  104. data/spec/riak/secondary_index_spec.rb +49 -49
  105. data/spec/riak/serializers_spec.rb +9 -9
  106. data/spec/riak/stamp_spec.rb +9 -9
  107. data/spec/riak/walk_spec_spec.rb +46 -46
  108. data/spec/spec_helper.rb +14 -22
  109. data/spec/support/certs/README.md +13 -0
  110. data/spec/support/certs/ca.crt +22 -0
  111. data/spec/support/certs/client.crt +95 -0
  112. data/spec/support/certs/client.key +27 -0
  113. data/spec/support/certs/empty_ca.crt +21 -0
  114. data/spec/support/certs/server.crl +13 -0
  115. data/spec/support/certs/server.crt +95 -0
  116. data/spec/support/certs/server.key +27 -0
  117. data/spec/support/integration_setup.rb +1 -1
  118. data/spec/support/search_corpus_setup.rb +29 -8
  119. data/spec/support/test_client.rb +46 -0
  120. data/spec/support/test_client.yml.example +10 -0
  121. data/spec/support/unified_backend_examples.rb +104 -83
  122. data/spec/support/version_filter.rb +2 -2
  123. data/spec/support/wait_until.rb +14 -0
  124. metadata +134 -132
  125. data/erl_src/riak_kv_test014_backend.beam +0 -0
  126. data/erl_src/riak_kv_test014_backend.erl +0 -189
  127. data/erl_src/riak_kv_test_backend.beam +0 -0
  128. data/erl_src/riak_kv_test_backend.erl +0 -731
  129. data/erl_src/riak_search_test_backend.beam +0 -0
  130. data/erl_src/riak_search_test_backend.erl +0 -175
  131. data/lib/riak/client/excon_backend.rb +0 -172
  132. data/lib/riak/client/http_backend.rb +0 -413
  133. data/lib/riak/client/http_backend/bucket_streamer.rb +0 -15
  134. data/lib/riak/client/http_backend/chunked_json_streamer.rb +0 -42
  135. data/lib/riak/client/http_backend/configuration.rb +0 -227
  136. data/lib/riak/client/http_backend/key_streamer.rb +0 -15
  137. data/lib/riak/client/http_backend/object_methods.rb +0 -114
  138. data/lib/riak/client/http_backend/request_headers.rb +0 -34
  139. data/lib/riak/client/http_backend/transport_methods.rb +0 -201
  140. data/lib/riak/client/instrumentation.rb +0 -25
  141. data/lib/riak/client/net_http_backend.rb +0 -82
  142. data/lib/riak/cluster.rb +0 -151
  143. data/lib/riak/failed_request.rb +0 -81
  144. data/lib/riak/instrumentation.rb +0 -6
  145. data/lib/riak/node.rb +0 -40
  146. data/lib/riak/node/configuration.rb +0 -304
  147. data/lib/riak/node/console.rb +0 -133
  148. data/lib/riak/node/control.rb +0 -207
  149. data/lib/riak/node/defaults.rb +0 -85
  150. data/lib/riak/node/generation.rb +0 -127
  151. data/lib/riak/node/log.rb +0 -34
  152. data/lib/riak/node/version.rb +0 -29
  153. data/lib/riak/search.rb +0 -3
  154. data/lib/riak/test_server.rb +0 -89
  155. data/lib/riak/util/headers.rb +0 -32
  156. data/lib/riak/util/multipart.rb +0 -52
  157. data/lib/riak/util/multipart/stream_parser.rb +0 -62
  158. data/spec/fixtures/munchausen.txt +0 -1033
  159. data/spec/integration/riak/cluster_spec.rb +0 -88
  160. data/spec/integration/riak/http_backends_spec.rb +0 -180
  161. data/spec/integration/riak/node_spec.rb +0 -170
  162. data/spec/integration/riak/test_server_spec.rb +0 -57
  163. data/spec/riak/excon_backend_spec.rb +0 -102
  164. data/spec/riak/headers_spec.rb +0 -21
  165. data/spec/riak/http_backend/configuration_spec.rb +0 -273
  166. data/spec/riak/http_backend/object_methods_spec.rb +0 -243
  167. data/spec/riak/http_backend/transport_methods_spec.rb +0 -97
  168. data/spec/riak/http_backend_spec.rb +0 -367
  169. data/spec/riak/instrumentation_spec.rb +0 -167
  170. data/spec/riak/multipart_spec.rb +0 -23
  171. data/spec/riak/net_http_backend_spec.rb +0 -15
  172. data/spec/riak/stream_parser_spec.rb +0 -53
  173. data/spec/support/drb_mock_server.rb +0 -39
  174. data/spec/support/http_backend_implementation_examples.rb +0 -253
  175. data/spec/support/mock_server.rb +0 -81
  176. data/spec/support/mocks.rb +0 -4
  177. data/spec/support/riak_test.rb +0 -77
  178. data/spec/support/sometimes.rb +0 -46
  179. data/spec/support/test_server.rb +0 -61
  180. data/spec/support/test_server.yml.example +0 -14
@@ -1,189 +0,0 @@
1
- %% -------------------------------------------------------------------
2
- %%
3
- %% riak_kv_test_backend: storage engine based on ETS tables
4
- %%
5
- %% Copyright (c) 2007-2010 Basho Technologies, Inc. All Rights Reserved.
6
- %%
7
- %% This file is provided to you under the Apache License,
8
- %% Version 2.0 (the "License"); you may not use this file
9
- %% except in compliance with the License. You may obtain
10
- %% a copy of the License at
11
- %%
12
- %% http://www.apache.org/licenses/LICENSE-2.0
13
- %%
14
- %% Unless required by applicable law or agreed to in writing,
15
- %% software distributed under the License is distributed on an
16
- %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
- %% KIND, either express or implied. See the License for the
18
- %% specific language governing permissions and limitations
19
- %% under the License.
20
- %%
21
- %% -------------------------------------------------------------------
22
-
23
- % @doc riak_kv_test_backend is a Riak storage backend using ets that
24
- % exposes a reset function for efficiently clearing stored data.
25
-
26
- -module(riak_kv_test014_backend).
27
- -behavior(riak_kv_backend).
28
- -behavior(gen_server).
29
- -ifdef(TEST).
30
- -include_lib("eunit/include/eunit.hrl").
31
- -endif.
32
- -export([start/2,stop/1,get/2,put/3,list/1,list_bucket/2,delete/2,
33
- is_empty/1, drop/1, fold/3, callback/3, reset/0]).
34
-
35
- -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
36
- terminate/2, code_change/3]).
37
-
38
-
39
- % @type state() = term().
40
- -record(state, {t, p}).
41
-
42
- % @spec start(Partition :: integer(), Config :: proplist()) ->
43
- % {ok, state()} | {{error, Reason :: term()}, state()}
44
- start(Partition, _Config) ->
45
- gen_server:start_link(?MODULE, [Partition], []).
46
-
47
- % @spec reset() -> ok | {error, timeout}
48
- reset() ->
49
- Pids = lists:foldl(fun(Item, Acc) ->
50
- case lists:prefix("test_backend", atom_to_list(Item)) of
51
- true -> [whereis(Item)|Acc];
52
- _ -> Acc
53
- end
54
- end, [], registered()),
55
- [gen_server:cast(Pid,{reset, self()})|| Pid <- Pids],
56
- receive_reset(Pids).
57
-
58
- receive_reset([]) -> ok;
59
- receive_reset(Pids) ->
60
- receive
61
- {reset, Pid} ->
62
- receive_reset(lists:delete(Pid, Pids))
63
- after 1000 ->
64
- {error, timeout}
65
- end.
66
-
67
- %% @private
68
- init([Partition]) ->
69
- PName = list_to_atom("test_backend" ++ integer_to_list(Partition)),
70
- P = list_to_atom(integer_to_list(Partition)),
71
- register(PName, self()),
72
- {ok, #state{t=ets:new(P,[]), p=P}}.
73
-
74
- %% @private
75
- handle_cast({reset,From}, State) ->
76
- ets:delete_all_objects(State#state.t),
77
- From ! {reset, self()},
78
- {noreply, State};
79
- handle_cast(_, State) -> {noreply, State}.
80
-
81
- %% @private
82
- handle_call(stop,_From,State) -> {reply, srv_stop(State), State};
83
- handle_call({get,BKey},_From,State) -> {reply, srv_get(State,BKey), State};
84
- handle_call({put,BKey,Val},_From,State) ->
85
- {reply, srv_put(State,BKey,Val),State};
86
- handle_call({delete,BKey},_From,State) -> {reply, srv_delete(State,BKey),State};
87
- handle_call(list,_From,State) -> {reply, srv_list(State), State};
88
- handle_call({list_bucket,Bucket},_From,State) ->
89
- {reply, srv_list_bucket(State, Bucket), State};
90
- handle_call(is_empty, _From, State) ->
91
- {reply, ets:info(State#state.t, size) =:= 0, State};
92
- handle_call(drop, _From, State) ->
93
- ets:delete(State#state.t),
94
- {reply, ok, State};
95
- handle_call({fold, Fun0, Acc}, _From, State) ->
96
- Fun = fun({{B,K}, V}, AccIn) -> Fun0({B,K}, V, AccIn) end,
97
- Reply = ets:foldl(Fun, Acc, State#state.t),
98
- {reply, Reply, State}.
99
-
100
- % @spec stop(state()) -> ok | {error, Reason :: term()}
101
- stop(SrvRef) -> gen_server:call(SrvRef,stop).
102
- srv_stop(State) ->
103
- true = ets:delete(State#state.t),
104
- ok.
105
-
106
- % get(state(), riak_object:bkey()) ->
107
- % {ok, Val :: binary()} | {error, Reason :: term()}
108
- % key must be 160b
109
- get(SrvRef, BKey) -> gen_server:call(SrvRef,{get,BKey}).
110
- srv_get(State, BKey) ->
111
- case ets:lookup(State#state.t,BKey) of
112
- [] -> {error, notfound};
113
- [{BKey,Val}] -> {ok, Val};
114
- Err -> {error, Err}
115
- end.
116
-
117
- % put(state(), riak_object:bkey(), Val :: binary()) ->
118
- % ok | {error, Reason :: term()}
119
- % key must be 160b
120
- put(SrvRef, BKey, Val) -> gen_server:call(SrvRef,{put,BKey,Val}).
121
- srv_put(State,BKey,Val) ->
122
- true = ets:insert(State#state.t, {BKey,Val}),
123
- ok.
124
-
125
- % delete(state(), riak_object:bkey()) ->
126
- % ok | {error, Reason :: term()}
127
- % key must be 160b
128
- delete(SrvRef, BKey) -> gen_server:call(SrvRef,{delete,BKey}).
129
- srv_delete(State, BKey) ->
130
- true = ets:delete(State#state.t, BKey),
131
- ok.
132
-
133
- % list(state()) -> [riak_object:bkey()]
134
- list(SrvRef) -> gen_server:call(SrvRef,list).
135
- srv_list(State) ->
136
- MList = ets:match(State#state.t,{'$1','_'}),
137
- list(MList,[]).
138
- list([],Acc) -> Acc;
139
- list([[K]|Rest],Acc) -> list(Rest,[K|Acc]).
140
-
141
- % list_bucket(term(), Bucket :: riak_object:bucket()) -> [Key :: binary()]
142
- list_bucket(SrvRef, Bucket) ->
143
- gen_server:call(SrvRef,{list_bucket, Bucket}).
144
- srv_list_bucket(State, {filter, Bucket, Fun}) ->
145
- MList = lists:filter(Fun, ets:match(State#state.t,{{Bucket,'$1'},'_'})),
146
- list(MList,[]);
147
- srv_list_bucket(State, Bucket) ->
148
- case Bucket of
149
- '_' -> MatchSpec = {{'$1','_'},'_'};
150
- _ -> MatchSpec = {{Bucket,'$1'},'_'}
151
- end,
152
- MList = ets:match(State#state.t,MatchSpec),
153
- list(MList,[]).
154
-
155
- is_empty(SrvRef) -> gen_server:call(SrvRef, is_empty).
156
-
157
- drop(SrvRef) -> gen_server:call(SrvRef, drop).
158
-
159
- fold(SrvRef, Fun, Acc0) -> gen_server:call(SrvRef, {fold, Fun, Acc0}, infinity).
160
-
161
- %% Ignore callbacks for other backends so multi backend works
162
- callback(_State, _Ref, _Msg) ->
163
- ok.
164
-
165
- %% @private
166
- handle_info(_Msg, State) -> {noreply, State}.
167
-
168
- %% @private
169
- terminate(_Reason, _State) -> ok.
170
-
171
- %% @private
172
- code_change(_OldVsn, State, _Extra) -> {ok, State}.
173
-
174
- %%
175
- %% Test
176
- %%
177
- -ifdef(TEST).
178
-
179
- % @private
180
- simple_test() ->
181
- riak_kv_backend:standard_test(?MODULE, []).
182
-
183
- -ifdef(EQC).
184
- %% @private
185
- eqc_test() ->
186
- ?assertEqual(true, backend_eqc:test(?MODULE, true)).
187
-
188
- -endif. % EQC
189
- -endif. % TEST
@@ -1,731 +0,0 @@
1
- %% -------------------------------------------------------------------
2
- %%
3
- %% riak_kv_test_backend: storage engine using ETS tables, for use in testing.
4
- %%
5
- %% Copyright (c) 2007-2011 Basho Technologies, Inc. All Rights Reserved.
6
- %%
7
- %% This file is provided to you under the Apache License,
8
- %% Version 2.0 (the "License"); you may not use this file
9
- %% except in compliance with the License. You may obtain
10
- %% a copy of the License at
11
- %%
12
- %% http://www.apache.org/licenses/LICENSE-2.0
13
- %%
14
- %% Unless required by applicable law or agreed to in writing,
15
- %% software distributed under the License is distributed on an
16
- %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
- %% KIND, either express or implied. See the License for the
18
- %% specific language governing permissions and limitations
19
- %% under the License.
20
- %%
21
- %% -------------------------------------------------------------------
22
-
23
- %% @doc riak_kv_memory_backend is a Riak storage backend that uses ets
24
- %% tables to store all data in memory.
25
- %%
26
- %% === Configuration Options ===
27
- %%
28
- %% The following configuration options are available for the memory backend.
29
- %% The options should be specified in the `memory_backend' section of your
30
- %% app.config file.
31
- %%
32
- %% <ul>
33
- %% <li>`ttl' - The time in seconds that an object should live before being expired.</li>
34
- %% <li>`max_memory' - The amount of memory in megabytes to limit the backend to.</li>
35
- %% <li>`test' - When `true', exposes the internal ETS tables so that they can be efficiently cleared using {@link reset/3}.</li>
36
- %% </ul>
37
- %%
38
-
39
- -module(riak_kv_test_backend).
40
- -behavior(riak_kv_backend).
41
-
42
- %% KV Backend API
43
- -export([api_version/0,
44
- capabilities/1,
45
- capabilities/2,
46
- start/2,
47
- stop/1,
48
- get/3,
49
- put/5,
50
- delete/4,
51
- drop/1,
52
- fold_buckets/4,
53
- fold_keys/4,
54
- fold_objects/4,
55
- is_empty/1,
56
- status/1,
57
- callback/3]).
58
-
59
- %% "Testing" backend API
60
- -export([reset/0]).
61
-
62
- -ifdef(TEST).
63
- -include_lib("eunit/include/eunit.hrl").
64
- -compile([export_all]).
65
- -endif.
66
-
67
- -define(API_VERSION, 1).
68
- -define(CAPABILITIES, [async_fold, indexes]).
69
-
70
- %% Macros for working with indexes
71
- -define(DELETE_PTN(B,K), {{B,'_','_',K},'_'}).
72
-
73
- %% ETS table name macros so we can break encapsulation for testing
74
- %% mode
75
- -define(DNAME(P), list_to_atom("riak_kv_"++integer_to_list(P))).
76
- -define(INAME(P), list_to_atom("riak_kv_"++integer_to_list(P)++"_i")).
77
- -define(TNAME(P), list_to_atom("riak_kv_"++integer_to_list(P)++"_t")).
78
-
79
- -record(state, {data_ref :: ets:tid(),
80
- index_ref :: ets:tid(),
81
- time_ref :: ets:tid(),
82
- max_memory :: undefined | integer(),
83
- used_memory=0 :: integer(),
84
- ttl :: integer()}).
85
-
86
- -type state() :: #state{}.
87
- -type config() :: [].
88
-
89
- %% ===================================================================
90
- %% Public API
91
- %% ===================================================================
92
-
93
- %% KV Backend API
94
-
95
- %% @doc Return the major version of the
96
- %% current API.
97
- -spec api_version() -> {ok, integer()}.
98
- api_version() ->
99
- case lists:member({capabilities, 1}, riak_kv_backend:behaviour_info(callbacks)) of
100
- true -> % Using 1.1 API or later
101
- {ok, ?API_VERSION};
102
- _ -> % Using 1.0 API
103
- {?API_VERSION, ?CAPABILITIES}
104
- end.
105
-
106
- %% @doc Return the capabilities of the backend.
107
- -spec capabilities(state()) -> {ok, [atom()]}.
108
- capabilities(_) ->
109
- {ok, ?CAPABILITIES}.
110
-
111
- %% @doc Return the capabilities of the backend.
112
- -spec capabilities(riak_object:bucket(), state()) -> {ok, [atom()]}.
113
- capabilities(_, _) ->
114
- {ok, ?CAPABILITIES}.
115
-
116
- %% @doc Start the memory backend
117
- -spec start(integer(), config()) -> {ok, state()}.
118
- %% Bug in riak_kv_vnode in 1.0
119
- start(Partition, [{async_folds,_}=AFolds, Rest]) when is_list(Rest) ->
120
- start(Partition, [AFolds|Rest]);
121
- start(Partition, Config) ->
122
- TTL = get_prop_or_env(ttl, Config, memory_backend),
123
- MemoryMB = get_prop_or_env(max_memory, Config, memory_backend),
124
- TableOpts = case get_prop_or_env(test, Config, memory_backend) of
125
- true ->
126
- [ordered_set, public, named_table];
127
- _ ->
128
- [ordered_set]
129
- end,
130
- case MemoryMB of
131
- undefined ->
132
- MaxMemory = undefined,
133
- TimeRef = undefined;
134
- _ ->
135
- MaxMemory = MemoryMB * 1024 * 1024,
136
- TimeRef = ets:new(?TNAME(Partition), TableOpts)
137
- end,
138
- IndexRef = ets:new(?INAME(Partition), TableOpts),
139
- DataRef = ets:new(?DNAME(Partition), TableOpts),
140
- {ok, #state{data_ref=DataRef,
141
- index_ref=IndexRef,
142
- max_memory=MaxMemory,
143
- time_ref=TimeRef,
144
- ttl=TTL}}.
145
-
146
- %% @doc Stop the memory backend
147
- -spec stop(state()) -> ok.
148
- stop(#state{data_ref=DataRef,
149
- index_ref=IndexRef,
150
- max_memory=MaxMemory,
151
- time_ref=TimeRef}) ->
152
- catch ets:delete(DataRef),
153
- catch ets:delete(IndexRef),
154
- case MaxMemory of
155
- undefined ->
156
- ok;
157
- _ ->
158
- catch ets:delete(TimeRef)
159
- end,
160
- ok.
161
-
162
- %% @doc Retrieve an object from the memory backend
163
- -spec get(riak_object:bucket(), riak_object:key(), state()) ->
164
- {ok, any(), state()} |
165
- {ok, not_found, state()} |
166
- {error, term(), state()}.
167
- get(Bucket, Key, State=#state{data_ref=DataRef,
168
- index_ref=IndexRef,
169
- used_memory=UsedMemory,
170
- max_memory=MaxMemory,
171
- ttl=TTL}) ->
172
- case ets:lookup(DataRef, {Bucket, Key}) of
173
- [] -> {error, not_found, State};
174
- [{{Bucket, Key}, {{ts, Timestamp}, Val}}=Object] ->
175
- case exceeds_ttl(Timestamp, TTL) of
176
- true ->
177
- %% Because we do not have the IndexSpecs, we must
178
- %% delete the object directly and all index
179
- %% entries blindly using match_delete.
180
- ets:delete(DataRef, {Bucket, Key}),
181
- ets:match_delete(IndexRef, ?DELETE_PTN(Bucket, Key)),
182
- case MaxMemory of
183
- undefined ->
184
- UsedMemory1 = UsedMemory;
185
- _ ->
186
- UsedMemory1 = UsedMemory - object_size(Object)
187
- end,
188
- {error, not_found, State#state{used_memory=UsedMemory1}};
189
- false ->
190
- {ok, Val, State}
191
- end;
192
- [{{Bucket, Key}, Val}] ->
193
- {ok, Val, State};
194
- Error ->
195
- {error, Error, State}
196
- end.
197
-
198
- %% @doc Insert an object into the memory backend.
199
- -type index_spec() :: {add, Index, SecondaryKey} | {remove, Index, SecondaryKey}.
200
- -spec put(riak_object:bucket(), riak_object:key(), [index_spec()], binary(), state()) ->
201
- {ok, state()}.
202
- put(Bucket, PrimaryKey, IndexSpecs, Val, State=#state{data_ref=DataRef,
203
- index_ref=IndexRef,
204
- max_memory=MaxMemory,
205
- time_ref=TimeRef,
206
- ttl=TTL,
207
- used_memory=UsedMemory}) ->
208
- Now = now(),
209
- case TTL of
210
- undefined ->
211
- Val1 = Val;
212
- _ ->
213
- Val1 = {{ts, Now}, Val}
214
- end,
215
- {ok, Size} = do_put(Bucket, PrimaryKey, Val1, IndexSpecs, DataRef, IndexRef),
216
- case MaxMemory of
217
- undefined ->
218
- UsedMemory1 = UsedMemory;
219
- _ ->
220
- time_entry(Bucket, PrimaryKey, Now, TimeRef),
221
- Freed = trim_data_table(MaxMemory,
222
- UsedMemory + Size,
223
- DataRef,
224
- TimeRef,
225
- IndexRef,
226
- 0),
227
- UsedMemory1 = UsedMemory + Size - Freed
228
- end,
229
- {ok, State#state{used_memory=UsedMemory1}}.
230
-
231
- %% @doc Delete an object from the memory backend
232
- -spec delete(riak_object:bucket(), riak_object:key(), [index_spec()], state()) ->
233
- {ok, state()}.
234
- delete(Bucket, Key, IndexSpecs, State=#state{data_ref=DataRef,
235
- index_ref=IndexRef,
236
- time_ref=TimeRef,
237
- used_memory=UsedMemory}) ->
238
- case TimeRef of
239
- undefined ->
240
- UsedMemory1 = UsedMemory;
241
- _ ->
242
- %% Lookup the object so we can delete its
243
- %% entry from the time table and account
244
- %% for the memory used.
245
- [Object] = ets:lookup(DataRef, {Bucket, Key}),
246
- case Object of
247
- {_, {{ts, Timestamp}, _}} ->
248
- ets:delete(TimeRef, Timestamp),
249
- UsedMemory1 = UsedMemory - object_size(Object);
250
- _ ->
251
- UsedMemory1 = UsedMemory
252
- end
253
- end,
254
- update_indexes(Bucket, Key, IndexSpecs, IndexRef),
255
- ets:delete(DataRef, {Bucket, Key}),
256
- {ok, State#state{used_memory=UsedMemory1}}.
257
-
258
- %% @doc Fold over all the buckets.
259
- -spec fold_buckets(riak_kv_backend:fold_buckets_fun(),
260
- any(),
261
- [],
262
- state()) -> {ok, any()}.
263
- fold_buckets(FoldBucketsFun, Acc, Opts, #state{data_ref=DataRef}) ->
264
- FoldFun = fold_buckets_fun(FoldBucketsFun),
265
- case lists:member(async_fold, Opts) of
266
- true ->
267
- BucketFolder =
268
- fun() ->
269
- {Acc0, _} = ets:foldl(FoldFun, {Acc, sets:new()}, DataRef),
270
- Acc0
271
- end,
272
- {async, BucketFolder};
273
- false ->
274
- {Acc0, _} = ets:foldl(FoldFun, {Acc, sets:new()}, DataRef),
275
- {ok, Acc0}
276
- end.
277
-
278
- %% @doc Fold over all the keys for one or all buckets.
279
- -spec fold_keys(riak_kv_backend:fold_keys_fun(),
280
- any(),
281
- [{atom(), term()}],
282
- state()) -> {ok, term()} | {async, fun()}.
283
- fold_keys(FoldKeysFun, Acc, Opts, #state{data_ref=DataRef,
284
- index_ref=IndexRef}) ->
285
-
286
- %% Figure out how we should limit the fold: by bucket, by
287
- %% secondary index, or neither (fold across everything.)
288
- Bucket = lists:keyfind(bucket, 1, Opts),
289
- Index = lists:keyfind(index, 1, Opts),
290
-
291
- %% Multiple limiters may exist. Take the most specific limiter,
292
- %% get an appropriate folder function.
293
- Folder = if
294
- Index /= false ->
295
- FoldFun = fold_keys_fun(FoldKeysFun, Index),
296
- get_index_folder(FoldFun, Acc, Index, DataRef, IndexRef);
297
- Bucket /= false ->
298
- FoldFun = fold_keys_fun(FoldKeysFun, Bucket),
299
- get_folder(FoldFun, Acc, DataRef);
300
- true ->
301
- FoldFun = fold_keys_fun(FoldKeysFun, undefined),
302
- get_folder(FoldFun, Acc, DataRef)
303
- end,
304
-
305
- case lists:member(async_fold, Opts) of
306
- true ->
307
- {async, Folder};
308
- false ->
309
- {ok, Folder()}
310
- end.
311
-
312
- %% @doc Fold over all the objects for one or all buckets.
313
- -spec fold_objects(riak_kv_backend:fold_objects_fun(),
314
- any(),
315
- [{atom(), term()}],
316
- state()) -> {ok, any()} | {async, fun()}.
317
- fold_objects(FoldObjectsFun, Acc, Opts, #state{data_ref=DataRef}) ->
318
- Bucket = proplists:get_value(bucket, Opts),
319
- FoldFun = fold_objects_fun(FoldObjectsFun, Bucket),
320
- case lists:member(async_fold, Opts) of
321
- true ->
322
- {async, get_folder(FoldFun, Acc, DataRef)};
323
- false ->
324
- Acc0 = ets:foldl(FoldFun, Acc, DataRef),
325
- {ok, Acc0}
326
- end.
327
-
328
- %% @doc Delete all objects from this memory backend
329
- -spec drop(state()) -> {ok, state()}.
330
- drop(State=#state{data_ref=DataRef,
331
- index_ref=IndexRef,
332
- time_ref=TimeRef}) ->
333
- ets:delete_all_objects(DataRef),
334
- ets:delete_all_objects(IndexRef),
335
- case TimeRef of
336
- undefined ->
337
- ok;
338
- _ ->
339
- ets:delete_all_objects(TimeRef)
340
- end,
341
- {ok, State}.
342
-
343
- %% @doc Returns true if this memory backend contains any
344
- %% non-tombstone values; otherwise returns false.
345
- -spec is_empty(state()) -> boolean().
346
- is_empty(#state{data_ref=DataRef}) ->
347
- ets:info(DataRef, size) =:= 0.
348
-
349
- %% @doc Get the status information for this memory backend
350
- -spec status(state()) -> [{atom(), term()}].
351
- status(#state{data_ref=DataRef,
352
- index_ref=IndexRef,
353
- time_ref=TimeRef}) ->
354
- DataStatus = ets:info(DataRef),
355
- IndexStatus = ets:info(IndexRef),
356
- case TimeRef of
357
- undefined ->
358
- [{data_table_status, DataStatus},
359
- {index_table_status, IndexStatus}];
360
- _ ->
361
- TimeStatus = ets:info(TimeRef),
362
- [{data_table_status, DataStatus},
363
- {index_table_status, IndexStatus},
364
- {time_table_status, TimeStatus}]
365
- end.
366
-
367
- %% @doc Register an asynchronous callback
368
- -spec callback(reference(), any(), state()) -> {ok, state()}.
369
- callback(_Ref, _Msg, State) ->
370
- {ok, State}.
371
-
372
- %% @doc Resets state of all running memory backends on the local
373
- %% node. The `riak_kv' environment variable `memory_backend' must
374
- %% contain the `test' property, set to `true' for this to work.
375
- -spec reset() -> ok | {error, reset_disabled}.
376
- reset() ->
377
- reset(app_helper:get_env(memory_backend, test, app_helper:get_env(riak_kv, test)), app_helper:get_env(riak_kv, storage_backend)).
378
-
379
- reset(true, ?MODULE) ->
380
- {ok, Ring} = riak_core_ring_manager:get_my_ring(),
381
- [ begin
382
- catch ets:delete_all_objects(?DNAME(I)),
383
- catch ets:delete_all_objects(?INAME(I)),
384
- catch ets:delete_all_objects(?TNAME(I))
385
- end || I <- riak_core_ring:my_indices(Ring) ],
386
- ok;
387
- reset(_, _) ->
388
- {error, reset_disabled}.
389
-
390
- %% ===================================================================
391
- %% Internal functions
392
- %% ===================================================================
393
-
394
- %% @TODO Some of these implementations may be suboptimal.
395
- %% Need to do some measuring and testing to refine the
396
- %% implementations.
397
-
398
- %% @private
399
- %% Return a function to fold over the buckets on this backend
400
- fold_buckets_fun(FoldBucketsFun) ->
401
- fun({{Bucket, _}, _}, {Acc, BucketSet}) ->
402
- case sets:is_element(Bucket, BucketSet) of
403
- true ->
404
- {Acc, BucketSet};
405
- false ->
406
- {FoldBucketsFun(Bucket, Acc),
407
- sets:add_element(Bucket, BucketSet)}
408
- end
409
- end.
410
-
411
- %% @private
412
- %% Return a function to fold over keys on this backend
413
- fold_keys_fun(FoldKeysFun, undefined) ->
414
- fun({{Bucket, Key}, _}, Acc) ->
415
- FoldKeysFun(Bucket, Key, Acc);
416
- (_, Acc) ->
417
- Acc
418
- end;
419
- fold_keys_fun(FoldKeysFun, {bucket, FilterBucket}) ->
420
- fun({{Bucket, Key}, _}, Acc) when Bucket == FilterBucket ->
421
- FoldKeysFun(Bucket, Key, Acc);
422
- (_, Acc) ->
423
- Acc
424
- end;
425
- fold_keys_fun(FoldKeysFun, {index, FilterBucket, {eq, <<"$bucket">>, _}}) ->
426
- %% 2I exact match query on special $bucket field...
427
- fold_keys_fun(FoldKeysFun, {bucket, FilterBucket});
428
- fold_keys_fun(FoldKeysFun, {index, FilterBucket, {range, <<"$key">>, _, _}}) ->
429
- %% 2I range query on special $key field...
430
- fold_keys_fun(FoldKeysFun, {bucket, FilterBucket});
431
- fold_keys_fun(FoldKeysFun, {index, FilterBucket, {eq, <<"$key">>, _}}) ->
432
- %% 2I eq query on special $key field...
433
- fold_keys_fun(FoldKeysFun, {bucket, FilterBucket});
434
- fold_keys_fun(FoldKeysFun, {index, _FilterBucket, _Query}) ->
435
- fun({{Bucket, _FilterField, _FilterTerm, Key}, _}, Acc) ->
436
- FoldKeysFun(Bucket, Key, Acc);
437
- (_, Acc) ->
438
- Acc
439
- end.
440
-
441
-
442
- %% @private
443
- %% Return a function to fold over keys on this backend
444
- fold_objects_fun(FoldObjectsFun, undefined) ->
445
- fun({{Bucket, Key}, Value}, Acc) ->
446
- FoldObjectsFun(Bucket, Key, Value, Acc);
447
- (_, Acc) ->
448
- Acc
449
- end;
450
- fold_objects_fun(FoldObjectsFun, FilterBucket) ->
451
- fun({{Bucket, Key}, Value}, Acc) when Bucket == FilterBucket->
452
- FoldObjectsFun(Bucket, Key, Value, Acc);
453
- (_, Acc) ->
454
- Acc
455
- end.
456
-
457
- %% @private
458
- get_folder(FoldFun, Acc, DataRef) ->
459
- fun() ->
460
- ets:foldl(FoldFun, Acc, DataRef)
461
- end.
462
-
463
- %% @private
464
- get_index_folder(Folder, Acc0, {index, Bucket, {eq, <<"$bucket">>, _}}, DataRef, _) ->
465
- %% For the special $bucket index, turn it into a fold over the
466
- %% data table.
467
- fun() ->
468
- key_range_folder(Folder, Acc0, DataRef, {Bucket, <<>>}, Bucket)
469
- end;
470
- get_index_folder(Folder, Acc0, {index, Bucket, {range, <<"$key">>, Min, Max}}, DataRef, _) ->
471
- %% For the special range lookup on the $key index, turn it into a
472
- %% fold on the data table
473
- fun() ->
474
- key_range_folder(Folder, Acc0, DataRef, {Bucket, Min}, {Bucket, Min, Max})
475
- end;
476
- get_index_folder(Folder, Acc0, {index, Bucket, {eq, <<"$key">>, Val}}, DataRef, IndexRef) ->
477
- get_index_folder(Folder, Acc0, {index, Bucket, {range, <<"$key">>, Val, Val}}, DataRef, IndexRef);
478
- get_index_folder(Folder, Acc0, {index, Bucket, {eq, Field, Term}}, _, IndexRef) ->
479
- fun() ->
480
- index_range_folder(Folder, Acc0, IndexRef, {Bucket, Field, Term, undefined}, {Bucket, Field, Term, Term})
481
- end;
482
- get_index_folder(Folder, Acc0, {index, Bucket, {range, Field, Min, Max}}, _, IndexRef) ->
483
- fun() ->
484
- index_range_folder(Folder, Acc0, IndexRef, {Bucket, Field, Min, undefined}, {Bucket, Field, Min, Max})
485
- end.
486
-
487
-
488
- %% Iterates over a range of keys, for the special $key and $bucket
489
- %% indexes.
490
- %% @private
491
- -spec key_range_folder(function(), term(), ets:tid(), {riak_object:bucket(), riak_object:key()}, binary() | {riak_object:bucket(), term(), term()}) -> term().
492
- key_range_folder(Folder, Acc0, DataRef, {B,_}=DataKey, B) ->
493
- case ets:lookup(DataRef, DataKey) of
494
- [] ->
495
- key_range_folder(Folder, Acc0, DataRef, ets:next(DataRef, DataKey), B);
496
- [Object] ->
497
- Acc = Folder(Object, Acc0),
498
- key_range_folder(Folder, Acc, DataRef, ets:next(DataRef, DataKey), B)
499
- end;
500
- key_range_folder(Folder, Acc0, DataRef, {B,K}=DataKey, {B, Min, Max}=Query) when K >= Min, K =< Max ->
501
- case ets:lookup(DataRef, DataKey) of
502
- [] ->
503
- key_range_folder(Folder, Acc0, DataRef, ets:next(DataRef, DataKey), Query);
504
- [Object] ->
505
- Acc = Folder(Object, Acc0),
506
- key_range_folder(Folder, Acc, DataRef, ets:next(DataRef, DataKey), Query)
507
- end;
508
- key_range_folder(_Folder, Acc, _DataRef, _DataKey, _Query) ->
509
- Acc.
510
-
511
- %% Iterates over a range of index postings
512
- index_range_folder(Folder, Acc0, IndexRef, {B, I, V, _K}=IndexKey, {B, I, Min, Max}=Query) when V >= Min, V =< Max ->
513
- case ets:lookup(IndexRef, IndexKey) of
514
- [] ->
515
- %% This will happen on the first iteration, where the key
516
- %% does not exist. In all other cases, ETS will give us a
517
- %% real key from next/2.
518
- index_range_folder(Folder, Acc0, IndexRef, ets:next(IndexRef, IndexKey), Query);
519
- [Posting] ->
520
- Acc = Folder(Posting, Acc0),
521
- index_range_folder(Folder, Acc, IndexRef, ets:next(IndexRef, IndexKey), Query)
522
- end;
523
- index_range_folder(_Folder, Acc, _IndexRef, _IndexKey, _Query) ->
524
- Acc.
525
-
526
-
527
- %% @private
528
- do_put(Bucket, Key, Val, IndexSpecs, DataRef, IndexRef) ->
529
- Object = {{Bucket, Key}, Val},
530
- true = ets:insert(DataRef, Object),
531
- update_indexes(Bucket, Key, IndexSpecs, IndexRef),
532
- {ok, object_size(Object)}.
533
-
534
- %% Check if this timestamp is past the ttl setting.
535
- exceeds_ttl(Timestamp, TTL) ->
536
- Diff = (timer:now_diff(now(), Timestamp) / 1000 / 1000),
537
- Diff > TTL.
538
-
539
- update_indexes(_Bucket, _Key, undefined, _IndexRef) ->
540
- ok;
541
- update_indexes(_Bucket, _Key, [], _IndexRef) ->
542
- ok;
543
- update_indexes(Bucket, Key, [{remove, Field, Value}|Rest], IndexRef) ->
544
- true = ets:delete(IndexRef, {Bucket, Field, Value, Key}),
545
- update_indexes(Bucket, Key, Rest, IndexRef);
546
- update_indexes(Bucket, Key, [{add, Field, Value}|Rest], IndexRef) ->
547
- true = ets:insert(IndexRef, {{Bucket, Field, Value, Key}, <<>>}),
548
- update_indexes(Bucket, Key, Rest, IndexRef).
549
-
550
- %% @private
551
- time_entry(Bucket, Key, Now, TimeRef) ->
552
- ets:insert(TimeRef, {Now, {Bucket, Key}}).
553
-
554
- %% @private
555
- %% @doc Dump some entries if the max memory size has
556
- %% been breached.
557
- trim_data_table(MaxMemory, UsedMemory, _, _, _, Freed) when
558
- (UsedMemory - Freed) =< MaxMemory ->
559
- Freed;
560
- trim_data_table(MaxMemory, UsedMemory, DataRef, TimeRef, IndexRef, Freed) ->
561
- %% Delete the oldest object
562
- OldestSize = delete_oldest(DataRef, TimeRef, IndexRef),
563
- trim_data_table(MaxMemory,
564
- UsedMemory,
565
- DataRef,
566
- TimeRef,
567
- IndexRef,
568
- Freed + OldestSize).
569
-
570
- %% @private
571
- delete_oldest(DataRef, TimeRef, IndexRef) ->
572
- OldestTime = ets:first(TimeRef),
573
- case OldestTime of
574
- '$end_of_table' ->
575
- 0;
576
- _ ->
577
- OldestKey = ets:lookup_element(TimeRef, OldestTime, 2),
578
- ets:delete(TimeRef, OldestTime),
579
- case ets:lookup(DataRef, OldestKey) of
580
- [] ->
581
- delete_oldest(DataRef, TimeRef, IndexRef);
582
- [Object] ->
583
- {Bucket, Key} = OldestKey,
584
- ets:match_delete(IndexRef, ?DELETE_PTN(Bucket, Key)),
585
- ets:delete(DataRef, OldestKey),
586
- object_size(Object)
587
- end
588
- end.
589
-
590
- %% @private
591
- object_size(Object) ->
592
- case Object of
593
- {{Bucket, Key}, {{ts, _}, Val}} ->
594
- ok;
595
- {{Bucket, Key}, Val} ->
596
- ok
597
- end,
598
- size(Bucket) + size(Key) + size(Val).
599
-
600
- %% Copied from riak_core 1.2 app_helper module
601
- %% @private
602
- %% @doc Retrieve value for Key from Properties if it exists, otherwise
603
- %% return from the application's env.
604
- -spec get_prop_or_env(atom(), [{atom(), term()}], atom()) -> term().
605
- get_prop_or_env(Key, Properties, App) ->
606
- get_prop_or_env(Key, Properties, App, undefined).
607
-
608
- %% @private
609
- %% @doc Return the value for Key in Properties if it exists, otherwise return
610
- %% the value from the application's env, or Default.
611
- -spec get_prop_or_env(atom(), [{atom(), term()}], atom(), term()) -> term().
612
- get_prop_or_env(Key, Properties, App, Default) ->
613
- case proplists:get_value(Key, Properties) of
614
- undefined ->
615
- app_helper:get_env(App, Key, Default);
616
- Value ->
617
- Value
618
- end.
619
-
620
- %% ===================================================================
621
- %% EUnit tests
622
- %% ===================================================================
623
-
624
- -ifdef(TEST).
625
-
626
- simple_test_() ->
627
- riak_kv_backend:standard_test(?MODULE, []).
628
-
629
- ttl_test_() ->
630
- Config = [{ttl, 15}],
631
- {ok, State} = start(42, Config),
632
-
633
- Bucket = <<"Bucket">>,
634
- Key = <<"Key">>,
635
- Value = <<"Value">>,
636
-
637
- [
638
- %% Put an object
639
- ?_assertEqual({ok, State}, put(Bucket, Key, [], Value, State)),
640
- %% Wait 1 second to access it
641
- ?_assertEqual(ok, timer:sleep(1000)),
642
- ?_assertEqual({ok, Value, State}, get(Bucket, Key, State)),
643
- %% Wait 3 seconds and access it again
644
- ?_assertEqual(ok, timer:sleep(3000)),
645
- ?_assertEqual({ok, Value, State}, get(Bucket, Key, State)),
646
- %% Wait 15 seconds and it should expire
647
- {timeout, 30000, ?_assertEqual(ok, timer:sleep(15000))},
648
- %% This time it should be gone
649
- ?_assertEqual({error, not_found, State}, get(Bucket, Key, State))
650
- ].
651
-
652
- %% @private
653
- max_memory_test_() ->
654
- %% Set max size to 1.5kb
655
- Config = [{max_memory, 1.5 * (1 / 1024)}],
656
- {ok, State} = start(42, Config),
657
-
658
- Bucket = <<"Bucket">>,
659
- Key1 = <<"Key1">>,
660
- Value1 = list_to_binary(string:copies("1", 1024)),
661
- Key2 = <<"Key2">>,
662
- Value2 = list_to_binary(string:copies("2", 1024)),
663
-
664
- %% Write Key1 to the datastore
665
- {ok, State1} = put(Bucket, Key1, [], Value1, State),
666
- timer:sleep(timer:seconds(1)),
667
- %% Write Key2 to the datastore
668
- {ok, State2} = put(Bucket, Key2, [], Value2, State1),
669
-
670
- [
671
- %% Key1 should be kicked out
672
- ?_assertEqual({error, not_found, State2}, get(Bucket, Key1, State2)),
673
- %% Key2 should still be present
674
- ?_assertEqual({ok, Value2, State2}, get(Bucket, Key2, State2))
675
- ].
676
-
677
- regression_367_key_range_test_() ->
678
- {ok, State} = start(142, []),
679
- Keys = [begin
680
- Bin = list_to_binary(integer_to_list(I)),
681
- if I < 10 ->
682
- <<"obj0", Bin/binary>>;
683
- true -> <<"obj", Bin/binary>>
684
- end
685
- end || I <- lists:seq(1,30) ],
686
- Bucket = <<"keyrange">>,
687
- Value = <<"foobarbaz">>,
688
- State1 = lists:foldl(fun(Key, IState) ->
689
- {ok, NewState} = put(Bucket, Key, [], Value, IState),
690
- NewState
691
- end, State, Keys),
692
- Folder = fun(_B, K, Acc) ->
693
- Acc ++ [K]
694
- end,
695
- [
696
- ?_assertEqual({ok, [<<"obj01">>]}, fold_keys(Folder, [], [{index, Bucket, {range, <<"$key">>, <<"obj01">>, <<"obj01">>}}], State1)),
697
- ?_assertEqual({ok, [<<"obj10">>,<<"obj11">>]}, fold_keys(Folder, [], [{index, Bucket, {range, <<"$key">>, <<"obj10">>, <<"obj11">>}}], State1)),
698
- ?_assertEqual({ok, [<<"obj01">>]}, fold_keys(Folder, [], [{index, Bucket, {range, <<"$key">>, <<"obj00">>, <<"obj01">>}}], State1)),
699
- ?_assertEqual({ok, lists:sort(Keys)}, fold_keys(Folder, [], [{index, Bucket, {range, <<"$key">>, <<"obj0">>, <<"obj31">>}}], State1)),
700
- ?_assertEqual({ok, []}, fold_keys(Folder, [], [{index, Bucket, {range, <<"$key">>, <<"obj31">>, <<"obj32">>}}], State1)),
701
- ?_assertEqual({ok, [<<"obj01">>]}, fold_keys(Folder, [], [{index, Bucket, {eq, <<"$key">>, <<"obj01">>}}], State1)),
702
- ?_assertEqual(ok, stop(State1))
703
- ].
704
-
705
- -ifdef(EQC).
706
-
707
- eqc_test_() ->
708
- {spawn,
709
- [{inorder,
710
- [{setup,
711
- fun setup/0,
712
- fun cleanup/1,
713
- [
714
- {timeout, 60000,
715
- [?_assertEqual(true,
716
- backend_eqc:test(?MODULE, true))]}
717
- ]}]}]}.
718
-
719
- setup() ->
720
- application:load(sasl),
721
- application:set_env(sasl, sasl_error_logger, {file, "riak_kv_memory_backend_eqc_sasl.log"}),
722
- error_logger:tty(false),
723
- error_logger:logfile({open, "riak_kv_memory_backend_eqc.log"}),
724
- ok.
725
-
726
- cleanup(_) ->
727
- ok.
728
-
729
- -endif. % EQC
730
-
731
- -endif. % TEST