Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
91668668
Commit
91668668
authored
Mar 25, 2009
by
antirez
Browse files
Erlang client synched with Valentiono's repo
parent
f78fd11b
Changes
7
Show whitespace changes
Inline
Side-by-side
client-libraries/erlang/.hg_archival.txt
View file @
91668668
repo: 9e1f35ed7fdc7b3da7f5ff66a71d1975b85e2ae5
node:
7f98e864d76b0b2a7427049b943fb1c0dad0df2a
node:
d9dd3d00c6fafaa09809061816f4e3b85a32811d
client-libraries/erlang/include/erldis.hrl
View file @
91668668
-
record
(
redis
,
{
socket
,
buffer
=
[],
reply_caller
,
parsers
,
remaining
=
0
,
pstate
=
empty
,
results
=
[]}).
\ No newline at end of file
-
record
(
redis
,
{
socket
,
buffer
=
[],
reply_caller
,
calls
=
0
,
remaining
=
0
,
pstate
=
empty
,
results
=
[]}).
client-libraries/erlang/src/client.erl
View file @
91668668
...
...
@@ -34,34 +34,6 @@ format(Lines) ->
format
(
Lines
,
[]).
sformat
(
Line
)
->
format
([
Line
],
[]).
get_parser
(
Cmd
)
when
Cmd
=:=
set
orelse
Cmd
=:=
setnx
orelse
Cmd
=:=
del
orelse
Cmd
=:=
exists
orelse
Cmd
=:=
rename
orelse
Cmd
=:=
renamenx
orelse
Cmd
=:=
rpush
orelse
Cmd
=:=
lpush
orelse
Cmd
=:=
ltrim
orelse
Cmd
=:=
lset
orelse
Cmd
=:=
sadd
orelse
Cmd
=:=
srem
orelse
Cmd
=:=
sismember
orelse
Cmd
=:=
select
orelse
Cmd
=:=
move
orelse
Cmd
=:=
save
orelse
Cmd
=:=
bgsave
orelse
Cmd
=:=
flushdb
orelse
Cmd
=:=
flushall
->
fun
proto
:
parse
/
2
;
get_parser
(
Cmd
)
when
Cmd
=:=
lrem
->
fun
proto
:
parse_special
/
2
;
get_parser
(
Cmd
)
when
Cmd
=:=
incr
orelse
Cmd
=:=
incrby
orelse
Cmd
=:=
decr
orelse
Cmd
=:=
decrby
orelse
Cmd
=:=
llen
orelse
Cmd
=:=
scard
->
fun
proto
:
parse_int
/
2
;
get_parser
(
Cmd
)
when
Cmd
=:=
type
->
fun
proto
:
parse_types
/
2
;
get_parser
(
Cmd
)
when
Cmd
=:=
randomkey
->
fun
proto
:
parse_string
/
2
;
get_parser
(
Cmd
)
when
Cmd
=:=
get
orelse
Cmd
=:=
lindex
orelse
Cmd
=:=
lpop
orelse
Cmd
=:=
rpop
->
fun
proto
:
single_stateful_parser
/
2
;
get_parser
(
Cmd
)
when
Cmd
=:=
keys
orelse
Cmd
=:=
lrange
orelse
Cmd
=:=
sinter
orelse
Cmd
=:=
smembers
orelse
Cmd
=:=
sort
->
fun
proto
:
stateful_parser
/
2
.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
...
...
@@ -78,12 +50,12 @@ connect(Host, Port) ->
ssend
(
Client
,
Cmd
)
->
ssend
(
Client
,
Cmd
,
[]).
ssend
(
Client
,
Cmd
,
Args
)
->
gen_server
:
cast
(
Client
,
{
send
,
sformat
([
Cmd
|
Args
])
,
get_parser
(
Cmd
)
}).
gen_server
:
cast
(
Client
,
{
send
,
sformat
([
Cmd
|
Args
])}).
send
(
Client
,
Cmd
)
->
send
(
Client
,
Cmd
,
[]).
send
(
Client
,
Cmd
,
Args
)
->
gen_server
:
cast
(
Client
,
{
send
,
string
:
join
([
str
(
Cmd
),
format
(
Args
)],
" "
)
,
get_parser
(
Cmd
)
}).
string
:
join
([
str
(
Cmd
),
format
(
Args
)],
" "
)}).
asend
(
Client
,
Cmd
)
->
gen_server
:
cast
(
Client
,
{
asend
,
Cmd
}).
...
...
@@ -104,23 +76,23 @@ init([Host, Port]) ->
{
error
,
Why
}
->
{
error
,
{
socket_error
,
Why
}};
{
ok
,
Socket
}
->
{
ok
,
#redis
{
socket
=
Socket
,
parsers
=
queue
:
new
()
}}
{
ok
,
#redis
{
socket
=
Socket
,
calls
=
0
}}
end
.
handle_call
({
send
,
Cmd
,
Parser
},
From
,
State
=
#redis
{
parsers
=
Parsers
}
)
->
handle_call
({
send
,
Cmd
},
From
,
State
)
->
gen_tcp
:
send
(
State
#redis.socket
,
[
Cmd
|
?
EOL
]),
{
noreply
,
State
#redis
{
reply_caller
=
fun
(
V
)
->
gen_server
:
reply
(
From
,
lists
:
nth
(
1
,
V
))
end
,
parsers
=
queue
:
in
(
Parser
,
Parsers
),
remaining
=
1
}};
remaining
=
1
}};
handle_call
(
disconnect
,
_
From
,
State
)
->
{
stop
,
normal
,
ok
,
State
};
handle_call
(
get_all_results
,
From
,
State
)
->
case
queue
:
is_empty
(
State
#redis.
parsers
)
of
true
->
case
State
#redis.
calls
of
0
->
% answers came earlier than we could start listening...
% Very unlikely but totally possible.
{
reply
,
lists
:
reverse
(
State
#redis.results
),
State
#redis
{
results
=
[]}};
false
->
{
reply
,
lists
:
reverse
(
State
#redis.results
),
State
#redis
{
results
=
[]
,
calls
=
0
}};
_
->
% We are here earlier than results came, so just make
% ourselves wait until stuff is ready.
{
noreply
,
State
#redis
{
reply_caller
=
fun
(
V
)
->
gen_server
:
reply
(
From
,
V
)
end
}}
...
...
@@ -131,18 +103,17 @@ handle_call(_, _From, State) -> {noreply, State}.
handle_cast
({
asend
,
Cmd
},
State
)
->
gen_tcp
:
send
(
State
#redis.socket
,
[
Cmd
|
?
EOL
]),
{
noreply
,
State
};
handle_cast
({
send
,
Cmd
,
Parser
},
State
=
#redis
{
parsers
=
Parsers
,
remaining
=
Remaining
})
->
handle_cast
({
send
,
Cmd
},
State
=
#redis
{
remaining
=
Remaining
,
calls
=
Calls
})
->
% how we should do here: if remaining is already != 0 then we'll
% let handle_info take care of keeping track how many remaining things
% there are. If instead it's 0 we are the first call so let's just
% do it.
gen_tcp
:
send
(
State
#redis.socket
,
[
Cmd
|
?
EOL
]),
NewParsers
=
queue
:
in
(
Parser
,
Parsers
),
case
Remaining
of
0
->
{
noreply
,
State
#redis
{
remaining
=
1
,
parsers
=
NewParsers
}};
{
noreply
,
State
#redis
{
remaining
=
1
,
calls
=
1
}};
_
->
{
noreply
,
State
#redis
{
parsers
=
NewParsers
}}
{
noreply
,
State
#redis
{
calls
=
Calls
+
1
}}
end
;
handle_cast
(_
Msg
,
State
)
->
{
noreply
,
State
}.
...
...
@@ -152,13 +123,6 @@ trim2({ok, S}) ->
trim2
(
S
)
->
trim2
({
ok
,
S
}).
% This is useful to know if there are more messages still coming.
get_remaining
(
ParsersQueue
)
->
case
queue
:
is_empty
(
ParsersQueue
)
of
true
->
0
;
false
->
1
end
.
% This function helps with pipelining by creating a pubsub system with
% the caller. The caller could submit multiple requests and not listen
% until later when all or some of them have been answered, at that
...
...
@@ -175,10 +139,8 @@ get_remaining(ParsersQueue) ->
% results yet.
% In case we have requested results: if requests are not yet ready we
% just push them, otherwise we finally answer all of them.
save_or_reply
(
Result
,
State
=
#redis
{
results
=
Results
,
reply_caller
=
ReplyCaller
,
parsers
=
Parsers
})
->
case
get_remaining
(
Parsers
)
of
1
->
State
#redis
{
results
=
[
Result
|
Results
],
remaining
=
1
,
pstate
=
empty
,
buffer
=
[]};
save_or_reply
(
Result
,
State
=
#redis
{
calls
=
Calls
,
results
=
Results
,
reply_caller
=
ReplyCaller
})
->
case
Calls
of
0
->
% We don't reverse results here because if all the requests
% come in and then we submit another one, if we reverse
...
...
@@ -195,60 +157,73 @@ save_or_reply(Result, State=#redis{results=Results, reply_caller=ReplyCaller, pa
end
,
NewState
#redis
{
remaining
=
0
,
pstate
=
empty
,
reply_caller
=
undefined
,
buffer
=
[],
parsers
=
Parsers
}
calls
=
0
};
_
->
State
#redis
{
results
=
[
Result
|
Results
],
remaining
=
1
,
pstate
=
empty
,
buffer
=
[],
calls
=
Calls
}
end
.
handle_info
({
tcp
,
Socket
,
Data
},
State
)
->
{{
value
,
Parser
},
NewParsers
}
=
queue
:
out
(
State
#redis.parsers
),
handle_info
({
tcp
,
Socket
,
Data
},
State
=
#redis
{
calls
=
Calls
})
->
Trimmed
=
trim2
(
Data
),
NewState
=
case
{
State
#redis.remaining
-
1
,
P
arse
r
(
State
#redis.pstate
,
Trimmed
)}
of
NewState
=
case
{
State
#redis.remaining
-
1
,
proto
:
p
arse
(
State
#redis.pstate
,
Trimmed
)}
of
% This line contained an error code. Next line will hold
% The error message that we will parse.
{
0
,
error
}
->
% reinsert the parser in the front, next step is still gonna be needed
State
#redis
{
remaining
=
1
,
pstate
=
error
,
parsers
=
queue
:
in_r
(
Parser
,
NewParsers
)};
State
#redis
{
remaining
=
1
,
pstate
=
error
};
% The stateful parser just started and tells us the number
% of results that we will have to parse for those calls
% where more than one result is expected. The next
% line will start with the first item to read.
{
0
,
{
hold
,
Remaining
}}
->
% Reset the remaining value to the number of results
% that we need to parse.
% and reinsert the parser in the front, next step is still gonna be needed
State
#redis
{
remaining
=
Remaining
,
pstate
=
read
,
parsers
=
queue
:
in_r
(
Parser
,
NewParsers
)};
case
Remaining
of
nil
->
save_or_reply
(
nil
,
State
#redis
{
calls
=
Calls
-
1
});
_
->
% Reset the remaining value to the number of results that we need to parse.
State
#redis
{
remaining
=
Remaining
,
pstate
=
read
}
end
;
% We either had only one thing to read or we are at the
% end of the stuff that we need to read. either way
% just pack up the buffer and send.
{
0
,
{
read
,
NBytes
}}
->
CurrentValue
=
case
NBytes
of
nil
->
nil
;
_
->
inet
:
setopts
(
Socket
,
[{
packet
,
0
}]),
% go into raw mode to read bytes
CurrentValue
=
trim2
(
gen_tcp
:
recv
(
Socket
,
NBytes
+
2
)),
% also consume the \r\n
CV
=
trim2
(
gen_tcp
:
recv
(
Socket
,
NBytes
+
2
)),
% also consume the \r\n
inet
:
setopts
(
Socket
,
[{
packet
,
line
}]),
% go back to line mode
CV
end
,
OldBuffer
=
State
#redis.buffer
,
case
OldBuffer
of
[]
->
save_or_reply
(
CurrentValue
,
State
#redis
{
parsers
=
NewParsers
});
save_or_reply
(
CurrentValue
,
State
#redis
{
calls
=
Calls
-
1
});
_
->
save_or_reply
(
lists
:
reverse
([
CurrentValue
|
OldBuffer
]),
State
#redis
{
parsers
=
NewParsers
})
save_or_reply
(
lists
:
reverse
([
CurrentValue
|
OldBuffer
]),
State
#redis
{
calls
=
Calls
-
1
})
end
;
% The stateful parser tells us to read some bytes
{
N
,
{
read
,
NBytes
}}
->
% annoying repetition... I should reuse this code.
CurrentValue
=
case
NBytes
of
nil
->
nil
;
_
->
inet
:
setopts
(
Socket
,
[{
packet
,
0
}]),
% go into raw mode to read bytes
CurrentValue
=
trim2
(
gen_tcp
:
recv
(
Socket
,
NBytes
+
2
)),
% also consume the \r\n
CV
=
trim2
(
gen_tcp
:
recv
(
Socket
,
NBytes
+
2
)),
% also consume the \r\n
inet
:
setopts
(
Socket
,
[{
packet
,
line
}]),
% go back to line mode
CV
end
,
OldBuffer
=
State
#redis.buffer
,
State
#redis
{
remaining
=
N
,
buffer
=
[
CurrentValue
|
OldBuffer
],
pstate
=
read
,
parsers
=
queue
:
in_r
(
Parser
,
NewParsers
)};
State
#redis
{
remaining
=
N
,
buffer
=
[
CurrentValue
|
OldBuffer
],
pstate
=
read
};
% Simple return values contained in a single line
{
0
,
Value
}
->
save_or_reply
(
Value
,
State
#redis
{
parsers
=
NewParsers
})
save_or_reply
(
Value
,
State
#redis
{
calls
=
Calls
-
1
})
end
,
inet
:
setopts
(
Socket
,
[{
active
,
once
}]),
...
...
client-libraries/erlang/src/erldis.erl
View file @
91668668
...
...
@@ -31,7 +31,7 @@ incrby(Client, Key, By) -> client:ssend(Client, incrby, [Key, By]).
decr
(
Client
,
Key
)
->
client
:
ssend
(
Client
,
decr
,
[
Key
]).
decrby
(
Client
,
Key
,
By
)
->
client
:
ssend
(
Client
,
decrby
,
[
Key
,
By
]).
get
(
Client
,
Key
)
->
client
:
ssend
(
Client
,
get
,
[
Key
]).
mget
(
Client
,
Keys
)
->
client
:
ssend
(
Client
,
mget
,
Keys
).
%% Commands operating on every value
exists
(
Client
,
Key
)
->
client
:
ssend
(
Client
,
exists
,
[
Key
]).
...
...
client-libraries/erlang/src/proto.erl
View file @
91668668
-
module
(
proto
).
-
export
([
parse
/
2
,
parse_int
/
2
,
parse_types
/
2
,
parse_string
/
2
,
stateful_parser
/
2
,
single_stateful_parser
/
2
,
parse_special
/
2
]).
-
export
([
parse
/
2
]).
parse
(
empty
,
"+OK"
)
->
ok
;
parse
(
empty
,
"+PONG"
)
->
pong
;
parse
(
empty
,
"0"
)
->
parse
(
empty
,
"
:
0"
)
->
false
;
parse
(
empty
,
"1"
)
->
parse
(
empty
,
"
:
1"
)
->
true
;
parse
(
empty
,
"-1"
)
->
{
error
,
no_such_key
};
parse
(
empty
,
"-2"
)
->
{
error
,
wrong_type
};
parse
(
empty
,
"-3"
)
->
{
error
,
same_db
};
parse
(
empty
,
"-4"
)
->
{
error
,
argument_out_of_range
};
parse
(
empty
,
"-"
++
Message
)
->
{
error
,
Message
}.
parse_special
(
empty
,
"-1"
)
->
parse
(
empty
,
"-1"
);
parse_special
(
empty
,
"-2"
)
->
parse
(
empty
,
"-2"
);
parse_special
(
empty
,
N
)
->
list_to_integer
(
N
).
parse_int
(
empty
,
"-ERR "
++
Message
)
->
{
error
,
Message
};
parse_int
(
empty
,
Value
)
->
list_to_integer
(
Value
).
parse_string
(
empty
,
Message
)
->
parse
(
empty
,
"$-1"
)
->
{
read
,
nil
};
parse
(
empty
,
"*-1"
)
->
{
hold
,
nil
};
parse
(
empty
,
"$"
++
BulkSize
)
->
{
read
,
list_to_integer
(
BulkSize
)};
parse
(
read
,
"$"
++
BulkSize
)
->
{
read
,
list_to_integer
(
BulkSize
)};
parse
(
empty
,
"*"
++
MultiBulkSize
)
->
{
hold
,
list_to_integer
(
MultiBulkSize
)};
parse
(
empty
,
Message
)
->
convert
(
Message
).
convert
(
":"
++
Message
)
->
list_to_integer
(
Message
);
% in case the message is not OK or PONG it's a
% real value that we don't know how to convert
% to an atom, so just pass it as is and remove
% the +
convert
(
"+"
++
Message
)
->
Message
;
convert
(
Message
)
->
Message
.
parse_types
(
empty
,
"none"
)
->
none
;
parse_types
(
empty
,
"string"
)
->
string
;
parse_types
(
empty
,
"list"
)
->
list
;
parse_types
(
empty
,
"set"
)
->
set
.
% I'm used when redis returns multiple results
stateful_parser
(
empty
,
"nil"
)
->
nil
;
stateful_parser
(
error
,
"-ERR "
++
Error
)
->
{
error
,
Error
};
stateful_parser
(
empty
,
"-"
++
_
ErrorLength
)
->
error
;
stateful_parser
(
empty
,
NumberOfElements
)
->
{
hold
,
list_to_integer
(
NumberOfElements
)};
stateful_parser
(
read
,
ElementSize
)
->
{
read
,
list_to_integer
(
ElementSize
)}.
% I'm used when redis returns just one result
single_stateful_parser
(
empty
,
"nil"
)
->
nil
;
single_stateful_parser
(
error
,
"-ERR "
++
Error
)
->
{
error
,
Error
};
single_stateful_parser
(
empty
,
"-"
++
_
ErrorLength
)
->
error
;
single_stateful_parser
(
empty
,
ElementSize
)
->
{
read
,
list_to_integer
(
ElementSize
)}.
client-libraries/erlang/test/erldis_tests.erl
View file @
91668668
...
...
@@ -26,19 +26,22 @@ pipeline_test() ->
erldis
:
exists
(
Client
,
"hello"
),
erldis
:
exists
(
Client
,
"foo"
),
erldis
:
get
(
Client
,
"foo"
),
erldis
:
mget
(
Client
,
[
"hello"
,
"foo"
]),
erldis
:
del
(
Client
,
"hello"
),
erldis
:
del
(
Client
,
"foo"
),
erldis
:
exists
(
Client
,
"hello"
),
erldis
:
exists
(
Client
,
"foo"
),
[
true
,
true
,
"bar"
,
true
,
true
,
false
,
false
]
=
erldis
:
get_all_results
(
Client
),
[
true
,
true
,
"bar"
,
[
"kitty!"
,
"bar"
],
true
,
true
,
false
,
false
]
=
erldis
:
get_all_results
(
Client
),
erldis
:
set
(
Client
,
"pippo"
,
"pluto"
),
erldis
:
sadd
(
Client
,
"pippo"
,
"paperino"
),
% foo doesn't exist, the result will be nil
erldis
:
lrange
(
Client
,
"foo"
,
1
,
2
),
erldis
:
lrange
(
Client
,
"pippo"
,
1
,
2
),
[
ok
,
{
error
,
wrong_type
},
nil
,
{
error
,
"Operation against a key holding the wrong kind of value"
}
[
ok
,
{
error
,
"ERR Operation against a key holding the wrong kind of value"
},
nil
,
{
error
,
"ERR Operation against a key holding the wrong kind of value"
}
]
=
erldis
:
get_all_results
(
Client
),
erldis
:
del
(
Client
,
"pippo"
),
[
true
]
=
erldis
:
get_all_results
(
Client
),
...
...
@@ -49,7 +52,7 @@ pipeline_test() ->
erldis
:
rpush
(
Client
,
"a_list"
,
"1"
),
erldis
:
lrem
(
Client
,
"a_list"
,
1
,
"1"
),
erldis
:
lrange
(
Client
,
"a_list"
,
0
,
2
),
[
ok
,
ok
,
ok
,
ok
,
1
,
[
"2"
,
"3"
,
"1"
]]
=
erldis
:
get_all_results
(
Client
),
[
ok
,
ok
,
ok
,
ok
,
true
,
[
"2"
,
"3"
,
"1"
]]
=
erldis
:
get_all_results
(
Client
),
erldis
:
sort
(
Client
,
"a_list"
),
erldis
:
sort
(
Client
,
"a_list"
,
"DESC"
),
...
...
client-libraries/erlang/test/proto_tests.erl
View file @
91668668
...
...
@@ -5,6 +5,6 @@
parse_test
()
->
ok
=
proto
:
parse
(
empty
,
"+OK"
),
pong
=
proto
:
parse
(
empty
,
"+PONG"
),
false
=
proto
:
parse
(
empty
,
"0"
),
true
=
proto
:
parse
(
empty
,
"1"
),
false
=
proto
:
parse
(
empty
,
"
:
0"
),
true
=
proto
:
parse
(
empty
,
"
:
1"
),
{
error
,
no_such_key
}
=
proto
:
parse
(
empty
,
"-1"
).
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment