Remove uuid and epochs from db records The db record has become used too widely to make for an easy upgrade. Rather than attempt and fix all the places its used we'll just remove the new fields and store them in the header which isn't used nearly as often.
To get the values there are two new API functions `couch_db:get_uuid/1` and `couch_db:get_epochs/1` that take a db record as the single argument. There are also a couple slight behavior changes with this patch. We now make sure that every db opened has initialized values for the uuid and epochs. We also transmit those values during compaction so that every header in a compacted file will have the correct values as well. Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch/commit/e538ff7f Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/e538ff7f Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/e538ff7f Branch: refs/heads/windsor-merge-209 Commit: e538ff7f9f9f41ba68159d83adae919ea7941c66 Parents: 9138d07 Author: Paul J. Davis <[email protected]> Authored: Thu Jun 13 15:29:59 2013 -0500 Committer: Robert Newson <[email protected]> Committed: Tue Aug 5 15:21:40 2014 +0100 ---------------------------------------------------------------------- include/couch_db.hrl | 4 +-- src/couch_db.erl | 14 ++++++++-- src/couch_db_updater.erl | 62 ++++++++++++++++++++++++++++--------------- 3 files changed, 54 insertions(+), 26 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e538ff7f/include/couch_db.hrl ---------------------------------------------------------------------- diff --git a/include/couch_db.hrl b/include/couch_db.hrl index 5c549ec..bbf74c3 100644 --- a/include/couch_db.hrl +++ b/include/couch_db.hrl @@ -180,9 +180,7 @@ options = [], compression, before_doc_update = nil, % nil | fun(Doc, Db) -> NewDoc - after_doc_read = nil, % nil | fun(Doc, Db) -> NewDoc - uuid = nil, - epochs = nil + after_doc_read = nil % nil | fun(Doc, Db) -> NewDoc }). -record(view_fold_helper_funs, { http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e538ff7f/src/couch_db.erl ---------------------------------------------------------------------- diff --git a/src/couch_db.erl b/src/couch_db.erl index aaab19b..ab5e3da 100644 --- a/src/couch_db.erl +++ b/src/couch_db.erl @@ -21,6 +21,7 @@ -export([open_doc/2,open_doc/3,open_doc_revs/4]). -export([set_revs_limit/2,get_revs_limit/1]). -export([get_missing_revs/2,name/1,get_update_seq/1,get_committed_update_seq/1]). +-export([get_uuid/1, get_epochs/1]). -export([enum_docs/4,enum_docs_since/5]). -export([enum_docs_since_reduce_to_count/1,enum_docs_reduce_to_count/1]). -export([increment_update_seq/1,get_purge_seq/1,purge_docs/2,get_last_purged/1]). @@ -294,6 +295,12 @@ get_doc_count(Db) -> {ok, {Count, _, _}} = couch_btree:full_reduce(Db#db.id_tree), {ok, Count}. +get_uuid(#db{header=Header}) -> + Header#header.uuid. + +get_epochs(#db{header=Header}) -> + Header#header.epochs. + get_db_info(Db) -> #db{fd=Fd, header=#db_header{disk_version=DiskVersion}, @@ -304,11 +311,14 @@ get_db_info(Db) -> committed_update_seq=CommittedUpdateSeq, id_tree = IdBtree, seq_tree = SeqBtree, - local_tree = LocalBtree, - uuid = Uuid + local_tree = LocalBtree } = Db, {ok, Size} = couch_file:bytes(Fd), {ok, DbReduction} = couch_btree:full_reduce(IdBtree), + Uuid = case get_uuid(Db) of + undefined -> null; + Uuid0 -> Uuid0 + end, InfoList = [ {db_name, Name}, {doc_count, element(1, DbReduction)}, http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/e538ff7f/src/couch_db_updater.erl ---------------------------------------------------------------------- diff --git a/src/couch_db_updater.erl b/src/couch_db_updater.erl index f6276c5..eadd6c7 100644 --- a/src/couch_db_updater.erl +++ b/src/couch_db_updater.erl @@ -227,9 +227,7 @@ handle_cast({compact_done, CompactFilepath}, #db{filepath=Filepath}=Db) -> main_pid = self(), filepath = Filepath, instance_start_time = Db#db.instance_start_time, - revs_limit = Db#db.revs_limit, - uuid = Db#db.uuid, - epochs = Db#db.epochs + revs_limit = Db#db.revs_limit }), ?LOG_DEBUG("CouchDB swapping files ~s and ~s.", @@ -459,7 +457,7 @@ simple_upgrade_record(Old, _New) -> init_db(DbName, Filepath, Fd, Header0, Options) -> Header1 = simple_upgrade_record(Header0, #db_header{}), - Header = + Header2 = case element(2, Header1) of 1 -> throw({database_disk_version_error, ?OLD_DISK_VERSION_ERROR}); 2 -> throw({database_disk_version_error, ?OLD_DISK_VERSION_ERROR}); @@ -470,6 +468,11 @@ init_db(DbName, Filepath, Fd, Header0, Options) -> _ -> throw({database_disk_version_error, "Incorrect disk header version"}) end, + Header = Header2#db_header{ + uuid = initialize_uuid(Header2), + epochs = initialize_epochs(Header2) + }, + {ok, FsyncOptions} = couch_util:parse_term( config:get("couchdb", "fsync_options", "[before_header, after_header, on_file_open]")), @@ -505,7 +508,7 @@ init_db(DbName, Filepath, Fd, Header0, Options) -> StartTime = ?l2b(io_lib:format("~p", [(MegaSecs*1000000*1000000) + (Secs*1000000) + MicroSecs])), ok = couch_file:set_db_pid(Fd, self()), - #db{ + Db = #db{ fd=Fd, fd_monitor = erlang:monitor(process, Fd), header=Header, @@ -524,10 +527,20 @@ init_db(DbName, Filepath, Fd, Header0, Options) -> options = Options, compression = Compression, before_doc_update = couch_util:get_value(before_doc_update, Options, nil), - after_doc_read = couch_util:get_value(after_doc_read, Options, nil), - uuid = initialize_uuid(Header), - epochs = initialize_epochs(Header) - }. + after_doc_read = couch_util:get_value(after_doc_read, Options, nil) + }, + + % If we just created a new UUID while upgrading a + % database then we want to flush that to disk or + % we risk sending out the uuid and having the db + % crash which would result in it generating a new + % uuid each time it was reopened. + case Header /= Header2 of + true -> + sync_header(Db, Header); + false -> + Db + end. close_db(#db{fd_monitor = Ref}) -> @@ -808,9 +821,7 @@ db_to_header(Db, Header) -> id_tree_state = couch_btree:get_state(Db#db.id_tree), local_tree_state = couch_btree:get_state(Db#db.local_tree), security_ptr = Db#db.security_ptr, - revs_limit = Db#db.revs_limit, - uuid = Db#db.uuid, - epochs = update_epochs(Db)}. + revs_limit = Db#db.revs_limit}. commit_data(Db) -> commit_data(Db, false). @@ -831,7 +842,7 @@ commit_data(Db, _) -> NewHeader -> sync_header(Db, NewHeader) end. -sync_header(Db, NewHeader) -> +sync_header(Db, NewHeader0) -> #db{ fd = Fd, filepath = FilePath, @@ -839,6 +850,8 @@ sync_header(Db, NewHeader) -> waiting_delayed_commit = Timer } = Db, + NewHeader = update_epochs(NewHeader0), + if is_reference(Timer) -> erlang:cancel_timer(Timer); true -> ok end, Before = lists:member(before_header, FsyncOptions), @@ -1034,11 +1047,11 @@ copy_compact(Db, NewDb0, Retry) -> start_copy_compact(#db{}=Db) -> - #db{name=Name, filepath=Filepath, options=Options} = Db, + #db{name=Name, filepath=Filepath, options=Options, header=Header} = Db, ?LOG_DEBUG("Compaction process spawned for db \"~s\"", [Name]), {ok, NewDb, DName, DFd, MFd, Retry} = - open_compaction_files(Name, Filepath, Options), + open_compaction_files(Name, Header, Filepath, Options), erlang:monitor(process, MFd), % This is a bit worrisome. init_db/4 will monitor the data fd @@ -1059,7 +1072,7 @@ start_copy_compact(#db{}=Db) -> gen_server:cast(Db#db.main_pid, {compact_done, DName}). -open_compaction_files(DbName, DbFilePath, Options) -> +open_compaction_files(DbName, Header, DbFilePath, Options) -> DataFile = DbFilePath ++ ".compact.data", MetaFile = DbFilePath ++ ".compact.meta", {ok, DataFd, DataHdr} = open_compaction_file(DataFile), @@ -1071,15 +1084,22 @@ open_compaction_files(DbName, DbFilePath, Options) -> Db1 = bind_emsort(Db0, MetaFd, A#comp_header.meta_state), {ok, Db1, DataFile, DataFd, MetaFd, Db0#db.id_tree}; {#db_header{}, _} -> - ok = reset_compaction_file(MetaFd, #db_header{}), + NewHeader = #db_header{ + uuid = DataHdr#db_header.uuid, + epochs = DataHdr#db_header.epochs + }, + ok = reset_compaction_file(MetaFd, NewHeader), Db0 = init_db(DbName, DataFile, DataFd, DataHdr, Options), Db1 = bind_emsort(Db0, MetaFd, nil), {ok, Db1, DataFile, DataFd, MetaFd, Db0#db.id_tree}; _ -> - Header = #db_header{}, - ok = reset_compaction_file(DataFd, Header), - ok = reset_compaction_file(MetaFd, Header), - Db0 = init_db(DbName, DataFile, DataFd, Header, Options), + NewHeader = #db_header{ + uuid = Header#db_header.uuid, + epochs = Header#db_header.epochs + }, + ok = reset_compaction_file(DataFd, NewHeader), + ok = reset_compaction_file(MetaFd, NewHeader), + Db0 = init_db(DbName, DataFile, DataFd, NewHeader, Options), Db1 = bind_emsort(Db0, MetaFd, nil), {ok, Db1, DataFile, DataFd, MetaFd, nil} end.
