7

Say you were writing a daemon that services a job queue. Various other software writes jobs for the daemon into the queue. The daemon polls the queue every few seconds for pending jobs. Assume the queue is implemented as a table in a MySQL database and that the daemon is a simple loop:

  1. get all due jobs from the queue
  2. do the jobs
  3. sleep for N seconds
  4. goto 1

The daemon must survive interrupted service from the MySQL DB server and disruption to DB connections.

Would you design the daemon to connect to the DB server once per cycle? i.e. connect before 1. and disconnect between 2 an 3?

Or would you have daemon keep a connection open? In which case it needs also to a) detect when the server or connection is not working, b) disconnect and reconnect, and c) do so without accumulating DB connections, dud connection descriptors or other dead resources.

If you have a preference, why?

Pros and cons?

Factors that enter into the design?

Any other approaches?

The answer here: mysql connection from daemon written in php doesn't say why it's better to keep the connection open. I read elsewhere that the per-connection overhead in MySQL is very light. So it isn't obvious why permanently consuming one server connection is better than connecting/disconnecting every few seconds.

In my case the daemon is written in PHP.

Community
  • 1
  • 1
  • What is it you're trying to do? Depending on the case, it sounds like a FIFO file or a socket might be better for triggering the daemon... (And what I would do would be to re-create the connection every poll interval, but then again I wouldn't be polling every few seconds)... – ircmaxell Nov 12 '10 at 21:57
  • @ircmaxell: a FIFO isn't convenient in my case because the processes that put jobs in the queue need to be able to read it and because the job queue state needs to survive server reboots. MySQL replication of the queue is also handy. –  Nov 16 '10 at 15:49
  • I didn't mean to pass all the data via a FIFO. Just to trigger the daemon to read from the mysql based queue. So basically you'd `INSERT INTO queue VALUES (job)`, then `file_put_contents('fifo.file', '1');`. The daemon would sleep trying to open the fifo. So if it's done processing all it basically will block on `fopen('fifo.file', 'r');`. The next time a process adds to the queue, it just notifies the daemon which then immediately starts processing the job. But while it's sleeping it's not consuming any resources since it's a system call that's blocking (even for hours at a time). – ircmaxell Nov 16 '10 at 15:56
  • @ircmaxell: that's a swell approach if you want to kick the daemon to service the queue after you put a job in it. if, otoh, queued jobs might be specified as due for running only after such-and-such a time in the future, then it is not useful. –  Nov 16 '10 at 21:02

3 Answers3

7

I'm actually work on something very close to what you described, but in my case the daemon doesn't poll for event it get's them asynchronously via XMPP (but that's besides the point).

Cut out the Middle Man

I think instead of storing the events in the database and polling them w/ MySQL you could probably use Gearman to send them from the client asynchronously (example).

Garbage Collection

PHP isn't really designed to run as a daemon, and it wasn't until PHP 5.3 when it got circular reference garbage collection that it became a viable option. It's very important that you use PHP 5.3 if you want any chance at long term running without memory leaks.

Another thing to keep in mind about GC is that memory is only free if it's not longer referenced (anywhere). So if you assign a variable to the global scope, it'll be there until the daemon exits. It's important that any code you create or use doesn't built up variables in places (ie, static log, not removing old data, etc).

Stat Cache

Another thing is that it's important to run clearstatcache every so often. Since your PHP process isn't restarted it's important to do this call manually to prevent getting old stat data (which may or may not effect you). According to the documentation these functions are cached.

Affected functions include stat(), lstat(), file_exists(), is_writable(), is_readable(), is_executable(), is_file(), is_dir(), is_link(), filectime(), fileatime(), filemtime(), fileinode(), filegroup(), fileowner(), filesize(), filetype(), and fileperms().

Resource management

If your going to be using thing like MySQL during the lifetime of your process, I'd suggest making one connection at startup and keeping it alive. Even though it might use more ram on the MySQL side, you'll cut out some latency and CPU overhead by not having to connect every 1 second.

No URL request

This may seem obvious, but with CLI PHP there is no URL request info. Some libraries aren't written with this in mind, and this can cause some problems.

LooPHP

I'm going to pop a shameless plug in here for a framework I wrote to help with the management of PHP daemons. LooPHP is a run loop framework that lets you schedule event to happen or create listens for abstract sources (socket, stream, etc). In my case, I have the daemon doing more than 1 thing, so it's very helpful to have system keep track of all the timers for me so that I can effectively poll stream_select for the XMPP connection.

Community
  • 1
  • 1
Kendall Hopkins
  • 43,213
  • 17
  • 66
  • 89
  • That's some very useful commentary, Kendall. Thanks! I'm pretty sure I have no circular references but I'm running on 5.3.3 so that's OK. Not using `stat` but that's a good general point. No shame in sharing LooPHP just because you wrote it. It's more than I need just now but I have another project on the horizon that might find it very useful. –  Nov 12 '10 at 22:29
  • @fsb How are you putting jobs into the queue? I might be worth looking at Gearman (my first point). It abstract a lot of the queue processing for you. – Kendall Hopkins Nov 12 '10 at 22:48
  • Like I said in the OP, the queue is a MySQL table. So jobs are put in it with INSERT. –  Nov 16 '10 at 15:34
  • 1
    I will take a look at Gearman. In my case, the convenience for having the queue in MySQL is that it is easy for processes that create jobs to examine the queue. Plus some jobs are delayed until a specified time in the future when they are entered into the queue. And with the queue in MySQL, its state survives various things including server reboot. –  Nov 16 '10 at 15:43
  • @fsb If you need to queue to live over reboots, then MySQL is probably a good choice. If the queue was simply 1 one-way pipe that jobs from the client would be pushed to the daemon then Gearman is a good fit, but in your case you need more complexity. – Kendall Hopkins Nov 16 '10 at 17:39
1

If you want to make a reliable daemon, you will need to catch database errors/disconnects and reconnect in either case (disconnecting or staying connected). Since you need to do this anyway, you might as well re-use one connection.

In other words, just because you have a freshly opened connection it does not mean that the query won't fail and the connection need to be re-opened and tried again.

So, I believe that the cleanest way would be to preserve the connection. But only barely.

Sean Reifschneider
  • 1,261
  • 10
  • 16
  • Hi Sean. In my case it's fine for the daemon to use the same loop for polling as it does for reconnecting. So if the freshly opened connection doesn't work, or something DB related fails in steps 1 (see OP) the deamon just skips to step 3. So an explicit reconnect-on-db-dailure code branch is obviated. –  Nov 16 '10 at 15:46
0

I think that the best thing you could do is to measure the time it takes to connect/disconnect to/from the database. Then try and come up with some sort of likelihood of the database server becoming unavailable. Determine the cost of a permanent server connection. And finally try to determine the cost (in hours, annoyance, or whatever) of adding code that deals with database connectivity issues. If you can successfully determine those numbers, and compare them, you have an answer. It is difficult for me (and I assume anyone) to come up with good guesstimates for those values, but the conclusion will probably be that it is a choice between performance versus feasibility (in terms of lower cost).

Klaus Byskov Pedersen
  • 117,245
  • 29
  • 183
  • 222
  • A conclusive quantitative study of server resource costs relative to daemon design difficulties is going to cost me more than the server hosting fees;-) –  Nov 12 '10 at 22:36
  • @fsb, haha, I'm sure you're right. And you are still confident that you are not micro-optimizing or worrying about a problem that you don't have? – Klaus Byskov Pedersen Nov 12 '10 at 22:42