summaryrefslogtreecommitdiff
path: root/src/couchdb
diff options
context:
space:
mode:
Diffstat (limited to 'src/couchdb')
-rw-r--r--src/couchdb/couch_db.erl12
-rw-r--r--src/couchdb/couch_db.hrl3
-rw-r--r--src/couchdb/couch_db_updater.erl12
-rw-r--r--src/couchdb/couch_httpd_db.erl29
-rw-r--r--src/couchdb/couch_task_status.erl2
-rw-r--r--src/couchdb/couch_view.erl160
-rw-r--r--src/couchdb/couch_view_group.erl142
-rw-r--r--src/couchdb/couch_view_updater.erl20
8 files changed, 225 insertions, 155 deletions
diff --git a/src/couchdb/couch_db.erl b/src/couchdb/couch_db.erl
index 979bd39d..2f0fa847 100644
--- a/src/couchdb/couch_db.erl
+++ b/src/couchdb/couch_db.erl
@@ -13,7 +13,7 @@
-module(couch_db).
-behaviour(gen_server).
--export([open/2,close/1,create/2,start_compact/1,get_db_info/1]).
+-export([open/2,close/1,create/2,start_compact/1,get_db_info/1,get_design_docs/1]).
-export([open_ref_counted/2,is_idle/1,monitor/1,count_changes_since/2]).
-export([update_doc/3,update_docs/4,update_docs/2,update_docs/3,delete_doc/3]).
-export([get_doc_info/2,open_doc/2,open_doc/3,open_doc_revs/4]).
@@ -192,6 +192,16 @@ get_db_info(Db) ->
],
{ok, InfoList}.
+get_design_docs(#db{fulldocinfo_by_id_btree=Btree}=Db) ->
+ couch_btree:foldl(Btree, <<"_design/">>,
+ fun(#full_doc_info{id= <<"_design/",_/binary>>}=FullDocInfo, _Reds, AccDocs) ->
+ {ok, Doc} = couch_db:open_doc_int(Db, FullDocInfo, []),
+ {ok, [Doc | AccDocs]};
+ (_, _Reds, AccDocs) ->
+ {stop, AccDocs}
+ end,
+ []).
+
check_is_admin(#db{admins=Admins, user_ctx=#user_ctx{name=Name,roles=Roles}}) ->
DbAdmins = [<<"_admin">> | Admins],
case DbAdmins -- [Name | Roles] of
diff --git a/src/couchdb/couch_db.hrl b/src/couchdb/couch_db.hrl
index 580b91cb..905b489b 100644
--- a/src/couchdb/couch_db.hrl
+++ b/src/couchdb/couch_db.hrl
@@ -190,8 +190,7 @@
headers = []
}).
--record(group,
- {type=view, % can also be temp_view
+-record(group, {
sig=nil,
db=nil,
fd=nil,
diff --git a/src/couchdb/couch_db_updater.erl b/src/couchdb/couch_db_updater.erl
index 31ddbf8c..6fef29eb 100644
--- a/src/couchdb/couch_db_updater.erl
+++ b/src/couchdb/couch_db_updater.erl
@@ -350,7 +350,7 @@ close_db(#db{fd_ref_counter = RefCntr}) ->
refresh_validate_doc_funs(Db) ->
- {ok, DesignDocs} = get_design_docs(Db),
+ {ok, DesignDocs} = couch_db:get_design_docs(Db),
ProcessDocFuns = lists:flatmap(
fun(DesignDoc) ->
case couch_doc:get_validate_doc_fun(DesignDoc) of
@@ -360,16 +360,6 @@ refresh_validate_doc_funs(Db) ->
end, DesignDocs),
Db#db{validate_doc_funs=ProcessDocFuns}.
-get_design_docs(#db{fulldocinfo_by_id_btree=Btree}=Db) ->
- couch_btree:foldl(Btree, <<"_design/">>,
- fun(#full_doc_info{id= <<"_design/",_/binary>>}=FullDocInfo, _Reds, AccDocs) ->
- {ok, Doc} = couch_db:open_doc_int(Db, FullDocInfo, []),
- {ok, [Doc | AccDocs]};
- (_, _Reds, AccDocs) ->
- {stop, AccDocs}
- end,
- []).
-
% rev tree functions
flush_trees(_Db, [], AccFlushedTrees) ->
diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl
index 8c692f8d..edb2f310 100644
--- a/src/couchdb/couch_httpd_db.erl
+++ b/src/couchdb/couch_httpd_db.erl
@@ -15,7 +15,8 @@
-export([handle_request/1, handle_compact_req/2, handle_design_req/2,
db_req/2, couch_doc_open/4,handle_changes_req/2,
- update_doc_result_to_json/1, update_doc_result_to_json/2]).
+ update_doc_result_to_json/1, update_doc_result_to_json/2,
+ handle_design_info_req/2, handle_view_cleanup_req/2]).
-import(couch_httpd,
[send_json/2,send_json/3,send_json/4,send_method_not_allowed/2,
@@ -156,6 +157,15 @@ handle_compact_req(#httpd{method='POST'}=Req, Db) ->
handle_compact_req(Req, _Db) ->
send_method_not_allowed(Req, "POST").
+handle_view_cleanup_req(#httpd{method='POST'}=Req, Db) ->
+ % delete unreferenced index files
+ ok = couch_view:cleanup_index_files(Db),
+ send_json(Req, 202, {[{ok, true}]});
+
+handle_view_cleanup_req(Req, _Db) ->
+ send_method_not_allowed(Req, "POST").
+
+
handle_design_req(#httpd{
path_parts=[_DbName,_Design,_DesName, <<"_",_/binary>> = Action | _Rest],
design_url_handlers = DesignUrlHandlers
@@ -166,6 +176,23 @@ handle_design_req(#httpd{
handle_design_req(Req, Db) ->
db_req(Req, Db).
+handle_design_info_req(#httpd{
+ method='GET',
+ path_parts=[_DbName, _Design, DesignName, _]
+ }=Req, Db) ->
+ DesignId = <<"_design/", DesignName/binary>>,
+ ?LOG_ERROR("DesignId ~p",[DesignId]),
+ {ok, GroupInfoList} = couch_view:get_group_info(Db, DesignId),
+ ?LOG_ERROR("GroupInfoList ~p",[GroupInfoList]),
+ send_json(Req, 200, {[
+ {name, DesignName},
+ {view_index, {GroupInfoList}}
+ ]});
+
+handle_design_info_req(Req, _Db) ->
+ send_method_not_allowed(Req, "GET").
+
+
create_db_req(#httpd{user_ctx=UserCtx}=Req, DbName) ->
ok = couch_httpd:verify_is_server_admin(Req),
case couch_server:create(DbName, [{user_ctx, UserCtx}]) of
diff --git a/src/couchdb/couch_task_status.erl b/src/couchdb/couch_task_status.erl
index ff2c7bee..ee7bdff5 100644
--- a/src/couchdb/couch_task_status.erl
+++ b/src/couchdb/couch_task_status.erl
@@ -107,12 +107,12 @@ handle_call(all, _, Server) ->
handle_cast({update_status, Pid, StatusText}, Server) ->
[{Pid, {Type, TaskName, _StatusText}}] = ets:lookup(?MODULE, Pid),
+ ?LOG_DEBUG("New task status for ~s: ~s",[TaskName, StatusText]),
true = ets:insert(?MODULE, {Pid, {Type, TaskName, StatusText}}),
{noreply, Server};
handle_cast(stop, State) ->
{stop, normal, State}.
-
handle_info({'DOWN', _MonitorRef, _Type, Pid, _Info}, Server) ->
%% should we also erlang:demonitor(_MonitorRef), ?
ets:delete(?MODULE, Pid),
diff --git a/src/couchdb/couch_view.erl b/src/couchdb/couch_view.erl
index 301ea8b7..87feea12 100644
--- a/src/couchdb/couch_view.erl
+++ b/src/couchdb/couch_view.erl
@@ -17,7 +17,7 @@
detuple_kvs/2,init/1,terminate/2,handle_call/3,handle_cast/2,handle_info/2,
code_change/3,get_reduce_view/4,get_temp_reduce_view/5,get_temp_map_view/4,
get_map_view/4,get_row_count/1,reduce_to_count/1,fold_reduce/7,
- extract_map_view/1,get_group_server/2]).
+ extract_map_view/1,get_group_server/2,get_group_info/2,cleanup_index_files/1]).
-include("couch_db.hrl").
@@ -28,18 +28,32 @@
start_link() ->
gen_server:start_link({local, couch_view}, couch_view, [], []).
-get_temp_updater(DbName, Type, DesignOptions, MapSrc, RedSrc) ->
- {ok, Pid} = gen_server:call(couch_view,
- {start_temp_updater, DbName, Type, DesignOptions, MapSrc, RedSrc}),
- Pid.
-
-get_group_server(DbName, GroupId) ->
- case gen_server:call(couch_view, {start_group_server, DbName, GroupId}) of
+get_temp_updater(DbName, Language, DesignOptions, MapSrc, RedSrc) ->
+ % make temp group
+ % do we need to close this db?
+ {ok, _Db, Group} =
+ couch_view_group:open_temp_group(DbName, Language, DesignOptions, MapSrc, RedSrc),
+ case gen_server:call(couch_view, {get_group_server, DbName, Group}) of
{ok, Pid} ->
Pid;
Error ->
throw(Error)
end.
+
+get_group_server(DbName, GroupId) ->
+ % get signature for group
+ case couch_view_group:open_db_group(DbName, GroupId) of
+ % do we need to close this db?
+ {ok, _Db, Group} ->
+ case gen_server:call(couch_view, {get_group_server, DbName, Group}) of
+ {ok, Pid} ->
+ Pid;
+ Error ->
+ throw(Error)
+ end;
+ Error ->
+ throw(Error)
+ end.
get_group(Db, GroupId, Stale) ->
MinUpdateSeq = case Stale of
@@ -50,18 +64,52 @@ get_group(Db, GroupId, Stale) ->
get_group_server(couch_db:name(Db), GroupId),
MinUpdateSeq).
-
-get_temp_group(Db, Type, DesignOptions, MapSrc, RedSrc) ->
+get_temp_group(Db, Language, DesignOptions, MapSrc, RedSrc) ->
couch_view_group:request_group(
- get_temp_updater(couch_db:name(Db), Type, DesignOptions, MapSrc, RedSrc),
- couch_db:get_update_seq(Db)).
+ get_temp_updater(couch_db:name(Db), Language, DesignOptions, MapSrc, RedSrc),
+ couch_db:get_update_seq(Db)).
+
+get_group_info(Db, GroupId) ->
+ couch_view_group:request_group_info(
+ get_group_server(couch_db:name(Db), GroupId)).
+
+cleanup_index_files(Db) ->
+ % load all ddocs
+ {ok, DesignDocs} = couch_db:get_design_docs(Db),
+
+ % make unique list of group sigs
+ Sigs = lists:map(fun(#doc{id = GroupId} = DDoc) ->
+ {ok, Info} = get_group_info(Db, GroupId),
+ ?b2l(proplists:get_value(signature, Info))
+ end, [DD||DD <- DesignDocs, DD#doc.deleted == false]),
+
+ FileList = list_index_files(Db),
+
+ % regex that matches all ddocs
+ RegExp = "("++ string:join(Sigs, "|") ++")",
+
+ % filter out the ones in use
+ DeleteFiles = lists:filter(fun(FilePath) ->
+ regexp:first_match(FilePath, RegExp)==nomatch
+ end, FileList),
+ % delete unused files
+ ?LOG_DEBUG("deleting unused view index files: ~p",[DeleteFiles]),
+ [file:delete(File)||File <- DeleteFiles],
+ ok.
+
+list_index_files(Db) ->
+ % call server to fetch the index files
+ RootDir = couch_config:get("couchdb", "view_index_dir"),
+ Files = filelib:wildcard(RootDir ++ "/." ++ ?b2l(couch_db:name(Db)) ++ "_design"++"/*").
+
get_row_count(#view{btree=Bt}) ->
{ok, {Count, _Reds}} = couch_btree:full_reduce(Bt),
{ok, Count}.
-get_temp_reduce_view(Db, Type, DesignOptions, MapSrc, RedSrc) ->
- {ok, #group{views=[View]}=Group} = get_temp_group(Db, Type, DesignOptions, MapSrc, RedSrc),
+get_temp_reduce_view(Db, Language, DesignOptions, MapSrc, RedSrc) ->
+ {ok, #group{views=[View]}=Group} =
+ get_temp_group(Db, Language, DesignOptions, MapSrc, RedSrc),
{ok, {temp_reduce, View}, Group}.
@@ -141,8 +189,8 @@ get_key_pos(Key, [_|Rest], N) ->
get_key_pos(Key, Rest, N+1).
-get_temp_map_view(Db, Type, DesignOptions, Src) ->
- {ok, #group{views=[View]}=Group} = get_temp_group(Db, Type, DesignOptions, Src, []),
+get_temp_map_view(Db, Language, DesignOptions, Src) ->
+ {ok, #group{views=[View]}=Group} = get_temp_group(Db, Language, DesignOptions, Src, []),
{ok, View, Group}.
get_map_view(Db, GroupId, Name, Stale) ->
@@ -220,9 +268,8 @@ init([]) ->
ok
end),
ets:new(couch_groups_by_db, [bag, private, named_table]),
- ets:new(group_servers_by_name, [set, protected, named_table]),
+ ets:new(group_servers_by_sig, [set, protected, named_table]),
ets:new(couch_groups_by_updater, [set, private, named_table]),
- ets:new(couch_temp_group_fd_by_db, [set, protected, named_table]),
process_flag(trap_exit, true),
{ok, #server{root_dir=RootDir}}.
@@ -232,37 +279,15 @@ terminate(Reason, _Srv) ->
ok.
-handle_call({start_temp_updater, DbName, Lang, DesignOptions, MapSrc, RedSrc},
- _From, #server{root_dir=Root}=Server) ->
- <<SigInt:128/integer>> = erlang:md5(term_to_binary({Lang, DesignOptions, MapSrc, RedSrc})),
- Name = lists:flatten(io_lib:format("_temp_~.36B",[SigInt])),
- Pid =
- case ets:lookup(group_servers_by_name, {DbName, Name}) of
+handle_call({get_group_server, DbName,
+ #group{name=GroupId,sig=Sig}=Group}, _From, #server{root_dir=Root}=Server) ->
+ case ets:lookup(group_servers_by_sig, {DbName, Sig}) of
[] ->
- case ets:lookup(couch_temp_group_fd_by_db, DbName) of
- [] ->
- FileName = Root ++ "/." ++ binary_to_list(DbName) ++ "_temp",
- {ok, Fd} = couch_file:open(FileName, [create, overwrite]),
- Count = 0;
- [{_, Fd, Count}] ->
- ok
- end,
- ?LOG_DEBUG("Spawning new temp update process for db ~s.", [DbName]),
- {ok, NewPid} = couch_view_group:start_link({slow_view, DbName, Fd, Lang, DesignOptions, MapSrc, RedSrc}),
- true = ets:insert(couch_temp_group_fd_by_db, {DbName, Fd, Count + 1}),
- add_to_ets(NewPid, DbName, Name),
- NewPid;
- [{_, ExistingPid0}] ->
- ExistingPid0
- end,
- {reply, {ok, Pid}, Server};
-handle_call({start_group_server, DbName, GroupId}, _From, #server{root_dir=Root}=Server) ->
- case ets:lookup(group_servers_by_name, {DbName, GroupId}) of
- [] ->
- ?LOG_DEBUG("Spawning new group server for view group ~s in database ~s.", [GroupId, DbName]),
- case (catch couch_view_group:start_link({view, Root, DbName, GroupId})) of
+ ?LOG_DEBUG("Spawning new group server for view group ~s in database ~s.",
+ [GroupId, DbName]),
+ case (catch couch_view_group:start_link({Root, DbName, Group})) of
{ok, NewPid} ->
- add_to_ets(NewPid, DbName, GroupId),
+ add_to_ets(NewPid, DbName, Sig),
{reply, {ok, NewPid}, Server};
Error ->
{reply, Error, Server}
@@ -272,22 +297,22 @@ handle_call({start_group_server, DbName, GroupId}, _From, #server{root_dir=Root}
end.
handle_cast({reset_indexes, DbName}, #server{root_dir=Root}=Server) ->
- % shutdown all the updaters
+ % shutdown all the updaters and clear the files, the db got changed
Names = ets:lookup(couch_groups_by_db, DbName),
lists:foreach(
- fun({_DbName, GroupId}) ->
- ?LOG_DEBUG("Killing update process for view group ~s. in database ~s.", [GroupId, DbName]),
- [{_, Pid}] = ets:lookup(group_servers_by_name, {DbName, GroupId}),
+ fun({_DbName, Sig}) ->
+ ?LOG_DEBUG("Killing update process for view group ~s. in database ~s.", [Sig, DbName]),
+ [{_, Pid}] = ets:lookup(group_servers_by_sig, {DbName, Sig}),
exit(Pid, kill),
receive {'EXIT', Pid, _} ->
- delete_from_ets(Pid, DbName, GroupId)
+ delete_from_ets(Pid, DbName, Sig)
end
end, Names),
delete_index_dir(Root, DbName),
file:delete(Root ++ "/." ++ binary_to_list(DbName) ++ "_temp"),
{noreply, Server}.
-handle_info({'EXIT', FromPid, Reason}, #server{root_dir=RootDir}=Server) ->
+handle_info({'EXIT', FromPid, Reason}, Server) ->
case ets:lookup(couch_groups_by_updater, FromPid) of
[] ->
if Reason /= normal ->
@@ -296,40 +321,27 @@ handle_info({'EXIT', FromPid, Reason}, #server{root_dir=RootDir}=Server) ->
exit(Reason);
true -> ok
end;
- [{_, {DbName, "_temp_" ++ _ = GroupId}}] ->
- delete_from_ets(FromPid, DbName, GroupId),
- [{_, Fd, Count}] = ets:lookup(couch_temp_group_fd_by_db, DbName),
- case Count of
- 1 -> % Last ref
- couch_file:close(Fd),
- file:delete(RootDir ++ "/." ++ binary_to_list(DbName) ++ "_temp"),
- true = ets:delete(couch_temp_group_fd_by_db, DbName);
- _ ->
- true = ets:insert(couch_temp_group_fd_by_db, {DbName, Fd, Count - 1})
- end;
[{_, {DbName, GroupId}}] ->
delete_from_ets(FromPid, DbName, GroupId)
end,
{noreply, Server}.
-add_to_ets(Pid, DbName, GroupId) ->
- true = ets:insert(couch_groups_by_updater, {Pid, {DbName, GroupId}}),
- true = ets:insert(group_servers_by_name, {{DbName, GroupId}, Pid}),
- true = ets:insert(couch_groups_by_db, {DbName, GroupId}).
+add_to_ets(Pid, DbName, Sig) ->
+ true = ets:insert(couch_groups_by_updater, {Pid, {DbName, Sig}}),
+ true = ets:insert(group_servers_by_sig, {{DbName, Sig}, Pid}),
+ true = ets:insert(couch_groups_by_db, {DbName, Sig}).
-delete_from_ets(Pid, DbName, GroupId) ->
+delete_from_ets(Pid, DbName, Sig) ->
true = ets:delete(couch_groups_by_updater, Pid),
- true = ets:delete(group_servers_by_name, {DbName, GroupId}),
- true = ets:delete_object(couch_groups_by_db, {DbName, GroupId}).
+ true = ets:delete(group_servers_by_sig, {DbName, Sig}),
+ true = ets:delete_object(couch_groups_by_db, {DbName, Sig}).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
-
-
delete_index_dir(RootDir, DbName) ->
- nuke_dir(RootDir ++ "/." ++ binary_to_list(DbName) ++ "_design").
+ nuke_dir(RootDir ++ "/." ++ ?b2l(DbName) ++ "_design").
nuke_dir(Dir) ->
case file:list_dir(Dir) of
diff --git a/src/couchdb/couch_view_group.erl b/src/couchdb/couch_view_group.erl
index 0ab1077e..0b390b22 100644
--- a/src/couchdb/couch_view_group.erl
+++ b/src/couchdb/couch_view_group.erl
@@ -14,8 +14,8 @@
-behaviour(gen_server).
%% API
--export([start_link/1, request_group/2]).
--export([design_doc_to_view_group/1]).
+-export([start_link/1, request_group/2, request_group_info/1]).
+-export([open_db_group/2, open_temp_group/5, design_doc_to_view_group/1,design_root/2]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -47,6 +47,22 @@ request_group(Pid, Seq) ->
throw(Error)
end.
+request_group_info(Pid) ->
+ case gen_server:call(Pid, request_group_info) of
+ {ok, GroupInfoList} ->
+ {ok, GroupInfoList};
+ Error ->
+ throw(Error)
+ end.
+
+request_index_files(Pid) ->
+ case gen_server:call(Pid, request_index_files) of
+ {ok, Filelist} ->
+ {ok, Filelist};
+ Error ->
+ throw(Error)
+ end.
+
% from template
start_link(InitArgs) ->
@@ -67,9 +83,7 @@ start_link(InitArgs) ->
Error
end.
-% init differentiates between temp and design_doc views. It creates a closure
-% which spawns the appropriate view_updater. (It might also spawn the first
-% view_updater run.)
+% init creates a closure which spawns the appropriate view_updater.
init({InitArgs, ReturnPid, Ref}) ->
process_flag(trap_exit, true),
case prepare_group(InitArgs, false) of
@@ -95,8 +109,7 @@ init({InitArgs, ReturnPid, Ref}) ->
% view group, and the couch_view_updater, which when spawned, updates the
% group and sends it back here. We employ a caching mechanism, so that between
% database writes, we don't have to spawn a couch_view_updater with every view
-% request. This should give us more control, and the ability to request view
-% statuses eventually.
+% request.
% The caching mechanism: each request is submitted with a seq_id for the
% database at the time it was read. We guarantee to return a view from that
@@ -137,8 +150,14 @@ handle_call({request_group, RequestSeq}, From,
#group_state{waiting_list=WaitList}=State) ->
{noreply, State#group_state{
waiting_list=[{From, RequestSeq}|WaitList]
- }, infinity}.
+ }, infinity};
+handle_call(request_group_info, _From, #group_state{
+ group = Group,
+ compactor_pid = CompactorPid
+ } = State) ->
+ GroupInfo = get_group_info(Group, CompactorPid),
+ {reply, {ok, GroupInfo}, State}.
handle_cast({start_compact, CompactFun}, #group_state{ compactor_pid=nil,
group=Group, init_args={view, RootDir, DbName, GroupId} } = State) ->
@@ -154,15 +173,14 @@ handle_cast({start_compact, _}, State) ->
handle_cast({compact_done, #group{fd=NewFd, current_seq=NewSeq} = NewGroup},
#group_state{
- group = #group{current_seq=OldSeq} = Group,
- init_args = {view, RootDir, DbName, GroupId},
+ group = #group{current_seq=OldSeq, sig=GroupSig} = Group,
+ init_args = {view, RootDir, DbName, _GroupId},
updater_pid = nil,
ref_counter = RefCounter
} = State) when NewSeq >= OldSeq ->
?LOG_INFO("View Group compaction complete", []),
- BaseName = RootDir ++ "/." ++ ?b2l(DbName) ++ ?b2l(GroupId),
- FileName = BaseName ++ ".view",
- CompactName = BaseName ++".compact.view",
+ FileName = index_file_name(RootDir, DbName, GroupSig),
+ CompactName = index_file_name(compact, RootDir, DbName, GroupSig),
file:delete(FileName),
ok = file:rename(CompactName, FileName),
@@ -224,8 +242,7 @@ handle_info({'EXIT', FromPid, {new_group, #group{db=Db}=Group}},
waiting_list=WaitList,
waiting_commit=WaitingCommit}=State) when UpPid == FromPid ->
ok = couch_db:close(Db),
-
- if Group#group.type == view andalso not WaitingCommit ->
+ if not WaitingCommit ->
erlang:send_after(1000, self(), delayed_commit);
true -> ok
end,
@@ -306,12 +323,13 @@ reply_all(#group_state{waiting_list=WaitList}=State, Reply) ->
[catch gen_server:reply(Pid, Reply) || {Pid, _} <- WaitList],
State#group_state{waiting_list=[]}.
-prepare_group({view, RootDir, DbName, GroupId}, ForceReset)->
- case open_db_group(DbName, GroupId) of
- {ok, Db, #group{sig=Sig}=Group} ->
- case open_index_file(RootDir, DbName, GroupId) of
+prepare_group({RootDir, DbName, #group{sig=Sig}=Group}, ForceReset)->
+ case couch_db:open(DbName, []) of
+ {ok, Db} ->
+ case open_index_file(RootDir, DbName, Sig) of
{ok, Fd} ->
if ForceReset ->
+ % this can happen if we missed a purge
{ok, reset_file(Db, Fd, DbName, Group)};
true ->
% 09 UPGRADE CODE
@@ -321,32 +339,18 @@ prepare_group({view, RootDir, DbName, GroupId}, ForceReset)->
% sigs match!
{ok, init_group(Db, Fd, Group, HeaderInfo)};
_ ->
+ % this happens on a new file
{ok, reset_file(Db, Fd, DbName, Group)}
end
end;
Error ->
- catch delete_index_file(RootDir, DbName, GroupId),
+ catch delete_index_file(RootDir, DbName, Sig),
Error
end;
- Error ->
- catch delete_index_file(RootDir, DbName, GroupId),
- Error
- end;
-prepare_group({slow_view, DbName, Fd, Lang, DesignOptions, MapSrc, RedSrc}, _ForceReset) ->
- case couch_db:open(DbName, []) of
- {ok, Db} ->
- View = #view{map_names=[<<"_temp">>],
- id_num=0,
- btree=nil,
- def=MapSrc,
- reduce_funs= if RedSrc==[] -> []; true -> [{<<"_temp">>, RedSrc}] end},
- {ok, init_group(Db, Fd, #group{type=slow_view, name= <<"_temp">>, db=Db,
- views=[View], def_lang=Lang, design_options=DesignOptions}, nil)};
- Error ->
- Error
+ Else ->
+ Else
end.
-
get_index_header_data(#group{current_seq=Seq, purge_seq=PurgeSeq,
id_btree=IdBtree,views=Views}) ->
ViewStates = [couch_btree:get_state(Btree) || #view{btree=Btree} <- Views],
@@ -355,14 +359,47 @@ get_index_header_data(#group{current_seq=Seq, purge_seq=PurgeSeq,
id_btree_state=couch_btree:get_state(IdBtree),
view_states=ViewStates}.
+hex_sig(GroupSig) ->
+ couch_util:to_hex(?b2l(GroupSig)).
+
+design_root(RootDir, DbName) ->
+ RootDir ++ "/." ++ ?b2l(DbName) ++ "_design/".
+
+index_file_name(RootDir, DbName, GroupSig) ->
+ design_root(RootDir, DbName) ++ hex_sig(GroupSig) ++".view".
-open_index_file(RootDir, DbName, GroupId) ->
- FileName = RootDir ++ "/." ++ ?b2l(DbName) ++ ?b2l(GroupId) ++".view",
+index_file_name(compact, RootDir, DbName, GroupSig) ->
+ design_root(RootDir, DbName) ++ hex_sig(GroupSig) ++".compact.view".
+
+
+open_index_file(RootDir, DbName, GroupSig) ->
+ FileName = index_file_name(RootDir, DbName, GroupSig),
case couch_file:open(FileName) of
{ok, Fd} -> {ok, Fd};
{error, enoent} -> couch_file:open(FileName, [create]);
Error -> Error
end.
+
+open_temp_group(DbName, Language, DesignOptions, MapSrc, RedSrc) ->
+ case couch_db:open(DbName, []) of
+ {ok, Db} ->
+ View = #view{map_names=[<<"_temp">>],
+ id_num=0,
+ btree=nil,
+ def=MapSrc,
+ reduce_funs= if RedSrc==[] -> []; true -> [{<<"_temp">>, RedSrc}] end},
+
+ {ok, Db, #group{
+ name = <<"_temp">>,
+ db=Db,
+ views=[View],
+ def_lang=Language,
+ design_options=DesignOptions,
+ sig = erlang:md5(term_to_binary({[View], Language, DesignOptions}))
+ }};
+ Error ->
+ Error
+ end.
open_db_group(DbName, GroupId) ->
case couch_db:open(DbName, []) of
@@ -378,12 +415,28 @@ open_db_group(DbName, GroupId) ->
Else
end.
+get_group_info(#group{
+ fd = Fd,
+ sig = GroupSig,
+ def_lang = Lang
+ }, CompactorPid) ->
+ {ok, Size} = couch_file:bytes(Fd),
+ [
+ {signature, ?l2b(hex_sig(GroupSig))},
+ {language, Lang},
+ {disk_size, Size},
+ {compact_running, CompactorPid /= nil}
+ ].
+
% maybe move to another module
design_doc_to_view_group(#doc{id=Id,body={Fields}}) ->
Language = proplists:get_value(<<"language">>, Fields, <<"javascript">>),
{DesignOptions} = proplists:get_value(<<"options">>, Fields, {[]}),
{RawViews} = proplists:get_value(<<"views">>, Fields, {[]}),
-
+ % sort the views by name to avoid spurious signature changes
+ SortedRawViews = lists:sort(fun({Name1, _}, {Name2, _}) ->
+ Name1 >= Name2
+ end, RawViews),
% add the views to a dictionary object, with the map source as the key
DictBySrc =
lists:foldl(
@@ -402,7 +455,7 @@ design_doc_to_view_group(#doc{id=Id,body={Fields}}) ->
View#view{reduce_funs=[{Name,RedSrc}|View#view.reduce_funs]}
end,
dict:store(MapSrc, View2, DictBySrcAcc)
- end, dict:new(), RawViews),
+ end, dict:new(), SortedRawViews),
% number the views
{Views, _N} = lists:mapfoldl(
fun({_Src, View}, N) ->
@@ -410,7 +463,7 @@ design_doc_to_view_group(#doc{id=Id,body={Fields}}) ->
end, 0, dict:to_list(DictBySrc)),
Group = #group{name=Id, views=Views, def_lang=Language, design_options=DesignOptions},
- Group#group{sig=erlang:md5(term_to_binary(Group))}.
+ Group#group{sig=erlang:md5(term_to_binary({Views, Language, DesignOptions}))}.
reset_group(#group{views=Views}=Group) ->
Views2 = [View#view{btree=nil} || View <- Views],
@@ -423,9 +476,8 @@ reset_file(Db, Fd, DbName, #group{sig=Sig,name=Name} = Group) ->
ok = couch_file:write_header(Fd, {Sig, nil}),
init_group(Db, Fd, reset_group(Group), nil).
-delete_index_file(RootDir, DbName, GroupId) ->
- file:delete(RootDir ++ "/." ++ binary_to_list(DbName)
- ++ binary_to_list(GroupId) ++ ".view").
+delete_index_file(RootDir, DbName, GroupSig) ->
+ file:delete(index_file_name(RootDir, DbName, GroupSig)).
init_group(Db, Fd, #group{views=Views}=Group, nil) ->
init_group(Db, Fd, Group,
diff --git a/src/couchdb/couch_view_updater.erl b/src/couchdb/couch_view_updater.erl
index 3c4c9108..11dfb544 100644
--- a/src/couchdb/couch_view_updater.erl
+++ b/src/couchdb/couch_view_updater.erl
@@ -106,26 +106,6 @@ process_doc(Db, DocInfo, {Docs, #group{sig=Sig,name=GroupId,design_options=Desig
[conflicts, deleted_conflicts]
end,
case {IncludeDesign, DocId} of
- {_, GroupId} ->
- % uh oh. this is the design doc with our definitions. See if
- % anything in the definition changed.
- case couch_db:open_doc_int(Db, DocInfo, DocOpts) of
- {ok, Doc} ->
- case couch_view_group:design_doc_to_view_group(Doc) of
- #group{sig=Sig} ->
- % The same md5 signature, keep on computing
- case IncludeDesign of
- true ->
- {[Doc | Docs], Group, ViewKVs, DocIdViewIdKeys};
- _ ->
- {Docs, Group, ViewKVs, DocIdViewIdKeys}
- end;
- _ ->
- exit(reset)
- end;
- {not_found, missing} ->
- exit(reset)
- end;
{false, <<?DESIGN_DOC_PREFIX, _/binary>>} -> % we skip design docs
{Docs, Group, ViewKVs, DocIdViewIdKeys};
_ ->