PageRenderTime 90ms CodeModel.GetById 2ms app.highlight 80ms RepoModel.GetById 1ms app.codeStats 1ms

/src/memcached.erl

https://github.com/fauxsoup/memcached-client
Erlang | 875 lines | 511 code | 139 blank | 225 comment | 8 complexity | 197a87eefaddd14a0f69b40cab7e30b8 MD5 | raw file
  1%%    Copyright (c) 2009-2010  Taro Minowa(Higepon) <higepon@users.sourceforge.jp>
  2%%
  3%%    Redistribution and use in source and binary forms, with or without
  4%%    modification, are permitted provided that the following conditions
  5%%    are met:
  6%%
  7%%    1. Redistributions of source code must retain the above copyright
  8%%       notice, this list of conditions and the following disclaimer.
  9%%
 10%%    2. Redistributions in binary form must reproduce the above copyright
 11%%       notice, this list of conditions and the following disclaimer in the
 12%%       documentation and/or other materials provided with the distribution.
 13%%
 14%%    3. Neither the name of the authors nor the names of its contributors
 15%%       may be used to endorse or promote products derived from this
 16%%       software without specific prior written permission.
 17%%
 18%%    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 19%%    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 20%%    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 21%%    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 22%%    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 23%%    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 24%%    TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 25%%    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 26%%    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 27%%    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 28%%    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 29
 30%%%-------------------------------------------------------------------
 31%%% File    : memcached.erl
 32%%% Author  : Taro Minowa(Higepon) <higepon@users.sourceforge.jp>
 33%%% Description : A minimal memcached client library.
 34%%%
 35%%% Created :  7 Dec 2009 by higepon <higepon@users.sourceforge.jp>
 36%%%-------------------------------------------------------------------
 37-module(memcached).
 38-behaviour(gen_server).
 39
 40%% API
 41-export([connect/1, connect/2, disconnect/1,
 42         set/3, set/5, setb/3, setb/5,
 43         cas/6, casb/6,
 44         get/2, getb/2, gets/2, getsb/2,
 45         get_multi/2, get_multib/2,
 46         gets_multi/2, gets_multib/2,
 47         replace/3, replace/5, replaceb/3, replaceb/5,
 48         add/3, add/5, addb/3, addb/5,
 49         append/3, prepend/3,
 50         delete/2,
 51         incr/3, decr/3,
 52         version/1,
 53         quit/1,
 54         stats/1,
 55         split/1,
 56         flush_all/1, flush_all/2
 57        ]).
 58
 59%% gen_server callbacks
 60-export([start_link/4, new_conn/1, init/1, handle_call/3, handle_cast/2, handle_info/2,
 61         terminate/2, code_change/3]).
 62
 63%%====================================================================
 64%% Definitions
 65%%====================================================================
 66-define(TCP_OPTIONS, [binary, {packet, raw}, {nodelay, true}, {reuseaddr, true}, {active, false},
 67                      {sndbuf,16384},{recbuf,4096}]).
 68-define(TIMEOUT, 10000).
 69-define(CR, 13).
 70-define(LF, 10).
 71
 72%%====================================================================
 73%% API
 74%%====================================================================
 75
 76start_link(Owner, Host, Port, Opts) -> 
 77    gen_server:start_link(?MODULE, [Owner, Host, Port, Opts], []).
 78
 79%start_link(Config) -> connect(Config).   
 80
 81%% @spec connect(Host::string(), Port::integer()) -> {ok, Conn} | {error, Reason}
 82connect(Host, Port) ->
 83    memcached_sup:connect(self(), Host, Port, []).
 84    %gen_server:start_link(?MODULE, [Host, Port], []).
 85
 86connect(HostPortSpecs) ->
 87    [{Host, Port} | _More] = HostPortSpecs,
 88    memcached_sup:connect(self(), Host, Port, []).
 89    %gen_server:start_link(?MODULE, [Host, Port], []).
 90
 91new_conn(HostPortSpecs) ->
 92    [{Host, Port} | _More] = HostPortSpecs,
 93    memcached_sup:connect(self(), Host, Port, []).
 94
 95%%--------------------------------------------------------------------
 96%% Function: set
 97%% Description: set value
 98%% Returns: ok
 99%%--------------------------------------------------------------------
100set(Conn, Key, Value) ->
101    setb(Conn, Key, term_to_binary(Value)).
102set(Conn, Key, Value, Flags, ExpTime) ->
103    setb(Conn, Key, term_to_binary(Value), Flags, ExpTime).
104
105%%--------------------------------------------------------------------
106%% Function: setb
107%% Description: set binary value
108%% Returns: ok
109%%--------------------------------------------------------------------
110setb(Conn, Key, Value) when is_list(Key) andalso is_binary(Value) ->
111    gen_server:call(Conn, {setb, Key, Value}).
112setb(Conn, Key, Value, Flags, ExpTime) when is_list(Key) andalso is_binary(Value) andalso is_integer(Flags) andalso is_integer(ExpTime) ->
113    gen_server:call(Conn, {setb, Key, Value, Flags, ExpTime}).
114
115
116%%--------------------------------------------------------------------
117%% Function: cas
118%% Description: set cas
119%% Returns: ok
120%%--------------------------------------------------------------------
121cas(Conn, Key, Value, Flags, ExpTime, CasUnique64) ->
122    casb(Conn, Key, term_to_binary(Value), Flags, ExpTime, CasUnique64).
123
124
125%%--------------------------------------------------------------------
126%% Function: casb
127%% Description: set cas
128%% Returns: ok
129%%--------------------------------------------------------------------
130casb(Conn, Key, Value, Flags, ExpTime, CasUnique64) when is_binary(Value) ->
131    gen_server:call(Conn, {casb, Key, Value, Flags, ExpTime, CasUnique64}).
132
133
134%%--------------------------------------------------------------------
135%% Function: replace
136%% Description: replace value
137%% Returns: ok, {error, not_stored} or {error, Reason}
138%%--------------------------------------------------------------------
139replace(Conn, Key, Value) when is_list(Key) ->
140    replaceb(Conn, Key, term_to_binary(Value)).
141replace(Conn, Key, Value, Flags, ExpTime) when is_list(Key) andalso is_integer(ExpTime) ->
142    replaceb(Conn, Key, term_to_binary(Value), Flags, ExpTime).
143
144
145%%--------------------------------------------------------------------
146%% Function: replaceb
147%% Description: replace binary value
148%% Returns: ok, {error, not_stored} or {error, Reason}
149%%--------------------------------------------------------------------
150replaceb(Conn, Key, Value) when is_list(Key) ->
151    gen_server:call(Conn, {replaceb, Key, Value}).
152replaceb(Conn, Key, Value, Flags, ExpTime) when is_list(Key) andalso is_integer(ExpTime) ->
153    gen_server:call(Conn, {replaceb, Key, Value, Flags, ExpTime}).
154
155
156%%--------------------------------------------------------------------
157%% Function: add
158%% Description: add value
159%% Returns: ok, {error, not_stored} or {error, Reason}
160%%--------------------------------------------------------------------
161add(Conn, Key, Value) when is_list(Key) ->
162    addb(Conn, Key, term_to_binary(Value)).
163add(Conn, Key, Value, Flags, ExpTime) when is_list(Key) andalso is_integer(ExpTime) ->
164    addb(Conn, Key, term_to_binary(Value), Flags, ExpTime).
165
166
167%%--------------------------------------------------------------------
168%% Function: addb
169%% Description: add binary value
170%% Returns: ok, {error, not_stored} or {error, Reason}
171%%--------------------------------------------------------------------
172addb(Conn, Key, Value) when is_list(Key) ->
173    gen_server:call(Conn, {addb, Key, Value}).
174addb(Conn, Key, Value, Flags, ExpTime) when is_list(Key) andalso is_integer(ExpTime) ->
175    gen_server:call(Conn, {addb, Key, Value, Flags, ExpTime}).
176
177
178%%--------------------------------------------------------------------
179%% Function: append
180%% Description: append value
181%% Returns: ok, {error, not_stored} or {error, Reason}
182%%--------------------------------------------------------------------
183append(Conn, Key, Value) when is_list(Key) ->
184    gen_server:call(Conn, {append, Key, Value}).
185
186
187%%--------------------------------------------------------------------
188%% Function: prepend
189%% Description: prepend value
190%% Returns: ok, {error, not_stored} or {error, Reason}
191%%--------------------------------------------------------------------
192prepend(Conn, Key, Value) when is_list(Key) ->
193    gen_server:call(Conn, {prepend, Key, Value}).
194
195
196%%--------------------------------------------------------------------
197%% Function: get
198%% Description: get value
199%% Returns: {ok, Value}, {error, not_found} or {error, Reason}
200%%--------------------------------------------------------------------
201get(Conn, Key) when is_list(Key) ->
202    gen_server:call(Conn, {get, Key}).
203
204
205%%--------------------------------------------------------------------
206%% Function: gets
207%% Description: get value and cas
208%% Returns: {ok, Value, CasUnique64}, {error, not_found} or {error, Reason}
209%%--------------------------------------------------------------------
210gets(Conn, Key) when is_list(Key) ->
211    gen_server:call(Conn, {gets, Key}).
212
213
214%%--------------------------------------------------------------------
215%% Function: getb
216%% Description: get value as binary
217%% Returns: {ok, Value}, {error, not_found} or {error, Reason}
218%%--------------------------------------------------------------------
219getb(Conn, Key) when is_list(Key) ->
220    gen_server:call(Conn, {getb, Key}).
221
222
223%%--------------------------------------------------------------------
224%% Function: getsb
225%% Description: get value as binary and cas
226%% Returns: {ok, Value, CasUnique64}, {error, not_found} or {error, Reason}
227%%--------------------------------------------------------------------
228getsb(Conn, Key) when is_list(Key) ->
229    gen_server:call(Conn, {getsb, Key}).
230
231
232%%--------------------------------------------------------------------
233%% Function: get_multi
234%% Description: get multiple values
235%% Returns: {ok, Values}, Values = list of {Key, Value}.
236%%--------------------------------------------------------------------
237get_multi(Conn, Keys) when is_list(Keys) ->
238    gen_server:call(Conn, {get_multi, Keys}).
239
240
241%%--------------------------------------------------------------------
242%% Function: get_multib
243%% Description: get multiple binary values
244%% Returns: {ok, Values}, Values = list of {Key, Value}.
245%%--------------------------------------------------------------------
246get_multib(Conn, Keys) when is_list(Keys) ->
247    gen_server:call(Conn, {get_multib, Keys}).
248
249
250%%--------------------------------------------------------------------
251%% Function: gets_multi
252%% Description: get multiple values with cas value
253%% Returns: {ok, Values}, Values = list of {Key, Value, CasValue}.
254%%--------------------------------------------------------------------
255gets_multi(Conn, Keys) when is_list(Keys) ->
256    gen_server:call(Conn, {gets_multi, Keys}).
257
258
259%%--------------------------------------------------------------------
260%% Function: gets_multib
261%% Description: get multiple binary values with cas value
262%% Returns: {ok, Values}, Values = list of {Key, Value}.
263%%--------------------------------------------------------------------
264gets_multib(Conn, Keys) when is_list(Keys) ->
265    gen_server:call(Conn, {gets_multib, Keys}).
266
267
268%%--------------------------------------------------------------------
269%% Function: delete
270%% Description: delete value
271%% Returns: ok
272%%--------------------------------------------------------------------
273delete(Conn, Key) when is_list(Key) ->
274    gen_server:call(Conn, {delete, Key}).
275
276
277%%--------------------------------------------------------------------
278%% Function: incr
279%% Description: incr value
280%% Returns: {ok, NewValue}
281%%--------------------------------------------------------------------
282incr(Conn, Key, Value) when is_integer(Value) ->
283    gen_server:call(Conn, {incr, Key, Value}).
284
285
286%%--------------------------------------------------------------------
287%% Function: decr
288%% Description: decr value
289%% Returns: {ok, NewValue}
290%%--------------------------------------------------------------------
291decr(Conn, Key, Value) when is_integer(Value) ->
292    gen_server:call(Conn, {decr, Key, Value}).
293
294
295%%--------------------------------------------------------------------
296%% Function: version
297%% Description: Returns memcached server version.
298%% Returns: Version string
299%%--------------------------------------------------------------------
300version(Conn) ->
301    gen_server:call(Conn, version).
302
303
304%%--------------------------------------------------------------------
305%% Function: stats
306%% Description: Returns memcached stats
307%% Returns: stats string
308%%--------------------------------------------------------------------
309stats(Conn) ->
310    gen_server:call(Conn, stats, 10 * 1000).
311
312
313%%--------------------------------------------------------------------
314%% Function: quit
315%% Description: Send quit command to server
316%% Returns: quite
317%%--------------------------------------------------------------------
318quit(Conn) ->
319    gen_server:call(Conn, quit).
320
321
322%%--------------------------------------------------------------------
323%% Function: flush_all
324%% Description: Send flush_all command to server
325%% Returns: quite
326%%--------------------------------------------------------------------
327flush_all(Conn) ->
328    gen_server:call(Conn, flush_all).
329flush_all(Conn, Sec) when is_integer(Sec) ->
330    gen_server:call(Conn, {flush_all, Sec}).
331
332
333%%--------------------------------------------------------------------
334%% Function: disconnect
335%% Description: disconnect
336%% Returns: ok
337%%--------------------------------------------------------------------
338disconnect(Conn) ->
339    gen_server:call(Conn, disconnect),
340    ok.
341
342
343init([Super, Host, Port, _Opts]) ->
344    case gen_tcp:connect(Host, Port, ?TCP_OPTIONS) of
345        {ok, Socket} ->
346            erlang:monitor(process, Super),
347            Server = Host ++ integer_to_list(Port),
348            CHash = memcached_chash:new(memcached),
349            ok = memcached_chash:add_node(CHash, Server, Server),
350            Connections = ets:new(connections, [set, protected]),
351            true = ets:insert(Connections, {Server, Socket}),
352            {ok, {Connections, CHash, Socket}};
353        {error, Reason} ->
354            {stop, Reason};
355        Other ->
356            {stop, Other}
357    end.
358
359call_with_socket(Fun, Key, Connections, CHash, _Socket) ->
360    case get_socket(Key, Connections, CHash) of
361        {ok, Socket, NewConnections} ->
362            apply(Fun, [Socket, NewConnections]);
363        {error, Reason} ->
364            {reply, {error, Reason}, {Connections, CHash, _Socket}}
365    end.
366
367%%--------------------------------------------------------------------
368%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
369%%                                      {reply, Reply, State, Timeout} |
370%%                                      {noreply, State} |
371%%                                      {noreply, State, Timeout} |
372%%                                      {stop, Reason, Reply, State} |
373%%                                      {stop, Reason, State}
374%% Description: Handling call messages
375%%--------------------------------------------------------------------
376handle_call({get, Key}, _From, {Connections, CHash, _Socket}) ->
377    call_with_socket(
378      fun(Socket, NewConnections) ->
379              case get_command(Socket, "get", [Key]) of
380                  {ok, []} ->
381                      {reply, {error, not_found}, {NewConnections, CHash, Socket}};
382                  {ok, [{_Key, Value, _CasUnique64}]} ->
383                      {reply, {ok, binary_to_term(Value)}, {NewConnections, CHash, Socket}};
384                  {error, Error}=Err when Error == enotconn ->
385                      {stop, normal, Err, {NewConnections, CHash, Socket}};
386                  Other ->
387                      {reply, Other, {NewConnections, CHash, Socket}}
388              end
389      end,
390      Key, Connections, CHash, _Socket);
391
392
393handle_call({gets, Key}, _From, {Connections, CHash, _Socket}) ->
394    call_with_socket(
395      fun(Socket, NewConnections) ->
396              case get_command(Socket, "gets", [Key]) of
397                  {ok, []} ->
398                      {reply, {error, not_found}, {NewConnections, CHash, Socket}};
399                  {ok, [{_Key, Value, CasUnique64}]} ->
400                      {reply, {ok, binary_to_term(Value), CasUnique64}, {NewConnections, CHash, Socket}};
401                  {error, Error}=Err when Error == enotconn ->
402                      {stop, normal, Err, {NewConnections, CHash, Socket}};
403                  Other ->
404                      {reply, Other, {NewConnections, CHash, Socket}}
405              end
406      end,
407      Key, Connections, CHash, _Socket);
408
409
410handle_call({getb, Key}, _From, {Connections, CHash, _Socket}) ->
411    call_with_socket(
412      fun(Socket, NewConnections) ->
413              case get_command(Socket, "get", [Key]) of
414                  {ok, []} ->
415                      {reply, {error, not_found}, {NewConnections, CHash, Socket}};
416                  {ok, [{_Key, Value, _CasUnique64}]} ->
417                      {reply, {ok, Value}, {NewConnections, CHash, Socket}};
418                  {error, Error}=Err when Error == enotconn ->
419                      {stop, normal, Err, {NewConnections, CHash, Socket}};
420                  Other ->
421                      {reply, Other, {NewConnections, CHash, Socket}}
422              end
423      end,
424      Key, Connections, CHash, _Socket);
425
426
427handle_call({getsb, Key}, _From, {Connections, CHash, _Socket}) ->
428    call_with_socket(
429      fun(Socket, NewConnections) ->
430              case get_command(Socket, "gets", [Key]) of
431                  {ok, []} ->
432                      {reply, {error, not_found}, {NewConnections, CHash, Socket}};
433                  {ok, [{_Key, Value, CasUnique64}]} ->
434                      {reply, {ok, Value, CasUnique64}, {NewConnections, CHash, Socket}};
435                  {error, Error}=Err when Error == enotconn ->
436                      {stop, normal, Err, {NewConnections, CHash, Socket}};
437                  Other ->
438                      {reply, Other, {NewConnections, CHash, Socket}}
439              end
440      end,
441      Key, Connections, CHash, _Socket);
442
443
444handle_call({get_multi, Keys}, _From, {Connections, CHash, Socket}) ->
445    case get_command(Socket, "get", Keys) of
446        {ok, BinaryValues} ->
447            Values = lists:map(fun({Key, Value, _CasUnique64}) -> {Key, binary_to_term(Value)} end, BinaryValues),
448            {reply, {ok, Values}, {Connections, CHash, Socket}};
449        {error, Error}=Err when Error == enotconn ->
450            {stop, normal, Err, {Connections, CHash, Socket}};
451        Other ->
452            {reply, Other, {Connections, CHash, Socket}}
453    end;
454
455
456handle_call({get_multib, Keys}, _From, {Connections, CHash, Socket}) ->
457    case get_command(Socket, "get", Keys) of
458        {ok, BinaryValues} ->
459            Values = lists:map(fun({Key, BinaryValue, _CasUnique64}) -> {Key, BinaryValue} end, BinaryValues),
460            {reply, {ok, Values}, {Connections, CHash, Socket}};
461        {error, Error}=Err when Error == enotconn ->
462            {stop, normal, Err, {Connections, CHash, Socket}};
463        Other ->
464            {reply, Other, {Connections, CHash, Socket}}
465    end;
466
467
468handle_call({gets_multi, Keys}, _From, {Connections, CHash, Socket}) ->
469    case get_command(Socket, "gets", Keys) of
470        {ok, BinaryValues} ->
471            Values = lists:map(fun({Key, Value, CasUnique64}) -> {Key, binary_to_term(Value), CasUnique64} end, BinaryValues),
472            {reply, {ok, Values}, {Connections, CHash, Socket}};
473        {error, Error}=Err when Error == enotconn ->
474            {stop, normal, Err, {Connections, CHash, Socket}};
475        Other ->
476            {reply, Other, {Connections, CHash, Socket}}
477    end;
478
479
480handle_call({gets_multib, Keys}, _From, {Connections, CHash, Socket}) ->
481    case get_command(Socket, "gets", Keys) of
482        {ok, BinaryValues} ->
483            Values = lists:map(fun({Key, Value, CasUnique64}) -> {Key, Value, CasUnique64} end, BinaryValues),
484            {reply, {ok, Values}, {Connections, CHash, Socket}};
485        {error, Error}=Err when Error == enotconn ->
486            {stop, normal, Err, {Connections, CHash, Socket}};
487        Other ->
488            {reply, Other, {Connections, CHash, Socket}}
489    end;
490
491
492handle_call({setb, Key, Value}, _From, {Connections, CHash, Socket}) ->
493    storage_command(Connections, CHash, Socket, "set", Key, Value, 0, 0);
494handle_call({setb, Key, Value, Flags, ExpTime}, _From, {Connections, CHash, Socket}) ->
495    storage_command(Connections, CHash, Socket, "set", Key, Value, Flags, ExpTime);
496
497
498handle_call({casb, Key, Value, Flags, ExpTime, CasUnique64}, _From, {Connections, CHash, Socket}) ->
499    storage_command(Connections, CHash, Socket, "set", Key, Value, Flags, ExpTime, CasUnique64);
500
501
502handle_call({replaceb, Key, Value}, _From, {Connections, CHash, Socket}) ->
503    storage_command(Connections, CHash, Socket, "replace", Key, Value, 0, 0);
504handle_call({replaceb, Key, Value, Flags, ExpTime}, _From, {Connections, CHash, Socket}) ->
505    storage_command(Connections, CHash, Socket, "replace", Key, Value, Flags, ExpTime);
506
507handle_call({addb, Key, Value}, _From, {Connections, CHash, Socket}) ->
508    storage_command(Connections, CHash, Socket, "add", Key, Value, 0, 0);
509handle_call({addb, Key, Value, Flags, ExpTime}, _From, {Connections, CHash, Socket}) ->
510    storage_command(Connections, CHash, Socket, "add", Key, Value, Flags, ExpTime);
511
512
513handle_call({append, Key, Value}, _From, {Connections, CHash, Socket}) ->
514    storage_command(Connections, CHash, Socket, "append", Key, term_to_binary(Value), 0, 0);
515handle_call({prepend, Key, Value}, _From, {Connections, CHash, Socket}) ->
516    storage_command(Connections, CHash, Socket, "prepend", Key, term_to_binary(Value), 0, 0);
517
518
519handle_call({delete, Key}, _From, {Connections, CHash, Socket}) ->
520    {reply, delete_command(Socket, Key), {Connections, CHash, Socket}};
521
522handle_call({incr, Key, Value}, _From, {Connections, CHash, Socket}) ->
523    {reply, incr_decr_command(Socket, "incr", Key, Value), {Connections, CHash, Socket}};
524handle_call({decr, Key, Value}, _From, {Connections, CHash, Socket}) ->
525    {reply, incr_decr_command(Socket, "decr", Key, Value), {Connections, CHash, Socket}};
526
527
528handle_call(disconnect, _From, {Connections, CHash, Socket}) ->
529    {stop, normal, ok, {Connections, CHash, Socket}};
530
531
532handle_call(version, _From, {Connections, CHash, Socket}) ->
533    gen_tcp:send(Socket, <<"version\r\n">>),
534    case gen_tcp:recv(Socket, 0, ?TIMEOUT) of
535        {ok, Packet} ->
536            case split(binary_to_list(Packet)) of
537                {Data, []} ->
538                    case Data of
539                        [$V | [$E | [$R | [$S | [$I | [$O | [$N | [32 | Version]]]]]]]] ->
540                            {reply, Version, {Connections, CHash, Socket}};
541                        _ ->
542                            {reply, {error, invalid_reseponse}, {Connections, CHash, Socket}}
543                    end;
544                Other ->
545                    {reply, Other, {Connections, CHash, Socket}}
546            end;
547        {error, Reason} ->
548            {reply, {error, Reason}, {Connections, CHash, Socket}}
549    end;
550
551
552handle_call(stats, _From, {Connections, CHash, Socket}) ->
553    gen_tcp:send(Socket, <<"stats\r\n">>),
554    case gen_tcp:recv(Socket, 0, ?TIMEOUT) of
555        {ok, Packet} ->
556            Stats = parse_stats(binary_to_list(Packet), []),
557            {reply, Stats, {Connections, CHash, Socket}};
558        {error, Reason} ->
559            {reply, {error, Reason}, {Connections, CHash, Socket}}
560    end;
561
562
563handle_call(quit, _From, {Connections, CHash, _Socket}) ->
564    lists:foreach(fun(Socket) -> gen_tcp:send(Socket, <<"quit\r\n">>) end, all_sockets(Connections)),
565    {reply, ok, {Connections, CHash, _Socket}};
566%%     case get_socket(Key, Connections, CHash) of
567%%         {ok, Socket, NewConnections} ->
568%%             gen_tcp:send(Socket, <<"quit\r\n">>),
569%%             {reply, ok, {NewConnections, CHash, Socket}};
570%%         {error, Reason} ->
571%%             {reply, {error, Reason}, {NewConnections, CHash, Socket}}
572%%     end;
573
574
575handle_call(flush_all, _From, {Connections, CHash, Socket}) ->
576    gen_tcp:send(Socket, <<"flush_all\r\n">>),
577    case gen_tcp:recv(Socket, 0, ?TIMEOUT) of
578        {ok, Packet} ->
579            case Packet of
580                <<"OK\r\n">> ->
581                    {reply, ok, {Connections, CHash, Socket}};
582                Other ->
583                    {reply, {error, Other}, {Connections, CHash, Socket}}
584            end;
585        Other ->
586            {error, Other}
587    end;
588
589
590handle_call({flush_all, Sec}, _From, {Connections, CHash, Socket}) ->
591    Command = iolist_to_binary([<<"flush_all ">>, integer_to_list(Sec), <<"\r\n">>]),
592    gen_tcp:send(Socket, Command),
593    case gen_tcp:recv(Socket, 0, ?TIMEOUT) of
594        {ok, Packet} ->
595            case Packet of
596                <<"OK\r\n">> ->
597                    {reply, ok, {Connections, CHash, Socket}};
598                Other ->
599                    {reply, {error, Other}, {Connections, CHash, Socket}}
600            end;
601        Other ->
602            {error, Other}
603    end.
604
605
606%%--------------------------------------------------------------------
607%% Function: handle_cast(Msg, State) -> {noreply, State} |
608%%                                      {noreply, State, Timeout} |
609%%                                      {stop, Reason, State}
610%% Description: Handling cast messages
611%%--------------------------------------------------------------------
612handle_cast(Msg, State) ->
613    io:format("cast=~p~n", [Msg]),
614    {noreply, State}.
615
616%%--------------------------------------------------------------------
617%% Function: handle_info(Info, State) -> {noreply, State} |
618%%                                       {noreply, State, Timeout} |
619%%                                       {stop, Reason, State}
620%% Description: Handling all non call/cast messages
621%%--------------------------------------------------------------------
622handle_info(Info, State) ->
623    io:format("info=~p~n", [Info]),
624    {noreply, State}.
625
626%%--------------------------------------------------------------------
627%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
628%% Description: Convert process state when code is changed
629%%--------------------------------------------------------------------
630code_change(_OldVsn, State, _Extra) ->
631    {ok, State}.
632
633%%--------------------------------------------------------------------
634%% Function: terminate(Reason, State) -> void()
635%% Description: This function is called by a gen_server when it is about to
636%% terminate. It should be the opposite of Module:init/1 and do any necessary
637%% cleaning up. When it returns, the gen_server terminates with Reason.
638%% The return value is ignored.
639%%--------------------------------------------------------------------
640terminate(_Reason, {Connections, _CHash, _Socket}) ->
641    lists:foreach(fun(Socket) -> gen_tcp:close(Socket) end,  all_sockets(Connections)).
642
643%%====================================================================
644%% Internal functions
645%%====================================================================
646
647parse_single_value(Data) ->
648    case io_lib:fread("VALUE ~s ~u ~u", Data) of
649        {ok, [Key, _Flags, Bytes], []} ->
650            {Key, Bytes, []};
651        _ ->
652            case io_lib:fread("VALUE ~s ~u ~u ~u", Data) of
653                {ok, [Key, _Flags, Bytes, CasUnique64], []} ->
654                    {Key, Bytes, CasUnique64};
655                Other ->
656                    {error, Other}
657            end
658    end.
659
660
661parse_values(Data, Socket) ->
662    parse_values(Data, [], Socket).
663
664parse_values(Data, Values, Socket) ->
665    %% Format: VALUE <key> <flags> <bytes> [<cas unique>]\r\n
666    case split(Data) of
667        {error, Reason} ->
668            {error, Reason};
669        {"END", []} ->
670            {ok, lists:reverse(Values)};
671        {"ERROR", []} ->
672            {error, unknown_command};
673        {Head, Tail} ->
674            case parse_single_value(Head) of
675                {Key, Bytes, CasUnique64} ->
676                    case Bytes > length(Tail) of
677                        true -> 
678
679                            Offset = Bytes - length(Tail),
680                            case get_remaining(Data, Offset, Socket) of
681                              {ok, NewData}  ->
682                                parse_values(NewData, Values, Socket);  
683
684                              {error, Reason} ->
685                                {error, Reason}
686                            end;
687
688                        false -> 
689                            {ValueList, Rest}  = lists:split(Bytes, Tail),
690                            Value = list_to_binary(ValueList),
691                            case Rest of
692                                [] -> {ok, lists:reverse([{Key, Value, CasUnique64} | Values])};
693                                [?CR| [?LF | R]] ->
694                                    parse_values(R, [{Key, Value, CasUnique64} | Values], Socket)
695                            end
696                   end;
697                Other ->
698                    Other
699            end
700    end.
701
702
703
704get_remaining(Data, Offset, Socket) ->
705    case gen_tcp:recv(Socket, Offset, ?TIMEOUT) of
706        {ok, Packet0} ->
707            case gen_tcp:recv(Socket, 0, ?TIMEOUT) of
708                {ok, Packet1}   -> Packets = binary_to_list(iolist_to_binary([Packet0, Packet1])),
709                                   {ok, Data ++ Packets};
710
711                {error, Reason} -> {error, Reason}
712            end;
713
714        {error, Reason} -> {error, Reason}
715           
716    end.
717
718
719parse_stats(Data, Stats) ->
720    case Data of
721        "END\r\n" ->
722            {ok, lists:reverse(Stats)};
723        _ ->
724            %% Format: VALUE <key> <flags> <bytes> [<cas unique>]\r\n
725            case split(Data) of
726                {error, Reason} ->
727                    {error, Reason};
728                {Head, Tail} ->
729                    Parsed = io_lib:fread("STAT ~s ", Head),
730                    {ok, [Key], Rest} = Parsed,
731                    parse_stats(Tail, [{Key, Rest} | Stats])
732            end
733    end.
734
735storage_command(Connections, CHash, Socket, Command, Key, Value, Flags, ExpTime) when is_integer(Flags) andalso is_integer(ExpTime) ->
736    EmptyCasUnique64 = <<>>,
737    storage_command(Connections, CHash, Socket, Command, Key, Value, Flags, ExpTime, EmptyCasUnique64).
738storage_command(Connections, CHash, _Socket, Command, Key, Value, Flags, ExpTime, CasUnique64) when is_integer(Flags) andalso is_integer(ExpTime) ->
739    call_with_socket(
740      fun(Socket, NewConnections) ->
741              ValueAsBinary = Value,
742              Bytes = integer_to_list(size(ValueAsBinary)),
743              CommandAsBinary = iolist_to_binary([Command, <<" ">>, Key, <<" ">>, integer_to_list(Flags), <<" ">>, integer_to_list(ExpTime), <<" ">>, Bytes, <<" ">>, CasUnique64]),
744              gen_tcp:send(Socket, <<CommandAsBinary/binary, "\r\n">>),
745              gen_tcp:send(Socket, <<ValueAsBinary/binary, "\r\n">>),
746              {reply,
747               case gen_tcp:recv(Socket, 0, ?TIMEOUT) of
748                   {ok, Packet} ->
749                       case string:tokens(binary_to_list(Packet), "\r\n") of
750                           ["STORED"] ->
751                               ok;
752                           ["NOT_STORED"] ->
753                               {error, not_stored};
754                           ["ERROR"] ->
755                               {error, unknown_command};
756                           %% memcached returns this for append command.
757                           ["ERROR", "ERROR"] ->
758                               {error, unknown_command};
759                           Other ->
760                               io:format("Other=~p~n", [Other]),
761                               {error, Other}
762                       end;
763                   {error, Reason} ->
764                       {error, Reason}
765              end,
766               {NewConnections, CHash, _Socket}}
767      end,
768      Key, Connections, CHash, _Socket).
769
770
771%% memcached 1.4.0 or higher doesn't support time argument.
772delete_command(Socket, Key) ->
773    Command = iolist_to_binary([<<"delete ">>, Key]),
774    gen_tcp:send(Socket, <<Command/binary, "\r\n">>),
775    case gen_tcp:recv(Socket, 0, ?TIMEOUT) of
776        {ok, <<"DELETED\r\n">>} ->
777            ok;
778        {ok, <<"NOT_FOUND\r\n">>} ->
779            {error, not_found};
780        {ok, Other} ->
781            {error, binary_to_list(Other)};
782        {error, Reason} ->
783            {error, Reason}
784    end.
785
786get_command(Socket, GetCommand, Keys) ->
787    Command = iolist_to_binary([GetCommand, <<" ">>, string_join(" ", Keys)]),
788    gen_tcp:send(Socket, <<Command/binary, "\r\n">>),
789    case gen_tcp:recv(Socket, 0, ?TIMEOUT) of
790        {ok, Packet} ->
791            parse_values(binary_to_list(Packet), Socket);
792        Other ->
793            Other
794    end.
795
796
797incr_decr_command(Socket, IncrDecr, Key, Value) ->
798    Command = iolist_to_binary([IncrDecr, " ", Key, " ", list_to_binary(integer_to_list(Value)), " "]),
799    gen_tcp:send(Socket, <<Command/binary, "\r\n">>),
800    case gen_tcp:recv(Socket, 0, ?TIMEOUT) of
801        {ok, Packet} ->
802            case split(binary_to_list(Packet)) of
803                {error, Reason} ->
804                    {error, Reason};
805                {"NOT_FOUND", []} ->
806                   {error, not_found};
807                {NewValueString, []} ->
808                    case io_lib:fread("~u", NewValueString) of
809                        {ok, [NewValue], []} ->
810                            {ok, NewValue};
811                        Other ->
812                            {error, Other}
813                    end;
814                Other ->
815                    {error, Other}
816            end
817    end.
818
819
820get_socket(Key, Connections, CHash) ->
821    Server = memcached_chash:get_node(CHash, Key),
822    case ets:lookup(Connections, Server) of
823        [{Server, {Host, Port}}] ->
824            case gen_tcp:connect(Host, Port, ?TCP_OPTIONS) of
825                {ok, Socket} ->
826                    Server = Host ++ integer_to_list(Port),
827                    true = ets:insert(Connections, {Server, Socket}),
828                    {ok, Socket, Connections};
829                {error, Reason} ->
830                    {error, Reason};
831                Other ->
832                    {error, Other}
833            end;
834        [{Server, Socket}] ->
835            {ok, Socket, Connections}
836    end.
837
838all_sockets(Connections) ->
839    ets:foldr(fun(X, Accum) ->
840                      case X of
841                          {_Server, Socket} when is_port(Socket) -> [Socket | Accum];
842                          {_Host, _Port} ->
843                              Accum
844                      end
845              end,
846              [],
847              Connections).
848
849
850
851%% Borrowed from http://www.trapexit.org/String_join_with
852string_join(Join, L) ->
853    string_join(Join, L, fun(E) -> E end).
854
855string_join(_Join, L=[], _Conv) ->
856    L;
857string_join(Join, [H|Q], Conv) ->
858    lists:flatten(lists:concat(
859        [Conv(H)|lists:map(fun(E) -> [Join, Conv(E)] end, Q)]
860    )).
861
862
863split(Head, S) ->
864    case S of
865        [?CR | [?LF | More]] ->
866            {lists:reverse(Head), More};
867        [] ->
868            {error, not_found};
869        [H | T] ->
870            split([H | Head], T)
871    end.
872
873%% split string with "\r\n"
874split(S) ->
875    split([], S).