summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Lehnardt <jan@apache.org>2009-02-22 13:50:38 +0000
committerJan Lehnardt <jan@apache.org>2009-02-22 13:50:38 +0000
commit598eee1cf5979b61171f623da7e3164244ca3792 (patch)
treec9ff009fe39f2f07b060ace1149a8f074040c2a6
parent1ebc2117452516f6ac6f4f458d7372bc710b79d2 (diff)
Add runtime statistics -- without EUnit tests for now.
git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@746691 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--etc/couchdb/default.ini.tpl.in4
-rw-r--r--share/Makefile.am1
-rw-r--r--share/www/script/couch.js27
-rw-r--r--share/www/script/couch_test_runner.js8
-rw-r--r--share/www/script/couch_tests.js1
-rw-r--r--src/couchdb/Makefile.am8
-rw-r--r--src/couchdb/couch_httpd.erl23
-rw-r--r--src/couchdb/couch_httpd_db.erl15
-rw-r--r--src/couchdb/couch_httpd_misc_handlers.erl9
-rw-r--r--src/couchdb/couch_httpd_view.erl10
-rw-r--r--src/couchdb/couch_server.erl10
-rw-r--r--src/couchdb/couch_server_sup.erl3
12 files changed, 90 insertions, 29 deletions
diff --git a/etc/couchdb/default.ini.tpl.in b/etc/couchdb/default.ini.tpl.in
index e94e1437..0a5dd0a2 100644
--- a/etc/couchdb/default.ini.tpl.in
+++ b/etc/couchdb/default.ini.tpl.in
@@ -35,6 +35,8 @@ external_manager={couch_external_manager, start_link, []}
db_update_notifier={couch_db_update_notifier_sup, start_link, []}
query_servers={couch_query_servers, start_link, []}
httpd={couch_httpd, start_link, []}
+stats_aggregator={couch_stats_aggregator, start, []}
+stats_collector={couch_stats_collector, start, []}
[httpd_global_handlers]
/ = {couch_httpd_misc_handlers, handle_welcome_req, <<"Welcome">>}
@@ -42,12 +44,12 @@ favicon.ico = {couch_httpd_misc_handlers, handle_favicon_req, "%localdatadir%/ww
_utils = {couch_httpd_misc_handlers, handle_utils_dir_req, "%localdatadir%/www"}
_all_dbs = {couch_httpd_misc_handlers, handle_all_dbs_req}
-_stats = {couch_httpd_misc_handlers, handle_stats_req}
_active_tasks = {couch_httpd_misc_handlers, handle_task_status_req}
_config = {couch_httpd_misc_handlers, handle_config_req}
_replicate = {couch_httpd_misc_handlers, handle_replicate_req}
_uuids = {couch_httpd_misc_handlers, handle_uuids_req}
_restart = {couch_httpd_misc_handlers, handle_restart_req}
+_stats = {couch_httpd_stats_handlers, handle_stats_req}
[httpd_db_handlers]
_view = {couch_httpd_view, handle_view_req}
diff --git a/share/Makefile.am b/share/Makefile.am
index 40466cf9..476aa675 100644
--- a/share/Makefile.am
+++ b/share/Makefile.am
@@ -112,5 +112,4 @@ nobase_dist_localdata_DATA = \
www/script/test/purge.js \
www/script/test/config.js \
www/script/test/security_validation.js \
- www/script/test/max_dbs_open.js \
www/style/layout.css
diff --git a/share/www/script/couch.js b/share/www/script/couch.js
index 3f4db334..7bec5e32 100644
--- a/share/www/script/couch.js
+++ b/share/www/script/couch.js
@@ -312,6 +312,23 @@ CouchDB.request = function(method, uri, options) {
return req;
}
+CouchDB.requestStats = function(module, key, aggregate, options) {
+ var options, optionsOrLast = Array.prototype.pop.apply(arguments);
+ if (typeof optionsOrLast == "string") {
+ options = null;
+ Array.prototype.push.apply(arguments, [optionsOrLast]);
+ } else {
+ options = optionsOrLast;
+ }
+
+ var request_options = {};
+ request_options.headers = {"Content-Type": "application/json"};
+
+ var stat = CouchDB.request("GET", "/_stats/" + Array.prototype.join.apply(arguments,["/"]) + (options ?
+ ("?" + CouchDB.params(options)) : ""), request_options).responseText;
+ return JSON.parse(stat)[module][key];
+}
+
CouchDB.uuids_cache = [];
CouchDB.newUuids = function(n) {
@@ -344,3 +361,13 @@ CouchDB.maybeThrowError = function(req) {
throw result;
}
}
+
+CouchDB.params = function(options) {
+ options = options || {};
+ var returnArray = [];
+ for(var key in options) {
+ var value = options[key];
+ returnArray.push(key + "=" + value);
+ }
+ return returnArray.join("&");
+} \ No newline at end of file
diff --git a/share/www/script/couch_test_runner.js b/share/www/script/couch_test_runner.js
index ae357aeb..ae0dc573 100644
--- a/share/www/script/couch_test_runner.js
+++ b/share/www/script/couch_test_runner.js
@@ -152,13 +152,13 @@ function updateTestsFooter() {
// display the line that failed.
// Example:
// T(MyValue==1);
-function T(arg1, arg2) {
+function T(arg1, arg2, testName) {
if (!arg1) {
if (currentRow) {
if ($("td.details ol", currentRow).length == 0) {
$("<ol></ol>").appendTo($("td.details", currentRow));
}
- $("<li><b>Assertion failed:</b> <code class='failure'></code></li>")
+ $("<li><b>Assertion " + (testName ? "'" + testName + "'" : "") + " failed:</b> <code class='failure'></code></li>")
.find("code").text((arg2 != null ? arg2 : arg1).toString()).end()
.appendTo($("td.details ol", currentRow));
}
@@ -166,6 +166,10 @@ function T(arg1, arg2) {
}
}
+function TEquals(expected, actual, testName) {
+ T(equals(expected, actual), "expected '" + expected + "', got '" + actual + "'", testName);
+}
+
function equals(a,b) {
if (a === b) return true;
try {
diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js
index 7b25cb4f..a6195cf0 100644
--- a/share/www/script/couch_tests.js
+++ b/share/www/script/couch_tests.js
@@ -66,7 +66,6 @@ loadTest("compact.js");
loadTest("purge.js");
loadTest("config.js");
loadTest("security_validation.js");
-loadTest("max_dbs_open.js");
function makeDocs(start, end, templateDoc) {
var templateDocSrc = templateDoc ? JSON.stringify(templateDoc) : "{}"
diff --git a/src/couchdb/Makefile.am b/src/couchdb/Makefile.am
index 8dd21256..10c06ebe 100644
--- a/src/couchdb/Makefile.am
+++ b/src/couchdb/Makefile.am
@@ -58,6 +58,7 @@ source_files = \
couch_httpd_show.erl \
couch_httpd_view.erl \
couch_httpd_misc_handlers.erl \
+ couch_httpd_stats_handlers.erl \
couch_key_tree.erl \
couch_log.erl \
couch_os_process.erl \
@@ -66,6 +67,8 @@ source_files = \
couch_rep.erl \
couch_server.erl \
couch_server_sup.erl \
+ couch_stats_aggregator.erl \
+ couch_stats_collector.erl \
couch_stream.erl \
couch_task_status.erl \
couch_util.erl \
@@ -96,6 +99,7 @@ compiled_files = \
couch_httpd_show.beam \
couch_httpd_view.beam \
couch_httpd_misc_handlers.beam \
+ couch_httpd_stats_handlers.beam \
couch_key_tree.beam \
couch_log.beam \
couch_os_process.beam \
@@ -104,6 +108,8 @@ compiled_files = \
couch_rep.beam \
couch_server.beam \
couch_server_sup.beam \
+ couch_stats_aggregator.beam \
+ couch_stats_collector.beam \
couch_stream.beam \
couch_task_status.beam \
couch_util.beam \
@@ -152,7 +158,7 @@ couch.app: couch.app.tpl
# $(ERL) -noshell -run edoc_run files [\"$<\"]
%.beam: %.erl couch_db.hrl
- $(ERLC) $<
+ $(ERLC) ${TEST} $<;
install-data-hook:
if test -f "$(DESTDIR)/$(couchprivlibdir)/couch_erl_driver"; then \
diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
index 5549e40a..9b175825 100644
--- a/src/couchdb/couch_httpd.erl
+++ b/src/couchdb/couch_httpd.erl
@@ -102,6 +102,7 @@ stop() ->
handle_request(MochiReq, UrlHandlers, DbUrlHandlers) ->
+ statistics(runtime), % prepare request_time counter, see end of function
AuthenticationFun = make_arity_1_fun(
couch_config:get("httpd", "authentication_handler")),
% for the path, use the raw path with the query string and fragment
@@ -123,11 +124,8 @@ handle_request(MochiReq, UrlHandlers, DbUrlHandlers) ->
mochiweb_headers:to_list(MochiReq:get(headers))
]),
- Method =
+ Method1 =
case MochiReq:get(method) of
- % alias HEAD to GET as mochiweb takes care of stripping the body
- 'HEAD' -> 'GET';
-
% already an atom
Meth when is_atom(Meth) -> Meth;
@@ -135,6 +133,15 @@ handle_request(MochiReq, UrlHandlers, DbUrlHandlers) ->
% possible (if any module references the atom, then it's existing).
Meth -> couch_util:to_existing_atom(Meth)
end,
+
+ increment_method_stats(Method1),
+
+ % alias HEAD to GET as mochiweb takes care of stripping the body
+ Method = case Method1 of
+ 'HEAD' -> 'GET';
+ Other -> Other
+ end,
+
HttpReq = #httpd{
mochi_req = MochiReq,
method = Method,
@@ -163,8 +170,14 @@ handle_request(MochiReq, UrlHandlers, DbUrlHandlers) ->
RawUri,
Resp:get(code)
]),
+ {_TotalRuntime, RequestTime} = statistics(runtime),
+ couch_stats_collector:record({couchdb, request_time}, RequestTime),
+ couch_stats_collector:increment({httpd, requests}),
{ok, Resp}.
+increment_method_stats(Method) ->
+ CounterName = list_to_atom(string:to_lower(atom_to_list(Method)) ++ "_requests"),
+ couch_stats_collector:increment({httpd, CounterName}).
special_test_authentication_handler(Req) ->
case header_value(Req, "WWW-Authenticate") of
@@ -325,6 +338,7 @@ basic_username_pw(Req) ->
start_chunked_response(#httpd{mochi_req=MochiReq}, Code, Headers) ->
+ couch_stats_collector:increment({http_status_codes, Code}),
{ok, MochiReq:respond({Code, Headers ++ server_header(), chunked})}.
send_chunk(Resp, Data) ->
@@ -332,6 +346,7 @@ send_chunk(Resp, Data) ->
{ok, Resp}.
send_response(#httpd{mochi_req=MochiReq}, Code, Headers, Body) ->
+ couch_stats_collector:increment({http_status_codes, Code}),
if Code >= 400 ->
?LOG_DEBUG("HTTPd ~p error response:~n ~s", [Code, Body]);
true -> ok
diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl
index 95c96349..78339911 100644
--- a/src/couchdb/couch_httpd_db.erl
+++ b/src/couchdb/couch_httpd_db.erl
@@ -80,6 +80,7 @@ db_req(#httpd{method='POST',path_parts=[DbName]}=Req, Db) ->
Doc = couch_doc:from_json_obj(couch_httpd:json_body(Req)),
DocId = couch_util:new_uuid(),
{ok, NewRev} = couch_db:update_doc(Db, Doc#doc{id=DocId, revs=[]}, []),
+ couch_stats_collector:increment({httpd, document_creates}),
DocUrl = absolute_uri(Req,
binary_to_list(<<"/",DbName/binary,"/",DocId/binary>>)),
send_json(Req, 201, [{"Location", DocUrl}], {[
@@ -102,6 +103,7 @@ db_req(#httpd{path_parts=[_,<<"_ensure_full_commit">>]}=Req, _Db) ->
send_method_not_allowed(Req, "POST");
db_req(#httpd{method='POST',path_parts=[_,<<"_bulk_docs">>]}=Req, Db) ->
+ couch_stats_collector:increment({httpd, bulk_requests}),
{JsonProps} = couch_httpd:json_body(Req),
DocsArray = proplists:get_value(<<"docs">>, JsonProps),
case couch_httpd:header_value(Req, "X-Couch-Full-Commit", "false") of
@@ -377,6 +379,7 @@ db_doc_req(#httpd{method='DELETE'}=Req, Db, DocId) ->
couch_httpd:send_error(Req, 409, <<"missing_rev">>,
<<"Document rev/etag must be specified to delete">>);
RevToDelete ->
+ couch_stats_collector:increment({httpd, document_deletes}),
{ok, NewRev} = couch_db:delete_doc(Db, DocId, [RevToDelete]),
send_json(Req, 200, {[
{ok, true},
@@ -391,6 +394,7 @@ db_doc_req(#httpd{method='GET'}=Req, Db, DocId) ->
open_revs = Revs,
options = Options
} = parse_doc_query(Req),
+ couch_stats_collector:increment({httpd, document_reads}),
case Revs of
[] ->
Doc = couch_doc_open(Db, DocId, Rev, Options),
@@ -400,7 +404,7 @@ db_doc_req(#httpd{method='GET'}=Req, Db, DocId) ->
[] -> [{"Etag", DiskEtag}]; % output etag only when we have no meta
_ -> []
end,
- send_json(Req, 200, Headers, couch_doc:to_json_obj(Doc, Options))
+ send_json(Req, 200, Headers, couch_doc:to_json_obj(Doc, Options))
end);
_ ->
{ok, Results} = couch_db:open_doc_revs(Db, DocId, Revs, Options),
@@ -467,8 +471,10 @@ db_doc_req(#httpd{method='PUT'}=Req, Db, DocId) ->
end,
case extract_header_rev(Req, ExplicitRev) of
missing_rev ->
+ couch_stats_collector:increment({httpd, document_creates}),
Revs = [];
Rev ->
+ couch_stats_collector:increment({httpd, document_updates}),
Revs = [Rev]
end,
{ok, NewRev} = couch_db:update_doc(Db, Doc#doc{id=DocId, revs=Revs}, Options),
@@ -492,6 +498,7 @@ db_doc_req(#httpd{method='COPY'}=Req, Db, SourceDocId) ->
% save new doc
{ok, NewTargetRev} = couch_db:update_doc(Db, Doc#doc{id=TargetDocId, revs=TargetRev}, []),
+ couch_stats_collector:increment({httpd, document_copies}),
send_json(Req, 201, [{"Etag", "\"" ++ binary_to_list(NewTargetRev) ++ "\""}], {[
{ok, true},
@@ -517,9 +524,9 @@ db_doc_req(#httpd{method='MOVE'}=Req, Db, SourceDocId) ->
Doc#doc{id=TargetDocId, revs=TargetRev},
#doc{id=SourceDocId, revs=[SourceRev], deleted=true}
],
-
{ok, ResultRevs} = couch_db:update_docs(Db, Docs, []),
-
+ couch_stats_collector:increment({httpd, document_moves}),
+
DocResults = lists:zipwith(
fun(FDoc, NewRev) ->
{[{id, FDoc#doc.id}, {rev, NewRev}]}
@@ -622,8 +629,10 @@ db_attachment_req(#httpd{method=Method}=Req, Db, DocId, FileNameParts)
Doc = case extract_header_rev(Req, couch_httpd:qs_value(Req, "rev")) of
missing_rev -> % make the new doc
+ couch_stats_collector:increment({httpd, document_creates}),
#doc{id=DocId};
Rev ->
+ couch_stats_collector:increment({httpd, document_updates}),
case couch_db:open_doc_revs(Db, DocId, [Rev], []) of
{ok, [{ok, Doc0}]} -> Doc0#doc{revs=[Rev]};
{ok, [Error]} -> throw(Error)
diff --git a/src/couchdb/couch_httpd_misc_handlers.erl b/src/couchdb/couch_httpd_misc_handlers.erl
index 92ff3b0a..be9e0033 100644
--- a/src/couchdb/couch_httpd_misc_handlers.erl
+++ b/src/couchdb/couch_httpd_misc_handlers.erl
@@ -14,7 +14,7 @@
-export([handle_welcome_req/2,handle_favicon_req/2,handle_utils_dir_req/2,
handle_all_dbs_req/1,handle_replicate_req/1,handle_restart_req/1,
- handle_uuids_req/1,handle_config_req/1,handle_stats_req/1,
+ handle_uuids_req/1,handle_config_req/1,
handle_task_status_req/1]).
-export([increment_update_seq_req/2]).
@@ -63,13 +63,6 @@ handle_all_dbs_req(Req) ->
send_method_not_allowed(Req, "GET,HEAD").
-handle_stats_req(#httpd{method='GET'}=Req) ->
- ok = couch_httpd:verify_is_server_admin(Req),
- send_json(Req, {couch_server:get_stats() ++ couch_file_stats:get_stats()});
-handle_stats_req(Req) ->
- send_method_not_allowed(Req, "GET,HEAD").
-
-
handle_task_status_req(#httpd{method='GET'}=Req) ->
ok = couch_httpd:verify_is_server_admin(Req),
% convert the list of prop lists to a list of json objects
diff --git a/src/couchdb/couch_httpd_view.erl b/src/couchdb/couch_httpd_view.erl
index 9c8545cb..c4e2174e 100644
--- a/src/couchdb/couch_httpd_view.erl
+++ b/src/couchdb/couch_httpd_view.erl
@@ -28,8 +28,8 @@ design_doc_view(Req, Db, Id, ViewName, Keys) ->
reduce = Reduce
} = QueryArgs = parse_view_query(Req, Keys),
DesignId = <<"_design/", Id/binary>>,
- case couch_view:get_map_view(Db, DesignId, ViewName, Stale) of
- {ok, View, Group} ->
+ Result = case couch_view:get_map_view(Db, DesignId, ViewName, Stale) of
+ {ok, View, Group} ->
output_map_view(Req, View, Group, Db, QueryArgs, Keys);
{not_found, Reason} ->
case couch_view:get_reduce_view(Db, DesignId, ViewName, Stale) of
@@ -45,7 +45,9 @@ design_doc_view(Req, Db, Id, ViewName, Keys) ->
_ ->
throw({not_found, Reason})
end
- end.
+ end,
+ couch_stats_collector:increment({httpd, view_reads}),
+ Result.
handle_view_req(#httpd{method='GET',path_parts=[_,_, Id, ViewName]}=Req, Db) ->
design_doc_view(Req, Db, Id, ViewName, nil);
@@ -60,7 +62,7 @@ handle_view_req(Req, _Db) ->
handle_temp_view_req(#httpd{method='POST'}=Req, Db) ->
QueryArgs = parse_view_query(Req),
-
+ couch_stats_collector:increment({httpd, temporary_view_reads}),
case couch_httpd:primary_header_value(Req, "content-type") of
undefined -> ok;
"application/json" -> ok;
diff --git a/src/couchdb/couch_server.erl b/src/couchdb/couch_server.erl
index 39b33c63..8f747a56 100644
--- a/src/couchdb/couch_server.erl
+++ b/src/couchdb/couch_server.erl
@@ -182,7 +182,9 @@ maybe_close_lru_db(#server{dbs_open=NumOpen, max_dbs_open=MaxOpen}=Server)
maybe_close_lru_db(#server{dbs_open=NumOpen}=Server) ->
% must free up the lru db.
case try_close_lru(now()) of
- ok -> {ok, Server#server{dbs_open=NumOpen-1}};
+ ok ->
+ couch_stats_collector:decrement({couchdb, open_databases}),
+ {ok, Server#server{dbs_open=NumOpen - 1}};
Error -> Error
end.
@@ -235,6 +237,7 @@ handle_call({open, DbName, Options}, _From, Server) ->
true = ets:insert(couch_dbs_by_pid, {MainPid, DbName}),
true = ets:insert(couch_dbs_by_lru, {LruTime, DbName}),
DbsOpen = Server2#server.dbs_open + 1,
+ couch_stats_collector:increment({couchdb, open_databases}),
{reply, {ok, MainPid},
Server2#server{dbs_open=DbsOpen}};
Error ->
@@ -270,6 +273,7 @@ handle_call({create, DbName, Options}, _From, Server) ->
true = ets:insert(couch_dbs_by_pid, {MainPid, DbName}),
true = ets:insert(couch_dbs_by_lru, {LruTime, DbName}),
DbsOpen = Server2#server.dbs_open + 1,
+ couch_stats_collector:increment({couchdb, open_databases}),
couch_db_update_notifier:notify({created, DbName}),
{reply, {ok, MainPid},
Server2#server{dbs_open=DbsOpen}};
@@ -299,6 +303,7 @@ handle_call({delete, DbName, _Options}, _From, Server) ->
true = ets:delete(couch_dbs_by_name, DbName),
true = ets:delete(couch_dbs_by_pid, Pid),
true = ets:delete(couch_dbs_by_lru, LruTime),
+ couch_stats_collector:decrement({couchdb, open_databases}),
Server#server{dbs_open=Server#server.dbs_open - 1}
end,
case file:delete(FullFilepath) of
@@ -328,6 +333,7 @@ handle_info({'EXIT', Pid, _Reason}, #server{dbs_open=DbsOpen}=Server) ->
true = ets:delete(couch_dbs_by_pid, Pid),
true = ets:delete(couch_dbs_by_name, DbName),
true = ets:delete(couch_dbs_by_lru, LruTime),
- {noreply, Server#server{dbs_open=DbsOpen-1}};
+ couch_stats_collector:decrement({couchdb, open_databases}),
+ {noreply, Server#server{dbs_open=DbsOpen - 1}};
handle_info(Info, _Server) ->
exit({unknown_message, Info}).
diff --git a/src/couchdb/couch_server_sup.erl b/src/couchdb/couch_server_sup.erl
index 627c34a9..34588cb8 100644
--- a/src/couchdb/couch_server_sup.erl
+++ b/src/couchdb/couch_server_sup.erl
@@ -158,7 +158,6 @@ start_primary_services() ->
supervisor,
dynamic}]}).
-
start_secondary_services() ->
DaemonChildSpecs = [
begin
@@ -173,7 +172,7 @@ start_secondary_services() ->
end
|| {Name, SpecStr}
<- couch_config:get("daemons"), SpecStr /= ""],
-
+
supervisor:start_link({local, couch_secondary_services}, couch_server_sup,
{{one_for_one, 10, 3600}, DaemonChildSpecs}).