Merge branch 'master' into record_last_compaction
diff --git a/src/couch/src/couch_bt_engine.erl b/src/couch/src/couch_bt_engine.erl
index a42d116..c8eaa8f 100644
--- a/src/couch/src/couch_bt_engine.erl
+++ b/src/couch/src/couch_bt_engine.erl
@@ -31,6 +31,7 @@
     last_activity/1,
 
     get_compacted_seq/1,
+    get_last_compaction/1,
     get_del_doc_count/1,
     get_disk_version/1,
     get_doc_count/1,
@@ -199,6 +200,10 @@
     couch_bt_engine_header:get(Header, compacted_seq).
 
 
+get_last_compaction(#st{header = Header}) ->
+    couch_bt_engine_header:get(Header, last_compaction).
+
+
 get_del_doc_count(#st{} = St) ->
     {ok, Reds} = couch_btree:full_reduce(St#st.id_tree),
     element(2, Reds).
@@ -933,6 +938,7 @@
     {ok, NewSt2} = commit_data(NewSt1#st{
         header = couch_bt_engine_header:set(Header, [
             {compacted_seq, get_update_seq(OldSt)},
+            {last_compaction, now_secs()},
             {revs_limit, get_revs_limit(OldSt)}
         ]),
         local_tree = NewLocal2
@@ -961,3 +967,8 @@
     {ok, NewSt2#st{
         filepath = FilePath
     }, undefined}.
+
+
+now_secs() ->
+    {Mega, Sec, _Micro} = os:timestamp(),
+    Mega * 1000000 + Sec.
diff --git a/src/couch/src/couch_bt_engine_header.erl b/src/couch/src/couch_bt_engine_header.erl
index 3d24f31..464942b 100644
--- a/src/couch/src/couch_bt_engine_header.erl
+++ b/src/couch/src/couch_bt_engine_header.erl
@@ -37,7 +37,8 @@
     revs_limit/1,
     uuid/1,
     epochs/1,
-    compacted_seq/1
+    compacted_seq/1,
+    last_compaction/1
 ]).
 
 
@@ -66,7 +67,8 @@
     revs_limit = 1000,
     uuid,
     epochs,
-    compacted_seq
+    compacted_seq,
+    last_compaction
 }).
 
 
@@ -82,7 +84,8 @@
     #db_header{
         uuid = Header#db_header.uuid,
         epochs = Header#db_header.epochs,
-        compacted_seq = Header#db_header.compacted_seq
+        compacted_seq = Header#db_header.compacted_seq,
+        last_compaction = Header#db_header.last_compaction
     }.
 
 
@@ -178,6 +181,10 @@
     get_field(Header, compacted_seq).
 
 
+last_compaction(Header) ->
+    get_field(Header, last_compaction).
+
+
 get_field(Header, Field) ->
     get_field(Header, Field, undefined).
 
diff --git a/src/couch/src/couch_db.erl b/src/couch/src/couch_db.erl
index b47cc7e..61925d9 100644
--- a/src/couch/src/couch_db.erl
+++ b/src/couch/src/couch_db.erl
@@ -36,6 +36,7 @@
     get_before_doc_update_fun/1,
     get_committed_update_seq/1,
     get_compacted_seq/1,
+    get_last_compaction/1,
     get_compactor_pid/1,
     get_db_info/1,
     get_del_doc_count/1,
@@ -418,6 +419,9 @@
 get_compacted_seq(#db{}=Db) ->
     couch_db_engine:get_compacted_seq(Db).
 
+get_last_compaction(#db{}=Db) ->
+    couch_db_engine:get_last_compaction(Db).
+
 get_compactor_pid(#db{compactor_pid = Pid}) ->
     Pid.
 
@@ -443,6 +447,10 @@
         undefined -> null;
         Else1 -> Else1
     end,
+    LastCompaction = case get_last_compaction(Db) of
+        undefined -> null;
+        Else2 -> Else2
+    end,
     InfoList = [
         {db_name, Name},
         {engine, couch_db_engine:get_engine(Db)},
@@ -464,6 +472,7 @@
         {disk_format_version, DiskVersion},
         {committed_update_seq, CommittedUpdateSeq},
         {compacted_seq, CompactedSeq},
+        {last_compaction, LastCompaction},
         {uuid, Uuid}
     ],
     {ok, InfoList}.
diff --git a/src/couch/src/couch_db_engine.erl b/src/couch/src/couch_db_engine.erl
index 502faa7..2ea075a 100644
--- a/src/couch/src/couch_db_engine.erl
+++ b/src/couch/src/couch_db_engine.erl
@@ -181,6 +181,13 @@
             CompactedSeq::non_neg_integer().
 
 
+% The database should make a note of the time when it
+% was last compacted. If the database doesn't need compacting it
+% can just hard code a return value of 0.
+-callback get_last_compaction(DbHandle::db_handle()) ->
+            LastCompaction::non_neg_integer().
+
+
 % The number of documents in the database which have all leaf
 % revisions marked as deleted.
 -callback get_del_doc_count(DbHandle::db_handle()) ->
@@ -593,6 +600,7 @@
 
     get_engine/1,
     get_compacted_seq/1,
+    get_last_compaction/1,
     get_del_doc_count/1,
     get_disk_version/1,
     get_doc_count/1,
@@ -717,6 +725,11 @@
     Engine:get_compacted_seq(EngineState).
 
 
+get_last_compaction(#db{} = Db) ->
+    #db{engine = {Engine, EngineState}} = Db,
+    Engine:get_last_compaction(EngineState).
+
+
 get_del_doc_count(#db{} = Db) ->
     #db{engine = {Engine, EngineState}} = Db,
     Engine:get_del_doc_count(EngineState).
diff --git a/src/couch/src/test_engine_get_set_props.erl b/src/couch/src/test_engine_get_set_props.erl
index 6d2a447..37e1fd6 100644
--- a/src/couch/src/test_engine_get_set_props.erl
+++ b/src/couch/src/test_engine_get_set_props.erl
@@ -39,7 +39,8 @@
     ?assertEqual(1000, Engine:get_revs_limit(St)),
     ?assertMatch(<<_:32/binary>>, Engine:get_uuid(St)),
     ?assertEqual([{Node, 0}], Engine:get_epochs(St)),
-    ?assertEqual(0, Engine:get_compacted_seq(St)).
+    ?assertEqual(0, Engine:get_compacted_seq(St)),
+    ?assertEqual(undefined, Engine:get_last_compaction(St)).
 
 
 cet_set_security() ->
diff --git a/src/fabric/src/fabric.erl b/src/fabric/src/fabric.erl
index 4a07271..863b5fe 100644
--- a/src/fabric/src/fabric.erl
+++ b/src/fabric/src/fabric.erl
@@ -78,6 +78,7 @@
         {doc_del_count, non_neg_integer()} |
         {purge_seq, non_neg_integer()} |
         {compact_running, boolean()} |
+        {last_compaction, non_neg_integer()} |
         {disk_size, non_neg_integer()} |
         {disk_format_version, pos_integer()}
     ]}.
diff --git a/src/fabric/src/fabric_db_info.erl b/src/fabric/src/fabric_db_info.erl
index 98e8e52..4af97ee 100644
--- a/src/fabric/src/fabric_db_info.erl
+++ b/src/fabric/src/fabric_db_info.erl
@@ -95,6 +95,13 @@
             [{purge_seq, lists:sum(X)} | Acc];
         (compact_running, X, Acc) ->
             [{compact_running, lists:member(true, X)} | Acc];
+        (last_compaction, Times, Acc) ->
+            case lists:member(null, Times) of
+                true ->
+                    [{last_compaction, null} | Acc];
+                false ->
+                    [{last_compaction, lists:min(Times)} | Acc]
+            end;
         (disk_size, X, Acc) -> % legacy
             [{disk_size, lists:sum(X)} | Acc];
         (data_size, X, Acc) -> % legacy