Earlier quoted context omitted.
Not recently, no. I do know that a journaled filesystem can exacerbate this sort of problem as it can make extra work. For example: http://lwn.net/Articles/328363/ In a few cases in the past when dealing with unimportant data I have downgraded to ext2 for a nice performance bump.
Just to make sure we're talking about the same thing, a journalling FS isn't the same thing as a log-structured FS. The first has a write-ahead log, the latter is basically just a log. So immediately writing to disk is relatively simple.
Logging can be tricky
61–70 of 77 posts
Re: Logging can be tricky
#62Earlier quoted context omitted.
Making sure your logging data gets written is relatively important. Say, if you hit a bug somewhere that causes the server itself to crash.
How about installing a SIGSEGV, etc. handler to do the fsync (and perhaps even print a stacktrace)?
Re: Logging can be tricky
#63Earlier quoted context omitted.
Hell, we're now infrastructure-less. No VM's (except for our QA to test on, doesn't count), no static servers we can't just destroy and spin up another of in It truly is a joy.
Curious: How do you persist data with no database servers? Using an outside service to store it?
I also forgot to mention we have a build server, but thats just a virtual machine that we backup regularly. It's our only piece of static infrastructure. Everything else, except storage which is, again, transparent to us, is destroyed on a weekly, if not daily, basis for new releases.
Re: Logging can be tricky
#64Compressing the text logs prior to writing them to the disk also helps with these kinds of issues. You can also offload your logging to a dedicated thread and then use a lock free queue to increase your performance even more.
> You can also offload your logging to a dedicated thread and then use a lock free queue to increase your performance even more. Or use syslog() like a sane person, and let that "extra thread" live inside the OS IPC mechanism. (Or stdout/stderr like a modern sane person, and let upstart/systemd/docker/etc. push your logs to syslog if that's where it feels like pushing them.)
As it turns out, we had nearly the same symptoms as the OP. After running strace on syslogd (CentOS 5), I realized that it also does an fsync() after every call. A quick trip to the man page and a configuration change later, our issues disappeared.
Re: Logging can be tricky
#65Earlier quoted context omitted.
IMO the real issue is that a competent logging framework doesn't block app code to sync the log to disk. The buffer should be swapped out under lock, and then synced in a separate thread. Yuck.
The downside is of course that if you crash hard, the most valuable log entries are the ones least likely to be on-disk afterwards.
Re: Logging can be tricky
#66Why would you fsync logs for a high-level service? Are you afraid a power outage is going to cause you to lose service logs?
Re: Logging can be tricky
#67Reminds me of the time I sped up the main business app of a large company by 85% by removing "debug" logging. 2tb/hr of "made it here" isn't really useful at the end of the day. Not the first time I've seen that by a long shot. //shakes zimmerframe, shuffles off
Thus why your logging library should have a configurable log-level, and the first thing the user-facing log() function should do is check the arguments against the log-level and early-return if the message isn't important enough. (In languages that support macros, you could also just make debug_log() a macro that gets compiled out on release builds, like assert(). That means a whole bunch of object files are going to…
This is a pretty fantastic route as far as syntactical simplicity goes.
I wonder if it can have an implications on the generated assembly nonetheless? Could there be extra assembly generated outside the branch for logging level check, which is necessary to bind the variables for the closure?
Similarly, I might suspect that an optimizing compiler that is considering a function for inlining might change its mind for any function where it sees a nontrivial closure like that, without knowing that "trace" level logging nearly never happens.
"Edge cases", to be sure, and the answers are surely different between languages, but interesting to consider nonetheless.
Re: Logging can be tricky
#68Compressing the text logs prior to writing them to the disk also helps with these kinds of issues. You can also offload your logging to a dedicated thread and then use a lock free queue to increase your performance even more.
> You can also offload your logging to a dedicated thread and then use a lock free queue to increase your performance even more. Or use syslog() like a sane person, and let that "extra thread" live inside the OS IPC mechanism. (Or stdout/stderr like a modern sane person, and let upstart/systemd/docker/etc. push your logs to syslog if that's where it feels like pushing them.)
Using a well implemented ring buffer [+] can get enqueue operations down to a few instructions and something like two memory fences.
The overhead of IPC, which wakes up the kernel scheduler, switches the processor back and forth between privilege modes a few times on the way, knocks apart all the CPU cache and register state to swap in another process, while the MMU is flipping all your pages around because these two processes don't trust each other to write directly into their respective memory... is not going to have quite the same performance characteristics.
An moment in the history of logging is java's log4j framework, which, within a single process, used exclusive synchronization. When this was replaced by a (relatively) lock-free queue implementation, throughput increased by orders of magnitude. (Their notes and graphs on this can be found at https://logging.apache.org/log4j/2.x/manual/async.html .) This isn't an exact metaphor for the difference between a good lockfree ringbuffer and IPC either, but it certainly has some similarities, and indeed ends with a specific shout-out to the power of avoiding "locks requiring kernel arbitration".
--
[+] The "mechanical sympathy" / Disruptor folks have some great and accessible writeups on how they addressed the finer points of high performance shared memory message passing. http://mechanitis.blogspot.com/2011/06/dissecting-disruptor-... is one of my favorite reads.
Re: Logging can be tricky
#69Earlier quoted context omitted.
Thus why your logging library should have a configurable log-level, and the first thing the user-facing log() function should do is check the arguments against the log-level and early-return if the message isn't important enough. (In languages that support macros, you could also just make debug_log() a macro that gets compiled out on release builds, like assert(). That means a whole bunch of object files are going to…
Clever. I like that (using the closure). I've never used Elixir but toyed with Erlang many years ago - is it called asynchronously by the logging process in Elixir? If so, I guess you're relying on immutable data structures to make sure you get the correct logging output?
This gives you another neat benefit, nearly for free: if you have, say, application Foo on node A, wanting to log to SyslogSink on node B, then Foo first sends its logs to a gen_event on A, and the SyslogSink handler in that gen_event gets the opportunity to drop messages, using whatever logic SyslogSink likes, before forwarding them over to B (which might be half-way across the Internet.)
Re: Logging can be tricky
#70Earlier quoted context omitted.
> You can also offload your logging to a dedicated thread and then use a lock free queue to increase your performance even more. Or use syslog() like a sane person, and let that "extra thread" live inside the OS IPC mechanism. (Or stdout/stderr like a modern sane person, and let upstart/systemd/docker/etc. push your logs to syslog if that's where it feels like pushing them.)
The OS IPC mechanism quite likely does not use a lock free queue -- or at least, not in quite the same way as I think the grandparent post refers to. Using a well implemented ring buffer [+] can get enqueue operations down to a few instructions and something like two memory fences. The overhead of IPC, which wakes up the kernel scheduler, switches the processor back and forth between privilege modes a few times on th…