1 /* A simple tool for benchmarking various logging strategies.
3 * We want to log a series of key/value pairs. Approaches that we try include:
4 * - Data written directly into the filesystem.
5 * - Data is written to a Berkeley DB.
6 * - Data is appended to a log file.
7 * In all cases we want to ensure that data is persistent on disk so it could
8 * be used for crash recovery. We measure how many log records we can write
9 * per second to gauge performance. */
12 #define _ATFILE_SOURCE
18 #include <sys/types.h>
32 const int queue_capacity = 1024;
33 const int item_size = 1024;
38 GCond *cond_empty, *cond_full;
40 struct item *get_item()
42 return (struct item *)g_async_queue_pop(queue);
45 void finish_item(struct item *item)
54 g_cond_signal(cond_empty);
55 if (outstanding < queue_capacity)
56 g_cond_signal(cond_full);
60 void writebuf(int fd, const char *buf, size_t len)
64 written = write(fd, buf, len);
65 if (written < 0 && errno == EINTR)
67 g_assert(written >= 0);
73 /************************ Direct-to-filesystem logging ***********************/
74 static int dirfd = -1;
76 gpointer fslog_thread(gpointer d)
78 g_print("Launching filesystem writer thread...\n");
81 struct item *item = get_item();
83 int fd = openat(dirfd, item->key, O_CREAT|O_WRONLY|O_TRUNC, 0666);
86 writebuf(fd, item->data, item->len);
100 dirfd = open("logdir", O_DIRECTORY);
101 g_assert(dirfd >= 0);
103 for (int i = 0; i < 1; i++)
104 g_thread_create(fslog_thread, NULL, FALSE, NULL);
107 /****************************** Single-File Log ******************************/
108 gpointer flatlog_thread(gpointer d)
110 g_print("Launching flat log writer thread...\n");
112 int fd = open("logfile", O_CREAT|O_WRONLY|O_TRUNC, 0666);
118 struct item *item = get_item();
120 writebuf(fd, item->key, strlen(item->key) + 1);
121 writebuf(fd, (char *)&item->len, sizeof(item->len));
122 writebuf(fd, item->data, item->len);
125 if (count % (1 << 8) == 0)
134 void launch_flatlog()
136 g_thread_create(flatlog_thread, NULL, FALSE, NULL);
139 /************************* Transactional Berkeley DB *************************/
140 gpointer bdb_thread(gpointer d)
142 g_print("Launching BDB log writer thread...\n");
150 res = db_env_create(&env, 0);
153 res = env->open(env, "bdb",
154 DB_CREATE | DB_RECOVER | DB_INIT_LOCK | DB_INIT_LOG
155 | DB_INIT_MPOOL | DB_INIT_TXN | DB_THREAD, 0644);
158 res = db_create(&db, env, 0);
161 res = db->open(db, NULL, "log.db", "log", DB_BTREE,
162 DB_CREATE | DB_THREAD | DB_AUTO_COMMIT, 0644);
167 res = env->txn_begin(env, NULL, &txn, 0);
171 struct item *item = get_item();
174 memset(&key, 0, sizeof(key));
175 memset(&value, 0, sizeof(value));
177 key.data = item->key;
178 key.size = strlen(item->key);
180 value.data = item->data;
181 value.size = item->len;
183 res = db->put(db, NULL, &key, &value, 0);
187 if (count % (1 << 8) == 0) {
200 g_thread_create(bdb_thread, NULL, FALSE, NULL);
203 int main(int argc, char *argv[])
206 queue = g_async_queue_new();
207 lock = g_mutex_new();
208 cond_empty = g_cond_new();
209 cond_full = g_cond_new();
215 for (int i = 0; i < (1 << 12); i++) {
216 struct item *item = g_new(struct item, 1);
217 item->key = g_strdup_printf("item-%06d", i);
218 item->data = g_malloc(item_size);
219 item->len = item_size;
222 while (outstanding >= queue_capacity)
223 g_cond_wait(cond_full, lock);
224 g_async_queue_push(queue, item);
226 g_mutex_unlock(lock);
230 while (outstanding > 0)
231 g_cond_wait(cond_empty, lock);
232 g_mutex_unlock(lock);