Cache open/stat for performance
Open fd stays valid for 60s. It will actually stick around forever,
there is no garbage collection for stale files yet.
Signed-off-by: Joern Engel <joern@logfs.org>
diff --git a/cancd.c b/cancd.c
index ebd8bdd..4538f6c 100644
--- a/cancd.c
+++ b/cancd.c
@@ -105,6 +105,9 @@
const char *filename;
const char *tmpfilename;
int had_newline;
+ int fd;
+ uint64_t filesize;
+ uint64_t reopen_time;
};
void handler(int signum)
@@ -322,7 +325,7 @@
return 0;
}
-static int write_formatted(int fd, struct source_ip *sip, char *buf, int count)
+static int write_formatted(struct source_ip *sip, char *buf, int count)
{
const char *format = "%b %d %H:%M:%S ";
char *end;
@@ -340,19 +343,21 @@
while (count > 1) {
if (sip->had_newline) {
n = strftime(timestr, sizeof(timestr), format, tm);
- err = do_write(fd, timestr, n);
+ err = do_write(sip->fd, timestr, n);
if (err)
return err;
}
end = strchr(buf, 0xa);
if (!end) {
/* no newline, just write what we have */
- do_write(fd, buf, count - 1);
+ err = do_write(sip->fd, buf, count - 1);
+ if (err)
+ return err;
sip->had_newline = 0;
break;
}
len = end - buf + 1;
- err = do_write(fd, buf, len);
+ err = do_write(sip->fd, buf, len);
if (err)
return err;
buf += len;
@@ -372,6 +377,7 @@
if (!sip) {
sip = calloc(1, sizeof(*sip));
sip->had_newline = 1;
+ sip->fd = -1;
sip->tmpfilename = get_path(&addr->sin_addr);
err = btree_insert32(&btree, key, sip);
assert(!err);
@@ -398,6 +404,10 @@
char buf[4096];
ssize_t count;
+ if (sip->fd >= 0) {
+ close(sip->fd);
+ sip->fd = -1;
+ }
fd = open(name, O_WRONLY | O_APPEND | O_CREAT, 0644);
if (fd < 0) {
syslog(LOG_ERR, "Unable to open \"%s\": %s", name,
@@ -427,9 +437,17 @@
return NULL;
}
+uint64_t gethrtime(void)
+{
+ struct timespec tod;
+
+ clock_gettime(CLOCK_MONOTONIC, &tod);
+ return tod.tv_sec * 1000000000ULL + tod.tv_nsec;
+}
+
static void do_output(char *buf, int len, struct sockaddr_in *addr, socklen_t socklen)
{
- int fd, err;
+ int err;
struct stat stat;
const char *name;
struct source_ip *sip = get_source_ip(addr);
@@ -438,17 +456,35 @@
if (!name)
return;
- fd = open(name, O_WRONLY | O_APPEND | O_CREAT, 0644);
- if (fd < 0) {
- syslog(LOG_ERR, "Unable to open \"%s\": %s", name, strerror(errno));
- return;
+ /*
+ * We cache the open fd for 60s. We also cache the filesize.
+ * This reduces cpu overhead a bit, but means logrotate has to
+ * keep the file around for 60s after rotation and we can
+ * overshoot the size limit a bit.
+ */
+ if (sip->fd >= 0 && gethrtime() > sip->reopen_time) {
+ close(sip->fd);
+ sip->fd = -1;
}
- err = fstat(fd, &stat);
- if (err || stat.st_size > size_limit)
- goto out;
- write_formatted(fd, sip, buf, len);
-out:
- close(fd);
+
+ if (sip->fd < 0) {
+ sip->fd = open(name, O_WRONLY | O_APPEND | O_CREAT, 0644);
+ if (sip->fd < 0) {
+ syslog(LOG_ERR, "Unable to open \"%s\": %s", name, strerror(errno));
+ return;
+ }
+ err = fstat(sip->fd, &stat);
+ if (err) {
+ close(sip->fd);
+ sip->fd = -1;
+ return;
+ }
+ sip->filesize = stat.st_size;
+ sip->reopen_time = gethrtime() + 60000000000ULL; /* 60s */
+ }
+ if (sip->filesize > size_limit)
+ return;
+ write_formatted(sip, buf, len);
}
static int set_blocking(int blocking)