[RFC] Creating a libRedis / refactoring global variables

37 views
Skip to first unread message

Simon Wistow

unread,
Jul 20, 2010, 7:54:22 PM7/20/10
to redi...@googlegroups.com
I've got a bit of spare time so I thought I'd start looking into what it
would take to seperate redis-server from libredis (i.e the network layer
from the underlying commands and datatype implementation).

There's 3 motivations for this:

1) Allow people to embed Redis in their apps a la SQLite
2) Allow people to provide alternative network interfaces (HTTP,
Protocol Buffers/Thrift, Binary etc etc)
3) Allow people to write plugins

The way I envisaged plugins was DLLs which were dynamically loaded at
initialisation time. That way people could implement new functionality
without bloating the Redis core - for example I have a code sketch of a
search engine including tokenisation, stemming and normalisation which
probably doesn't want to be core but could be useful for
other people.

To be honest I figured the easy bit would be the seperation of the two
layers and the hard bit would be things like making it easy for arbitary
network layers to cope with new commands and for commands to dictate
that they're exclusive (like the PubSub commands).

Instead it's actually proved to be a fairly laborious task because of
the use of global variables (specifically server and shared).

I've spent the morning attempting to make the global variables go away
but it's turning into a bit of a rathole.

I considered making libredis take server and shared as initialisation
params and then restash them in a global variable but having thought
about that I don't think it will work without some funky shared memory
hacks.

I can continue wading through everything and see where it takes me but I
first wanted to ask:

Am I missing anything here? Are there better ways to do this?

More importantly - if I do finish it is this separation a desirable
feature? Is it likely to ever get merged back in again?

Cheers,

Simon

Jak Sprats

unread,
Jul 21, 2010, 12:28:43 AM7/21/10
to Redis DB
Hi Simon,

Turning a single threaded process into a multi threaded process is
VERY hard.

It is not just global variables you would have to make into per thread
variables. There are also thread-safe functions which would replace
any non thread safe functions, and there are maybe 3-4 more little
hidden problems that make turning a single threaded process into a
multi-threaded one a HUGE task.

Alot of people want libredis, but its a tough call on how to implement
this.

The idea of making the redis-server multi-threaded is also most likely
a bad idea. Threads are good for hiding latency, which usually means
disk-latency. The redis-server is an in memory database meaning random
access speed, so threads really only bring overhead to the redis-
server. There is possible latency in data crossing the RAM to L2
memory wall, but threads dont help here either.

My personal opinion is to stick with the single process model and
abstract the network layer out and replace it with pthread_cond_wait()
on server side, and pthread_cond_signal() client side. This allows for
polling behavior at the kernel layer (i.e. its fast and low overhead).
So the redis-server would be a single process that just processes
requests and the clients could be multi-threaded.

Salvatore mentioned using fakeClient() to do the logic of libredis and
this more or less solves the logic-side or libredis, but how to best
implement the multi-threaded clients talking to the redis-server is
tricky.

- Jak

bigethan

unread,
Jul 21, 2010, 12:43:34 AM7/21/10
to Redis DB
At the SF Redis meetup Antirez indicated that it'd be tricky (as
you're discovering) because things were so intermingled. And that the
intermingling was good for performance, so it was unlikely that
there'd be a push to make it easier/possible.

Can't speak for the actual details of your questions, so not sure if I
should have replied at all :-)

-E

On Jul 20, 4:54 pm, Simon Wistow <si...@thegestalt.org> wrote:

Simon Wistow

unread,
Jul 26, 2010, 6:08:11 PM7/26/10
to redi...@googlegroups.com
On Tue, Jul 20, 2010 at 09:28:43PM -0700, Jak Sprats said:
> Turning a single threaded process into a multi threaded process is
> VERY hard.
>
> It is not just global variables you would have to make into per thread
> variables. There are also thread-safe functions which would replace
> any non thread safe functions, and there are maybe 3-4 more little
> hidden problems that make turning a single threaded process into a
> multi-threaded one a HUGE task.

Yeah, past experience refactoring code bases with global variables has
left with a nervous twitch whenever I see see them. One of the least fun
was trying to turn the DCRAW program into a libdcraw which has in its
FAQ (http://www.cybercom.net/~dcoffin/dcraw/#faq)

* Why don't you implement dcraw as a library?

I have decided that dcraw shall be a command-line program written in
C, and that any further abstraction layers must be added around this
core, not inside it.

Library code is ugly because it cannot use global variables.
Libraries are more difficult to modify, build, install, and test
than standalone programs, and so are inappropriate for file formats
that change every day.

There's a simpler way to make dcraw modular and thread-safe: Run it
as a separate process.


To be honest I'm less bothered about thread safety (although it's the
possible the people with 8 core UltraSPARC-T2s might disagree with me)
and more about letting people write plugins.

For the past week I've been occasionally converting each and every
function that needs it to take a context parameter. It's slow going
because it's incredibly boring, there's little tangile sense of
accomplishment, and, I'll admit, the resulting code isn't pretty.

For simplicity's sake I'm passing the server and shared variables
although I think that really there should be a custom context param
which doesn't have things like the port in it.

At various points during the week I've pondered whether a more sane
approach would be to do a rewrite - stopping occasionally to plunder the
current code base for various snippets and low level implementation
details. At one point I even considered C++ if only for the smart
pointers however I feel this might have been evidence of a high fever or
some other brain scrambling affliction.

So the tl;dr version is:

Progress is slow, tedious and unrewarding, I'm not really in love with
the results anyway and the lack of feedback from Salvatore or Peter
isn't really encouraging either (not that I blame them, especially since
I believe the official road map is to have server side scripting)

So - for the few people who've contacted me privately about this ... I'm
going to keep plugging away for a bit but frankly I wouldn't hold my
breath.

That said if there's ever a Redis 3.0 rewrite then I have some excellent
ideas ....

cheers,

Simon


Jak Sprats

unread,
Jul 27, 2010, 1:05:37 PM7/27/10
to Redis DB
How are you gonna handle multiple threads doing background dumps (in
parallel), or writing to the AOF file(in parallel).

Also the listening port can not be bound to by multiple threads, so a
listener thread and worker threads (ala Apache) need to be introduced.

Also the core HashTable may do some truly weird stuff if one thread
starts a rehash, then yields, then another thread comes around and the
hash table is in an unknown state.

If I thought on this longer I can probably find more things that dont
work in a multi-threaded environment.

I would really like a multi-threaded libredis, but as odd as it
sounds, the smartest way to do that may NOT be to turn the code into
multi-threaded code, rather to abstract ONLY the network layer out and
replace it with a multi-threaded client<->server API using pthread
conditions. Does that make sense? this is difficult to explain

Michael Russo

unread,
Jul 27, 2010, 1:42:41 PM7/27/10
to redi...@googlegroups.com
A straightforward but still valuable approach, in my opinion, is to keep threading as an application-level concern.  Let the application handle serializing access to the library, let the application decide when to save (and make sure that there is max one concurrent save at a time), etc.

It seems that this approach would still allow for the three original motivations to be met.

-Michael

--
You received this message because you are subscribed to the Google Groups "Redis DB" group.
To post to this group, send email to redi...@googlegroups.com.
To unsubscribe from this group, send email to redis-db+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/redis-db?hl=en.


Simon Wistow

unread,
Jul 27, 2010, 8:36:58 PM7/27/10
to redi...@googlegroups.com
On Tue, Jul 27, 2010 at 10:05:37AM -0700, Jak Sprats said:
> How are you gonna handle multiple threads doing background dumps (in
> parallel), or writing to the AOF file(in parallel).

Well, the original plan wasn't to make Redis multi threaded, just to
allow dlopen-ed plugins.

So the short answer is: I'm not.

Longer answer: once the separation is done it might be possible to go in
a wrap various operations with a mutex (probably wrapped in a macro so
that multithreading is a compile time option).

But that's in the future.

Reply all
Reply to author
Forward
0 new messages