• Stars
    star
    180
  • Rank 213,097 (Top 5 %)
  • Language
    Common Lisp
  • License
    Other
  • Created about 15 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Redis client for Common Lisp

CL-REDIS — A fast and robust Common Lisp client for Redis

(tested with Redis version 3.0.0 (2.9.104 to be precise))

Usage

Quickstart

  1. Make sure a Redis server is running.
  2. (ql:quickload 'cl-redis)
  3. Connect to the server to the given host and port with (redis:connect :host <host> :port <port>) (host defaults to 127.0.0.1, port — to 6379).
  4. Interact with the server using Redis commands from the red package.
CL-USER> (red:ping)
"PONG"
  1. Disconnect from the server with (redis:disconnect).
  2. Alternatively, wrap the whole interaction session in with-connection macro, which accepts the same arguments as connect does, opens a socket connection, executes the body of the macro with the current connection (*connection*) bound to this new connection, and ensures that the connection is closed afterwards.

Available commands

Code organization

The system provides 2 packages: REDIS and RED. All the functionality is available from the REDIS package. Not to cause symbol clashes, Redis commands are defined in this package with a prefix (which defaults to red- and is set at compilation time). The package RED is a syntactic sugar — it just provides the Redis commands without a prefix. So it is not intended to be imported to avoid symbol conflicts with package COMMON-LISP — just use the package-qualified symbol names: i.e. the same Redis command (for instance GET) can be called as RED-GET (if you import the REDIS package) or RED:GET.

Installation

Available through quicklisp.

Dependencies

Debugging and error recovery

If *echo-p* is T, all client-server communications will be echoed to the stream *echo-stream*, which defaults to *standard-output*.

Error handling is mimicked after Postmodern. In particular, whenever an error occurs that breaks the communication stream, a condition of type redis-connection-error is signalled offering a :reconnect restart. If it is selected the whole Redis command will be resent, if the reconnection attempt succeeds. Furthermore, connect checks if a connection to Redis is already established, and offers two restarts (:leave and :replace) if this is the case.

When the server respondes with an error reply (i.e., a reply that starts with -), a condition of type redis-error-reply is signalled.

There's also a high-level with-persistent-connection macro, that tries to do the right thing™ (i.e. automatically reopen the connection once, if it is broken).

Advanced usage

PubSub

Since there's no special command to receive messages from Redis via PubSub here's how you do it:

(bt:make-thread (lambda ()
                  (with-connection ()
                    (red:subscribe "foo")
                    (loop :for msg := (expect :anything) :do
                      (print msg))))
                "pubsub-listener")

To publish, obviously:

(with-connection ()
  (red:publish "foo" "test"))

Pipelining

For better performance Redis allows to pipeline commands and delay receiving results until the end, and process them all in oine batch afterwards. To support that there's with-pipelining macro. Compare execution times in the following examples (with pipelining and without: 6.567 secs vs. 2023.924 secs!):

(let ((names (let (acc)
               (dotimes (i 1000 (nreverse acc))
                 (push (format nil "n~a" i) acc))))
      (vals  (let (big-acc)
               (dotimes (i 1000 (nreverse big-acc))
                 (let (acc)
                   (dotimes (i (random 100))
                     (push (list (random 10) (format nil "n~a" i)) acc))
                   (push (nreverse acc) big-acc))))))
  (time (redis:with-connection ()
          (redis:with-pipelining
            (loop :for k :in names :for val :in vals :do
              (dolist (v val)
                (apply #'red:zadd k v)))
            (red:zunionstore "result" (length names) names)
            (red:zrange "result" 0 -1))))

  ;; Evaluation took:
  ;;  6.567 seconds of real time
  ;;  3.900243 seconds of total run time (3.200200 user, 0.700043 system)

  (time (redis:with-connection ()
          (loop :for k :in names :for val :in vals :do
            (dolist (v val)
              (apply #'red:zadd k v)))
          (red:zunionstore "result" (length names) names)
          (red:zrange "result" 0 -1))))

  ;; Evaluation took:
  ;; 2023.924 seconds of real time
  ;; 3.560222 seconds of total run time (2.976186 user, 0.584036 system)

Note, that with-pipelining calls theoretically may nest, but the results will only be available to the highest-level pipeline, all the nested pipelines will return :PIPELINED. So a warining is signalled in this situation.

Internals

Generic functions tell and expect implement the Redis protocol according to the spec. tell specifies how a request to Redis is formatted, expect — how the response is handled. The best way to implement another method on expect is usually with def-expect-method, which arranges reading data from the socket and provides a variable reply, which holds the decoded reply data from the server with the initial character removed. For example:

(def-expect-method :ok
  (assert (string= reply "OK"))
  reply)

Redis operations are defined as ordinary functions by def-cmd for which only arguments and return type should be provided. def-cmd prefixes all the defined functions' names with *cmd-prefix*, which defaults to 'red. (Note, that setting of *cmd-prefix* will have its effects at compile time). It also exports them from REDIS package, and from RED package without the prefix.

An example of command definition is given below:

(def-cmd KEYS (pattern) :multi
  "Return all the keys matching the given pattern.")

See commands.lisp for all defined commands.

Not implemented

  • The following commands are not implemented, because they are not intended for use in client: MONITOR, DEBUG OBJECT, and DEBUG SEGFAULT.
  • Support for Unix domain sockets — planned
  • Consistent hashing isn't built-in. Actually, such thing is orthogonal to the functionality of this library and, probably, should be implemented in a separate library.
  • Connection pooling is also not implemented, because in the presence of with-persistent-connection it is actually not needed so much. Persistent connections are more simple, efficient and less error-prone for dedicated threads. But there are other use-cases for pooling, so it will probably be implemented in future releases.

Credits

The library is developed and maintained by Vsevolod Dyomkin [email protected].

At the initial stages Alexandr Manzyuk [email protected] developed the connection handling code following the implementation in Postmodern. It was since partially rewritten to accommodate more advanced connection handling strategies, like persistent connection.

License

MIT (See LICENSE file for details).

More Repositories

1

rutils

Radical Utilities for Common Lisp
Common Lisp
234
star
2

cl-nlp

Common Lisp NLP toolset
Common Lisp
208
star
3

kpi-spos

Учебный курс "Операционные системы"
Common Lisp
75
star
4

progalgs-code

Code for the book "Programming Algorithms"
Common Lisp
48
star
5

should-test

A minimal yet feature-rich Common Lisp test framework
Common Lisp
42
star
6

wiki-lang-detect

Text language identification using Wikipedia data
Common Lisp
29
star
7

crawlik

Lisp web crawler and scrapper
Common Lisp
25
star
8

cl-agraph

CL-AGRAPH — A minimal portable Lisp client for AllegroGraph
Common Lisp
24
star
9

cl-parsec

Parser combinators in Common Lisp
Common Lisp
21
star
10

prj-algo

Курс по алгоритмике в Projector
C#
21
star
11

prj-nlp-2020

Home of Projector's "Data Science. Natural Language Processing" 2020 Edition
Jupyter Notebook
18
star
12

lisp-crash-ru

Креш-курс по Lisp
Common Lisp
16
star
13

prj-algo2

Java
14
star
14

flight-recorder

Robust REPL logging facility
Common Lisp
13
star
15

const-table

Minimal Perfect Hash-Table CL Implementation
Common Lisp
12
star
16

cl-who

Forked version of Edi Weitz's HTML generator with support for macro definition
Common Lisp
10
star
17

prj-algo3

Common Lisp
9
star
18

nuts

Non-Unit Test Suite -- a test library for Common Lisp
Common Lisp
8
star
19

plain-text

A tool to extract plain text from HTML pages
Common Lisp
8
star
20

date-utils

Date utilities for Common Lisp, that use an internal plist representation of dates
Common Lisp
5
star
21

cl-git-store

Use git as storage from CL
Common Lisp
4
star
22

arnesi

Fork of Marco Barringer's arnesi (http://common-lisp.net/project/bese/arnesi.html)
Common Lisp
4
star
23

lang-uk

Ukrainian language processing using CL-NLP
Common Lisp
4
star
24

asdfx

Some potential ASDF extensions
Common Lisp
2
star
25

sc-dsalg

Data Structures & Algorithms Course @ SoftConstruct
Common Lisp
2
star
26

biochat2

Matching bioinformatics dataset for fun and profit
Common Lisp
2
star
27

vseloved.github.io

My personal website
HTML
1
star
28

asdf2-versions

Broader support for versions in ASDF2
Common Lisp
1
star
29

scripts

Misc shell & other scripts
Emacs Lisp
1
star