PageRenderTime 66ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/src/medici/medici.erl

http://github.com/evanmiller/ChicagoBoss
Erlang | 384 lines | 230 code | 71 blank | 83 comment | 1 complexity | c5308684899b1724dfa52cad14e5468e MD5 | raw file
  1. %%% The contents of this file are subject to the Erlang Public License,
  2. %%% Version 1.1, (the "License"); you may not use this file except in
  3. %%% compliance with the License. You should have received a copy of the
  4. %%% Erlang Public License along with this software. If not, it can be
  5. %%% retrieved via the world wide web at http://www.erlang.org/.
  6. %%%
  7. %%% Software distributed under the License is distributed on an "AS IS"
  8. %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. %%% the License for the specific language governing rights and limitations
  10. %%% under the License.
  11. %%%-------------------------------------------------------------------
  12. %%% File: medici.erl
  13. %%% @author Jim McCoy <mccoy@mad-scientist.com>
  14. %%% @copyright Copyright (c) 2009, Jim McCoy. All Rights Reserved.
  15. %%%
  16. %%% @doc
  17. %%% This module provides the primary API for interfacing with the
  18. %%% medici application. These functions assume you are using the default
  19. %%% registered name for the service and that you know what sort of remote database
  20. %%% you are talking to (e.g. don't make table-specific calls to a hash
  21. %%% database.) If you need byte-order specific ops, want to register the
  22. %%% controller with a different name, or want to run medici interfaces
  23. %%% to multiple remote databases within the same erlang VM the you should
  24. %%% update these functions or just run gen_server:call() directly.
  25. %%% @end
  26. -module(medici).
  27. %% Starting and stopping the app
  28. -export([start/0, start/1, stop/0]).
  29. %% Basic API exports
  30. -export([put/2, putkeep/2, putcat/2, putshl/3, putnr/2, out/1, get/1,
  31. mget/1, vsiz/1, iterinit/0, iternext/0, fwmkeys/2, addint/2,
  32. adddouble/2, adddouble/3, sync/0, vanish/0, rnum/0, size/0,
  33. stat/0, copy/1, restore/2, setmst/2, optimize/1]).
  34. %% Table API exports
  35. -export([update/2, setindex/2, genuid/0, query_limit/2, query_limit/3,
  36. query_add_condition/4, query_order/3, search/1, searchcount/1,
  37. searchout/1]).
  38. -include("medici.hrl").
  39. %% @spec start() -> {ok, Pid} | Error:term()
  40. %%
  41. %% @doc Start the medici application.
  42. start() ->
  43. application:start(medici).
  44. %% @spec start(StartupOptions::proplist()) -> {ok, Pid} | Error:term()
  45. %%
  46. %% @doc
  47. %% Start the medici application, using a provided proplist as a set of
  48. %% additional options for the medici application. WARNING: If you use
  49. %% start/1 the options you provide will become the new default startup
  50. %% options until you restart your Erlang VM.
  51. %% @end
  52. start(StartupOptions) when is_list(StartupOptions) ->
  53. AppEnvOptions = case application:get_env(medici, options) of
  54. {ok, Val} -> Val;
  55. undefined -> []
  56. end,
  57. CombinedOptions = StartupOptions ++ AppEnvOptions,
  58. %% Merge into a single set of options, favoring those passed in
  59. %% to start/1 over the app env.
  60. MediciOptions = [{K, proplists:get_value(K, CombinedOptions)} ||
  61. K <- proplists:get_keys(CombinedOptions)],
  62. application:load(medici),
  63. ok = application:set_env(medici, options, MediciOptions),
  64. application:start(medici).
  65. stop() ->
  66. application:stop(medici).
  67. %% NOTE TO THOSE WHO CHANGE THE CONTROLLER NAME:
  68. %%
  69. %% The following api calls are just simple wrappers around calls
  70. %% to gen_server. If you change the controller name in the app
  71. %% configuration options you will no longer be able to use this
  72. %% api, but can quite easily acheive the same effect by just using
  73. %% gen_server:call() directly with your controller name where this
  74. %% api uses the ?CONTROLLER_NAME macro.
  75. put(Key, Value) ->
  76. gen_server:call(?CONTROLLER_NAME, {put, Key, Value}).
  77. putcat(Key, Value) ->
  78. gen_server:call(?CONTROLLER_NAME, {putcat, Key, Value}).
  79. putkeep(Key, Value) ->
  80. gen_server:call(?CONTROLLER_NAME, {putkeep, Key, Value}).
  81. putshl(Key, Value, Width) ->
  82. gen_server:call(?CONTROLLER_NAME, {putshl, Key, Value, Width}).
  83. putnr(Key, Value) ->
  84. gen_server:cast(?CONTROLLER_NAME, {putnr, Key, Value}).
  85. out(Key) ->
  86. gen_server:call(?CONTROLLER_NAME, {out, Key}).
  87. get(Key) ->
  88. gen_server:call(?CONTROLLER_NAME, {get, Key}).
  89. mget(KeyList) ->
  90. gen_server:call(?CONTROLLER_NAME, {mget, KeyList}).
  91. vsiz(Key) ->
  92. gen_server:call(?CONTROLLER_NAME, {vsiz, Key}).
  93. iterinit() ->
  94. gen_server:call(?CONTROLLER_NAME, {iterinit}).
  95. iternext() ->
  96. gen_server:call(?CONTROLLER_NAME, {iternext}).
  97. fwmkeys(Prefix, MaxKeys) ->
  98. gen_server:call(?CONTROLLER_NAME, {fwmkeys, Prefix, MaxKeys}).
  99. addint(Key, Int) ->
  100. gen_server:call(?CONTROLLER_NAME, {addint, Key, Int}).
  101. adddouble(Key, Double) ->
  102. gen_server:call(?CONTROLLER_NAME, {adddouble, Key, Double}).
  103. adddouble(Key, IntPart, FracPart) ->
  104. gen_server:call(?CONTROLLER_NAME, {adddouble, Key, IntPart, FracPart}).
  105. sync() ->
  106. gen_server:call(?CONTROLLER_NAME, {sync}).
  107. vanish() ->
  108. gen_server:call(?CONTROLLER_NAME, {vanish}).
  109. optimize(TuningOptions) ->
  110. gen_server:call(?CONTROLLER_NAME, {optimize, TuningOptions}).
  111. rnum() ->
  112. gen_server:call(?CONTROLLER_NAME, {rnum}).
  113. size() ->
  114. gen_server:call(?CONTROLLER_NAME, {size}).
  115. stat() ->
  116. gen_server:call(?CONTROLLER_NAME, {stat}).
  117. copy(PathName) ->
  118. gen_server:call(?CONTROLLER_NAME, {copy, PathName}).
  119. restore(PathName, TimeStamp) ->
  120. gen_server:call(?CONTROLLER_NAME, {restore, PathName, TimeStamp}).
  121. setmst(HostName, Port) ->
  122. gen_server:call(?CONTROLLER_NAME, {setmst, HostName, Port}).
  123. %% Additional table functions
  124. update(Key, NewCols) ->
  125. gen_server:call(?CONTROLLER_NAME, {update, Key, NewCols}).
  126. setindex(Column, Type) ->
  127. gen_server:call(?CONTROLLER_NAME, {setindex, Column, Type}).
  128. genuid() ->
  129. gen_server:call(?CONTROLLER_NAME, {genuid}).
  130. query_limit(OldQuery, Max) ->
  131. gen_server:call(?CONTROLLER_NAME, {query_limit, OldQuery, Max}).
  132. query_limit(OldQuery, Max, Skip) ->
  133. gen_server:call(?CONTROLLER_NAME, {query_limit, OldQuery, Max, Skip}).
  134. query_add_condition(OldQuery, Column, Op, ExprList) ->
  135. gen_server:call(?CONTROLLER_NAME, {query_add_condition, OldQuery, Column, Op, ExprList}).
  136. query_order(OldQuery, Column, Type) ->
  137. gen_server:call(?CONTROLLER_NAME, {query_order, OldQuery, Column, Type}).
  138. search(Query) ->
  139. gen_server:call(?CONTROLLER_NAME, {search, Query}).
  140. searchcount(Query) ->
  141. gen_server:call(?CONTROLLER_NAME, {searchcount, Query}).
  142. searchout(Query) ->
  143. gen_server:call(?CONTROLLER_NAME, {searchout, Query}).
  144. %% EUnit tests
  145. %%
  146. %% TODO: for completeness, set up two internal suites within this
  147. %% test suite, one for hash and another for table. Test hash (assuming
  148. %% the server is already running), then test hash again by running the
  149. %% server internal to medici, then test table the same way. Need to
  150. %% review eunit docs to get this one right...
  151. -ifdef(EUNIT).
  152. get_random_count() ->
  153. get_random_count(1000).
  154. get_random_count(Max) ->
  155. crypto:start(),
  156. {A1,A2,A3} = now(),
  157. random:seed(A1, A2, A3),
  158. round(Max * random:uniform()).
  159. %% setup_table_data() ->
  160. %% ColData = [{"rec1", [{"name", "alice"}, {"sport", "baseball"}]},
  161. %% {"rec2", [{"name", "bob"}, {"sport", "basketball"}]},
  162. %% {"rec3", [{"name", "carol"}, {"age", "24"}]},
  163. %% {"rec4", [{"name", "trent"}, {"age", "33"}, {"sport", "football"}]},
  164. %% {"rec5", [{"name", "mallet"}, {"sport", "tennis"}, {"fruit", "apple"}]}
  165. %% ],
  166. %% lists:foreach(fun({Key, ValProplist}) ->
  167. %% ok = ?MODULE:put(Key, ValProplist)
  168. %% end, ColData).
  169. init_test() ->
  170. ?MODULE:start(),
  171. ?MODULE:stop(),
  172. ok.
  173. %% init_with_args_test() ->
  174. %% ?MODULE:start([{foobar, 32}]),
  175. %% {ok, Options} = application:get_env(medici, options),
  176. %% ?assert(lists:member({foobar, 32}, Options)),
  177. %% ?MODULE:stop().
  178. medici_api_test_() ->
  179. {setup,
  180. fun() -> ?MODULE:start() end,
  181. fun(_Cleanup) -> ?MODULE:stop() end,
  182. [?_test(put_get_unit()),
  183. ?_test(put_get_random_unit()),
  184. ?_test(putkeep_unit()),
  185. ?_test(putcat_unit()),
  186. ?_test(putshl_unit()),
  187. %% ?_test(putnr_unit()),
  188. ?_test(out_unit()),
  189. ?_test(mget_unit()),
  190. ?_test(vsiz_unit()),
  191. ?_test(vanish_unit()),
  192. ?_test(iter_unit()),
  193. ?_test(fwmkeys_unit()),
  194. %% ?_test(addint_unit()),
  195. ?_test(sync_unit()),
  196. ?_test(rnum_unit()),
  197. ?_test(size_unit()),
  198. ?_test(stat_unit()),
  199. ?_test(optimize_unit())]
  200. }.
  201. put_get_unit() ->
  202. ?assert(?MODULE:put("put_get1", "testval") =:= ok),
  203. ?assert(?MODULE:put(<<"put_get2">>, <<32,145,56,0,14>>) =:= ok),
  204. ?assert(?MODULE:get(<<"put_get1">>) =:= <<"testval">>),
  205. ?assert(?MODULE:get("put_get2") =:= <<32, 145, 56, 0, 14>>).
  206. put_get_random_unit() ->
  207. ElementCount = get_random_count(),
  208. PutVals = lists:foldl(fun(_Seq, Acc) ->
  209. KeySize = random:uniform(1024),
  210. Key = crypto:rand_bytes(KeySize),
  211. ValSize = random:uniform(65536),
  212. Val = crypto:rand_bytes(ValSize),
  213. ok = ?MODULE:put(Key, Val),
  214. [{Key, Val} | Acc]
  215. end, [], lists:seq(1, ElementCount)),
  216. lists:foreach(fun({K, V}) ->
  217. ?assert(?MODULE:get(K) =:= V)
  218. end, PutVals).
  219. putkeep_unit() ->
  220. ok = ?MODULE:put(<<"putkeep1">>, <<"foo">>),
  221. ?assert(?MODULE:get(<<"putkeep1">>) =:= <<"foo">>),
  222. ?assertMatch({error, _}, ?MODULE:putkeep(<<"putkeep1">>, <<"bar">>)),
  223. ?assert(?MODULE:get(<<"putkeep1">>) =:= <<"foo">>), % no effect if key already exists before putkeep
  224. ok = ?MODULE:putkeep(<<"putkeep2">>, <<"baz">>),
  225. ?assert(?MODULE:get(<<"putkeep2">>) =:= <<"baz">>). % puts the key if key does not exist already
  226. putcat_unit() ->
  227. ok = ?MODULE:put(<<"putcat1">>, <<"foo">>),
  228. % append "bar" to the end
  229. ok = ?MODULE:putcat(<<"putcat1">>, <<"bar">>),
  230. ?assert(?MODULE:get(<<"putcat1">>) =:= <<"foobar">>).
  231. putshl_unit() ->
  232. ok = ?MODULE:put(<<"putshl">>, <<"foo">>),
  233. % append "bar" to the end and shift to the left to retain the width of "4"
  234. ok = ?MODULE:putshl(<<"putshl">>, <<"bar">>, 4),
  235. ?assert(?MODULE:get(<<"putshl">>) =:= <<"obar">>).
  236. %% putnr_unit() ->
  237. %% ?MODULE:putnr(<<"putnr1">>, <<"no reply">>),
  238. %% ?assert(?MODULE:get(<<"putnr1">>) =:= <<"no reply">>).
  239. out_unit() ->
  240. ok = ?MODULE:put(<<"out1">>, <<"to remove">>),
  241. ?assert(?MODULE:get(<<"out1">>) =:= <<"to remove">>),
  242. ok = ?MODULE:out(<<"out1">>),
  243. ?assertMatch({error, _}, ?MODULE:get(<<"out1">>)).
  244. mget_unit() ->
  245. ok = ?MODULE:put(<<"mget1">>, <<"alice">>),
  246. ok = ?MODULE:put(<<"mget2">>, <<"bob">>),
  247. ok = ?MODULE:put(<<"mget3">>, <<"carol">>),
  248. ok = ?MODULE:put(<<"mget4">>, <<"trent">>),
  249. ?assert(?MODULE:mget([<<"mget1">>, <<"mget2">>,
  250. <<"mget3">>, <<"mget4">>]) =:=
  251. [{<<"mget1">>, <<"alice">>},
  252. {<<"mget2">>, <<"bob">>},
  253. {<<"mget3">>, <<"carol">>},
  254. {<<"mget4">>, <<"trent">>}]).
  255. vsiz_unit() ->
  256. ok = ?MODULE:put(<<"vsiz1">>, <<"vsiz test">>),
  257. ?assert(?MODULE:vsiz(<<"vsiz1">>) =:= 9).
  258. vanish_unit() ->
  259. ok = ?MODULE:put(<<"vanish1">>, <<"going away">>),
  260. ok = ?MODULE:vanish(),
  261. ?assertMatch({error, _}, ?MODULE:get(<<"vanish1">>)).
  262. iter_unit() ->
  263. ok = ?MODULE:vanish(),
  264. ok = ?MODULE:put(<<"a">>, <<"first">>),
  265. ok = ?MODULE:iterinit(),
  266. <<"a">> = ?MODULE:iternext(), % "a" should be the first key
  267. % Now to test a bit of real iteration
  268. ok = ?MODULE:put(<<"b">>, <<"second">>),
  269. ok = ?MODULE:put(<<"c">>, <<"third">>),
  270. ok = ?MODULE:iterinit(),
  271. One = ?MODULE:iternext(),
  272. Two = ?MODULE:iternext(),
  273. Three = ?MODULE:iternext(),
  274. ?assertMatch({error, _}, ?MODULE:iternext()),
  275. ?assertMatch([<<"a">>, <<"b">>, <<"c">>], lists:sort([One, Two, Three])).
  276. fwmkeys_unit() ->
  277. ok = ?MODULE:vanish(),
  278. ok = ?MODULE:put(<<"fwmkeys1">>, <<"1">>),
  279. ok = ?MODULE:put(<<"fwmkeys2">>, <<"2">>),
  280. ok = ?MODULE:put(<<"fwmkeys3">>, <<"3">>),
  281. ok = ?MODULE:put(<<"fwmkeys4">>, <<"4">>),
  282. Keys1 = ?MODULE:fwmkeys(<<"fwmkeys">>, 4),
  283. ?assert(length(Keys1) =:= 4),
  284. ?assert(lists:member(<<"fwmkeys1">>, Keys1)),
  285. ?assert(lists:member(<<"fwmkeys2">>, Keys1)),
  286. ?assert(lists:member(<<"fwmkeys3">>, Keys1)),
  287. ?assert(lists:member(<<"fwmkeys4">>, Keys1)),
  288. Keys2 = ?MODULE:fwmkeys(<<"fwmkeys">>, 2),
  289. ?assert(length(Keys2) =:= 2).
  290. %% addint_unit() ->
  291. %% ok = ?MODULE:put(<<"addint1">>, 100),
  292. %% ?assert(?MODULE:addint(<<"addint1">>, 20) =:= 120).
  293. sync_unit() ->
  294. ?assert(?MODULE:sync() =:= ok).
  295. rnum_unit() ->
  296. ok = ?MODULE:vanish(),
  297. ok = ?MODULE:put(<<"rnum1">>, <<"foo">>),
  298. ok = ?MODULE:put(<<"rnum2">>, <<"foo">>),
  299. ?assert(?MODULE:rnum() =:= 2),
  300. ok = ?MODULE:vanish(),
  301. ?assert(?MODULE:rnum() =:= 0).
  302. size_unit() ->
  303. OldSize = ?MODULE:size(),
  304. ok = ?MODULE:put(<<"size1">>, <<"foo">>),
  305. NewSize = ?MODULE:size(),
  306. ?assert(NewSize > OldSize).
  307. stat_unit() ->
  308. StatInfo = ?MODULE:stat(),
  309. Protocol = proplists:get_value(protver, StatInfo),
  310. ?assert(list_to_float(Protocol) > 0.9).
  311. optimize_unit() ->
  312. ?assert(?MODULE:optimize("#bnum=1000000#opts=ld") =:= ok).
  313. -endif.