+ error = ovsdb_file_txn_commit(ftxn.json, ovsdb_txn_get_comment(txn),
+ durable, file->log);
+ if (error) {
+ return error;
+ }
+ file->n_transactions++;
+
+ /* If it has been at least COMPACT_MIN_MSEC ms since the last time we
+ * compacted (or at least COMPACT_RETRY_MSEC ms since the last time we
+ * tried), and if there are at least 100 transactions in the database, and
+ * if the database is at least 10 MB, then compact the database. */
+ if (time_msec() >= file->next_compact
+ && file->n_transactions >= 100
+ && ovsdb_log_get_offset(file->log) >= 10 * 1024 * 1024)
+ {
+ error = ovsdb_file_compact(file);
+ if (error) {
+ char *s = ovsdb_error_to_string(error);
+ ovsdb_error_destroy(error);
+ VLOG_WARN("%s: compacting database failed (%s), retrying in "
+ "%d seconds",
+ file->file_name, s, COMPACT_RETRY_MSEC / 1000);
+ free(s);
+
+ file->next_compact = time_msec() + COMPACT_RETRY_MSEC;
+ }
+ }
+
+ return NULL;
+}
+
+struct ovsdb_error *
+ovsdb_file_compact(struct ovsdb_file *file)
+{
+ struct ovsdb_log *new_log = NULL;
+ struct lockfile *tmp_lock = NULL;
+ struct ovsdb_error *error;
+ char *tmp_name = NULL;
+ char *comment = NULL;
+ int retval;
+
+ comment = xasprintf("compacting database online "
+ "(%.3f seconds old, %u transactions, %llu bytes)",
+ (time_wall_msec() - file->last_compact) / 1000.0,
+ file->n_transactions,
+ (unsigned long long) ovsdb_log_get_offset(file->log));
+ VLOG_INFO("%s: %s", file->file_name, comment);
+
+ /* Commit the old version, so that we can be assured that we'll eventually
+ * have either the old or the new version. */
+ error = ovsdb_log_commit(file->log);
+ if (error) {
+ goto exit;
+ }
+
+ /* Lock temporary file. */
+ tmp_name = xasprintf("%s.tmp", file->file_name);
+ retval = lockfile_lock(tmp_name, &tmp_lock);
+ if (retval) {
+ error = ovsdb_io_error(retval, "could not get lock on %s", tmp_name);
+ goto exit;
+ }
+
+ /* Remove temporary file. (It might not exist.) */
+ if (unlink(tmp_name) < 0 && errno != ENOENT) {
+ error = ovsdb_io_error(errno, "failed to remove %s", tmp_name);
+ goto exit;
+ }
+
+ /* Save a copy. */
+ error = ovsdb_file_save_copy__(tmp_name, false, comment, file->db,
+ &new_log);
+ if (error) {
+ goto exit;
+ }
+
+ /* Replace original by temporary. */
+ if (rename(tmp_name, file->file_name)) {
+ error = ovsdb_io_error(errno, "failed to rename \"%s\" to \"%s\"",
+ tmp_name, file->file_name);
+ goto exit;
+ }
+ fsync_parent_dir(file->file_name);
+
+exit:
+ if (!error) {
+ ovsdb_log_close(file->log);
+ file->log = new_log;
+ file->last_compact = time_msec();
+ file->next_compact = file->last_compact + COMPACT_MIN_MSEC;
+ file->n_transactions = 1;
+ } else {
+ ovsdb_log_close(new_log);
+ if (tmp_lock) {
+ unlink(tmp_name);
+ }
+ }
+
+ lockfile_unlock(tmp_lock);
+ free(tmp_name);
+ free(comment);
+
+ return error;