Permalink
Browse files

Prepare for github

  • Loading branch information...
wallrat committed Mar 23, 2012
1 parent 34aa63e commit 5948bf10fb1f5f901844312707d208a32d4236c3
View
@@ -17,7 +17,7 @@ keep the client up-to-date with Redis development. Also, useful documention for
- Replies can alse be deref:ed to their underlying values `@@(ping db) => "PONG"`
- Return values are processed as little as possible, eg. `@@(get db "xxx")` returns byte[].
Includes some helper fns for converting to `String` `(->str @(get r "xxx"))` and `String[]` (->strs)
-- Sane pub/sub support, including correct behaviour for UNSUBSCRIBE returning connection to normal state.
+- Sane pub/sub support, including correct behavior for UNSUBSCRIBE returning connection to normal state.
- Support for MULTI/EXEC and return values (see example below).
- labs-redis does not use global `*bindings*` for the connection ref (as in clj-redis and redis-clojure).
In my target code for this library talks to alot of different Redis instances and `(with-connection (client) (set key val))` adds alot of uneccesary boilerplate for us.
@@ -59,7 +59,7 @@ In my target code for this library talks to alot of different Redis instances an
=> "bar"
```
-Arguments to commands are converted in a do-what-I-mean style, so we can write code like
+Arguments to commands are converted and flattened in a do-what-I-mean style, so we can write code like
```clojure
;; ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2012 Preemptive Labs / Andreas Bielk (http://www.preemptive.se)
+ *
+ *************************************************************************************/
+package labs.redis;
+
+
+public class BulkReply extends Reply
+{
+ public static final char MARKER = '$';
+ public final byte[] bytes;
+
+ public BulkReply(byte[] bytes)
+ {
+ this.bytes = bytes;
+ }
+
+ @Override
+ public byte[] getValue()
+ {
+ return bytes;
+ }
+
+ @Override
+ public String toString()
+ {
+
+ return "BulkReply{" +
+ "bytes=" + (bytes == null ? "null" : bytes.length) +
+ '}';
+ }
+}
View
@@ -0,0 +1,136 @@
+/**
+ * Copyright 2012 Preemptive Labs / Andreas Bielk (http://www.preemptive.se)
+ *
+ *************************************************************************************/
+
+package labs.redis;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Client
+{
+ public static final Charset US_ASCII = Charset.forName("US-ASCII");
+ public static final Charset UTF8 = Charset.forName("UTF-8");
+
+ private static final byte[] EVALSHA_BYTES = "EVALSHA".getBytes(US_ASCII);
+ private static final byte[] PING_BYTES = "PING".getBytes(US_ASCII);
+
+ private final Map<String, byte[]> evalCache = new HashMap<String, byte[]>(16);
+ public final Connection protocol;
+ protected LinkedReplyFuture tail = null;
+
+ public Client(final Socket socket)
+ throws IOException
+ {
+ protocol = new Connection(socket);
+ }
+
+ public Client(String host, int port)
+ throws IOException
+ {
+ protocol = new Connection(SocketFactory.newSocket(host, port));
+ }
+
+ public Client()
+ throws IOException
+ {
+ this("localhost", 6379);
+ }
+
+
+ public synchronized LinkedReplyFuture pipeline(Object... o)
+ throws IOException
+ {
+ send(o);
+ this.tail = new LinkedReplyFuture(protocol, this.tail);
+ return this.tail;
+ }
+
+ /**
+ * Send data to Redis, should be paired with pull()
+ */
+ public synchronized void send(Object... o)
+ throws IOException
+ {
+ if (protocol.pipelined.get() > 128) tail.ensure();
+ protocol.send(o);
+ }
+
+ public synchronized LinkedReplyFuture pull()
+ {
+ this.tail = new LinkedReplyFuture(protocol, this.tail);
+ return this.tail;
+ }
+
+ public synchronized void close()
+ throws IOException
+ {
+ this.protocol.close();
+ }
+
+ // benchmark impl of PING
+ public synchronized LinkedReplyFuture ping()
+ throws IOException
+ {
+ send(new Object[]{PING_BYTES});
+ this.tail = new LinkedReplyFuture(protocol, this.tail);
+ return this.tail;
+ }
+
+
+ public synchronized LinkedReplyFuture eval(String lua, Object[] keys, Object[] args)
+ throws IOException
+ {
+ byte[] sha1 = evalCache.get(lua);
+ if (sha1 == null)
+ {
+ sha1 = (byte[]) pipeline("SCRIPT", "LOAD", lua).get().getValue();
+ evalCache.put(lua, sha1);
+ }
+
+ Object[] args2 = new Object[keys.length + args.length + 3];
+ args2[0] = EVALSHA_BYTES;
+ args2[1] = sha1;
+ args2[2] = keys.length;
+ System.arraycopy(keys, 0, args2, 3, keys.length);
+ System.arraycopy(args, 0, args2, 3 + keys.length, args.length);
+
+ send(args2);
+ this.tail = new LinkedReplyFuture(protocol, this.tail);
+ return this.tail;
+ }
+
+ /**
+ * EXEC and update tail with results
+ */
+ public synchronized MultiBulkReply execWithResults()
+ throws IOException
+ {
+ // capture tail
+ LinkedReplyFuture t = tail;
+
+ // EXEC
+ final MultiBulkReply exec = (MultiBulkReply) pipeline("EXEC").get();
+
+ // update tail
+ for (int i = exec.values.length - 1; i >= 0; i--)
+ {
+ // assertions
+ if (t == null) throw new IllegalStateException("Missing tail");
+ if (t.value != StatusReply.QUEUED)
+ throw new IllegalStateException("Currupt tail, expected QUEUED, got " + t.value.getValue());
+
+ t.value = exec.values[i];
+ t = t.tail;
+ }
+
+ // assertion
+ if (t != null) throw new IllegalStateException("Found longer tail than expected " + t.tail.value);
+
+ return exec;
+ }
+}
@@ -0,0 +1,79 @@
+/**
+ * Copyright 2012 Preemptive Labs / Andreas Bielk (http://www.preemptive.se)
+ *
+ *************************************************************************************/
+
+package labs.redis;
+
+import java.io.IOException;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
+
+public class ClientPool
+{
+ private final Queue<Client> queue = new ArrayBlockingQueue<Client>(100, true);
+ private final String host;
+ private final int port;
+ private final boolean testOnBorrow;
+
+ public ClientPool(String host, int port, boolean testOnBorrow)
+ {
+ this.host = host;
+ this.port = port;
+ this.testOnBorrow = testOnBorrow;
+ }
+
+ public ClientPool(boolean testOnBorrow)
+ {
+ this("localhost",6379, testOnBorrow);
+ }
+
+ public synchronized int size() { return queue.size(); }
+
+ public synchronized Client borrow()
+ throws IOException
+ {
+ final Client client = queue.poll();
+
+ if (client != null)
+ {
+ if (valid(client))
+ return client;
+ else
+ return borrow();
+ }
+
+ return new Client(SocketFactory.newSocket(host, port));
+ }
+
+ public synchronized void release(Client client)
+ {
+ // validate
+ if (client != null && valid(client)) queue.add(client);
+ }
+
+ private boolean valid(final Client client)
+ {
+ if (this.testOnBorrow)
+ {
+ try
+ {
+ return client.ping().get() == StatusReply.PONG;
+ }
+ catch (IOException e)
+ {
+ return false;
+ }
+ }
+
+ return client.protocol.isConnected();
+ }
+
+ public synchronized void flush()
+ throws IOException
+ {
+ Client c = null;
+ while((c=queue.poll()) != null)
+ c.close();
+ }
+}
Oops, something went wrong.

0 comments on commit 5948bf1

Please sign in to comment.