diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | README | 4 | ||||
-rw-r--r-- | bin/couchdb.tpl.in | 28 | ||||
-rwxr-xr-x | bootstrap | 2 | ||||
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | etc/couchdb/Makefile.am | 16 | ||||
-rw-r--r-- | etc/couchdb/couch.ini.tpl.in | 21 | ||||
-rw-r--r-- | share/www/script/couch_tests.js | 31 | ||||
-rw-r--r-- | src/Makefile.am | 13 | ||||
-rw-r--r-- | src/couchdb/Makefile.am | 70 | ||||
-rw-r--r-- | src/couchdb/couch.app.tpl.in | 4 | ||||
-rw-r--r-- | src/couchdb/couch_db_update_notifier.erl | 2 | ||||
-rw-r--r-- | src/couchdb/couch_file.erl | 2 | ||||
-rw-r--r-- | src/couchdb/couch_ft_query.erl | 32 | ||||
-rw-r--r-- | src/couchdb/couch_httpd.erl | 99 | ||||
-rw-r--r-- | src/couchdb/couch_log.erl | 24 | ||||
-rw-r--r-- | src/couchdb/couch_query_servers.erl | 50 | ||||
-rw-r--r-- | src/couchdb/couch_server.erl | 38 | ||||
-rw-r--r-- | src/couchdb/couch_server_sup.erl | 260 | ||||
-rw-r--r-- | src/couchdb/couch_util.erl | 49 | ||||
-rw-r--r-- | src/couchdb/couch_view.erl | 20 |
21 files changed, 452 insertions, 321 deletions
diff --git a/Makefile.am b/Makefile.am index 4333b238..744424f7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,7 +10,7 @@ ## License for the specific language governing permissions and limitations ## under the License. -SUBDIRS = bin etc src share var +SUBDIRS = bin etc src/couchdb src/mochiweb share test var localdoc_DATA = AUTHORS.gz BUGS.gz CHANGES.gz NEWS.gz README.gz THANKS.gz @@ -114,7 +114,7 @@ Note: All the examples assume you have installed into `/usr/local`. If everything was successful you should see the following message: - You have configured Apache CouchDB. Time to relax. + You have configured Apache CouchDB, time to relax. Relax. @@ -177,7 +177,7 @@ This uses the `sudo` command to run the `couchdb` command as the `couchdb` user. When Apache CouchDB starts it should eventually display the following message: - Apache CouchDB has started. Time to relax. + Apache CouchDB has started, time to relax. See http://localhost:5984/_utils/index.html Relax. diff --git a/bin/couchdb.tpl.in b/bin/couchdb.tpl.in index 2fbafb28..e4865b53 100644 --- a/bin/couchdb.tpl.in +++ b/bin/couchdb.tpl.in @@ -26,7 +26,9 @@ RESPAWN_TIMEOUT=0 LIB_DIRECTORY=%localstatelibdir% LOG_DIRECTORY=%localstatelogdir% -INI_FILE=%localconfdir%/couch.ini +DEFAULT_INI_FILE=%localconfdir%/default.ini +LOCAL_INI_FILE=%localconfdir%/local.ini + PID_FILE=%localstatedir%/run/couchdb.pid STDOUT_FILE=couchdb.stdout @@ -70,10 +72,10 @@ Options: -h display a short help message and exit -V display version information and exit - -c FILE set the configuration FILE (defaults to $INI_FILE) + -c FILE use configuration FILE (chainable, resets system default) -i use the interactive Erlang shell -b spawn as a background process - -p FILE set the background PID FILE (defaults to $PID_FILE) + -p FILE set the background PID FILE (overrides system default) -r SECONDS respawn background process after SECONDS (defaults to no respawn) -o FILE redirect background stdout to FILE (defaults to $STDOUT_FILE) -e FILE redirect background stderr to FILE (defaults to $STDERR_FILE) @@ -105,7 +107,7 @@ check_status () { PID=`_get_pid` if test -n "$PID"; then if kill -0 $PID 2> /dev/null; then - echo "Apache CouchDB is running as process $PID. Time to relax." + echo "Apache CouchDB is running as process $PID, time to relax." return $SCRIPT_OK else echo >&2 << EOF @@ -168,6 +170,17 @@ start_couchdb () { touch $PID_FILE interactive_option="+Bd -noinput" fi + if test -n "$INI_FILES"; then + ini_files="$INI_FILES" + else + ini_files="$DEFAULT_INI_FILE $LOCAL_INI_FILE" + fi + for file in $ini_files; do + if test -n "$start_arguments"; then + start_arguments="$start_arguments, "; + fi + start_arguments="$start_arguments \\\"$file\\\"" + done command="`%ICU_CONFIG% --invoke` \ %ERL% $interactive_option -smp auto -sasl errlog_type error \ -pa %localerlanglibdir%/couch-%version%/ebin \ @@ -177,13 +190,12 @@ start_couchdb () { -eval \"application:load(couch)\" \ -eval \"crypto:start()\" \ -eval \"inets:start()\" \ - -eval \"couch_server:start(), receive done -> done end.\" \ - -couchini $INI_FILE" + -eval \"couch_server:start([$start_arguments]), receive done -> done end.\" " if test "$BACKGROUND_BOOLEAN" = "true" \ -a "$RECURSED_BOOLEAN" = "false"; then $0 -c $INI_FILE -b -r $RESPAWN_TIMEOUT -p $PID_FILE \ -o $STDOUT_FILE -e $STDERR_FILE -R & - echo "Apache CouchDB has started. Time to relax." + echo "Apache CouchDB has started, time to relax." else if test "$RECURSED_BOOLEAN" = "true"; then while true; do @@ -257,7 +269,7 @@ parse_script_option_list () { case "$1" in -h) shift; display_help; exit $SCRIPT_OK;; -V) shift; display_version; exit $SCRIPT_OK;; - -c) shift; INI_FILE=$1; shift;; + -c) shift; INI_FILES="$INI_FILES $1"; shift;; -i) shift; INTERACTIVE_BOOLEAN=true;; -b) shift; BACKGROUND_BOOLEAN=true;; -r) shift; RESPAWN_TIMEOUT=$1; shift;; @@ -188,7 +188,7 @@ run_command_collection () { run_autoconf cat << EOF -You have bootstrapped Apache CouchDB. Time to relax. +You have bootstrapped Apache CouchDB, time to relax. Run \`./configure' to configure the source before you install. EOF diff --git a/configure.ac b/configure.ac index b82edf34..bf6d948a 100644 --- a/configure.ac +++ b/configure.ac @@ -232,7 +232,7 @@ AC_CONFIG_FILES([bin/couchjs.tpl]) AC_CONFIG_FILES([bin/couchdb.tpl]) AC_CONFIG_FILES([bin/Makefile]) AC_CONFIG_FILES([etc/couchdb/Makefile]) -AC_CONFIG_FILES([etc/couchdb/couch.ini.tpl]) +AC_CONFIG_FILES([etc/couchdb/default.ini.tpl]) AC_CONFIG_FILES([etc/default/couchdb.tpl]) AC_CONFIG_FILES([etc/default/Makefile]) AC_CONFIG_FILES([etc/init/couchdb.tpl]) @@ -246,12 +246,12 @@ AC_CONFIG_FILES([share/Makefile]) AC_CONFIG_FILES([src/couchdb/couch.app.tpl]) AC_CONFIG_FILES([src/couchdb/Makefile]) AC_CONFIG_FILES([src/mochiweb/Makefile]) -AC_CONFIG_FILES([src/Makefile]) +AC_CONFIG_FILES([test/Makefile]) AC_CONFIG_FILES([var/Makefile]) AC_OUTPUT echo -echo "You have configured Apache CouchDB. Time to relax." +echo "You have configured Apache CouchDB, time to relax." echo echo "Run \`make && make install' to install." diff --git a/etc/couchdb/Makefile.am b/etc/couchdb/Makefile.am index 9f686780..c253048d 100644 --- a/etc/couchdb/Makefile.am +++ b/etc/couchdb/Makefile.am @@ -10,16 +10,16 @@ ## License for the specific language governing permissions and limitations ## under the License. -localconf_DATA = couch.ini +couchprivlibdir = $(localerlanglibdir)/couch-$(version)/priv/lib + +localconf_DATA = default.ini CLEANFILES = $(localconf_DATA) transform = @program_transform_name@ couchjs_command_name = `echo couchjs | sed '$(transform)'` -couchprivlibdir = $(localerlanglibdir)/couch-$(version)/priv/lib - -couch.ini: couch.ini.tpl +default.ini: default.ini.tpl sed -e "s|%bindir%|$(bindir)|g" \ -e "s|%localconfdir%|$(localconfdir)|g" \ -e "s|%localdatadir%|$(localdatadir)|g" \ @@ -28,3 +28,11 @@ couch.ini: couch.ini.tpl -e "s|%couchprivlibdir%|$(couchprivlibdir)|g" \ -e "s|%couchjs_command_name%|$(couchjs_command_name)|g" \ < $< > $@ + +install-data-hook: + if test ! -f "$(localconfdir)/local.ini"; then \ + cp local.ini $(localconfdir)/local.ini; \ + fi + +uninstall-local: + rm -f $(localconfdir)/local.ini diff --git a/etc/couchdb/couch.ini.tpl.in b/etc/couchdb/couch.ini.tpl.in index af7d32b7..e69de29b 100644 --- a/etc/couchdb/couch.ini.tpl.in +++ b/etc/couchdb/couch.ini.tpl.in @@ -1,21 +0,0 @@ -[Couch] - -ConsoleStartupMsg=Apache CouchDB is starting. - -DbRootDir=%localstatelibdir% - -Port=5984 - -BindAddress=127.0.0.1 - -DocumentRoot=%localdatadir%/www - -LogFile=%localstatelogdir%/couch.log - -UtilDriverDir=%couchprivlibdir% - -LogLevel=info - -[Couch Query Servers] - -javascript=%bindir%/%couchjs_command_name% %localdatadir%/server/main.js diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js index 287ba6fb..fd6eaed2 100644 --- a/share/www/script/couch_tests.js +++ b/share/www/script/couch_tests.js @@ -388,12 +388,12 @@ var tests = { result = db.query(map, reduce, {startkey: i, endkey: numDocs - i}); T(result.rows[0].value == 2*(summate(numDocs-i) - summate(i-1))); } - + db.deleteDb(); db.createDb(); for(var i=1; i <= 5; i++) { - + for(var j=0; j < 10; j++) { // these docs are in the order of the keys collation, for clarity var docs = []; @@ -411,12 +411,12 @@ var tests = { T(db.bulkSave(docs).ok); T(db.info().doc_count == ((i - 1) * 10 * 11) + ((j + 1) * 11)); } - + map = function (doc) {emit(doc.keys, 1)}; reduce = function (keys, values) { return sum(values); }; - + var results = db.query(map, reduce, {group:true}); - + //group by exact key match T(equals(results.rows[0], {key:["a"],value:20*i})); T(equals(results.rows[1], {key:["a","b"],value:20*i})); @@ -432,7 +432,7 @@ var tests = { var results = db.query(map, reduce, {group_level:1}); T(equals(results.rows[0], {key:["a"],value:70*i})); T(equals(results.rows[1], {key:["d"],value:40*i})); - + //group by the first 2 elements in the key array var results = db.query(map, reduce, {group_level:2}); T(equals(results.rows[0], {key:["a"],value:20*i})); @@ -443,13 +443,13 @@ var tests = { T(equals(results.rows[5], {key:["d","b"],value:10*i})); T(equals(results.rows[6], {key:["d","c"],value:10*i})); } - + // now test out more complex reductions that need to use the combine option. - + db.deleteDb(); db.createDb(); - + var map = function (doc) {emit(doc.val, doc.val)}; var reduceCombine = function (keys, values, rereduce) { // This computes the standard deviation of the mapped results @@ -457,7 +457,7 @@ var tests = { var count=0; var total=0.0; var sqrTotal=0.0; - + if (!rereduce) { // This is the reduce phase, we are reducing over emitted values from // the map functions. @@ -467,7 +467,7 @@ var tests = { } count = values.length; } - else { + else { // This is the rereduce phase, we are re-reducing previosuly // reduced values. for(var i in values) { @@ -476,17 +476,18 @@ var tests = { sqrTotal = sqrTotal + values[i].sqrTotal; } } - + var variance = (sqrTotal - ((total * total)/count)) / count; stdDeviation = Math.sqrt(variance); - + // the reduce result. It contains enough information to be rereduced // with other reduce results. return {"stdDeviation":stdDeviation,"count":count, "total":total,"sqrTotal":sqrTotal}; }; - + // Save a bunch a docs. + for(var i=0; i < 10; i++) { var docs = []; docs.push({val:10}); @@ -786,7 +787,7 @@ var tests = { if (debug) debugger; var numDocs = 500; - + function makebigstring(power) { var str = "a"; while(power-- > 0) { diff --git a/src/Makefile.am b/src/Makefile.am index f656a6d0..e69de29b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,13 +0,0 @@ -## Licensed under the Apache License, Version 2.0 (the "License"); you may not -## use this file except in compliance with the License. You may obtain a copy -## of the License at -## -## http://www.apache.org/licenses/LICENSE-2.0 -## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -## WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -## License for the specific language governing permissions and limitations -## under the License. - -SUBDIRS = couchdb mochiweb diff --git a/src/couchdb/Makefile.am b/src/couchdb/Makefile.am index b7fe29ce..710ff1f0 100644 --- a/src/couchdb/Makefile.am +++ b/src/couchdb/Makefile.am @@ -7,12 +7,15 @@ ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, WITHOUT ## WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -## License for the specific language governing permissions and limitations -## under the License. +## License for the specific language governing permissions and limitations under +## the License. ICU_LOCAL_FLAGS = $(ICU_LOCAL_CFLAGS) $(ICU_LOCAL_LDFLAGS) +devdocdir = $(localdocdir)/developer/couchdb couchprivlibdir = $(localerlanglibdir)/couch-$(version)/priv/lib +couchincludedir = $(localerlanglibdir)/couch-$(version)/include +couchebindir = $(localerlanglibdir)/couch-$(version)/ebin couchprivlib_LTLIBRARIES = couch_erl_driver.la couch_erl_driver_la_SOURCES = couch_erl_driver.c @@ -23,14 +26,24 @@ couch_erl_driver_la_LIBADD = -licuuc -licudata -licui18n locallibbin_PROGRAMS = couchjs couchjs_SOURCES = couch_js.c -couchebindir = $(localerlanglibdir)/couch-$(version)/ebin -couchincludedir = $(localerlanglibdir)/couch-$(version)/include +couchinclude_DATA = couch_db.hrl + +couchebin_DATA = $(compiled_files) -couch_file_collection = \ +dist_devdoc_DATA = $(doc_base) $(doc_modules) + +EXTRA_DIST = $(couch_files) + +CLEANFILES = $(compiled_files) $(doc_base) $(doc_modules) edoc-info + +source_files = \ cjson.erl \ couch_btree.erl \ + couch_config.erl \ + couch_config_writer.erl \ couch_db.erl \ couch_db_update_notifier.erl \ + couch_db_update_notifier_sup.erl \ couch_doc.erl \ couch_event_sup.erl \ couch_file.erl \ @@ -47,12 +60,15 @@ couch_file_collection = \ couch_view.erl \ couch_db_updater.erl -couchebin_DATA = \ +compiled_files = \ cjson.beam \ couch.app \ couch_btree.beam \ + couch_config.beam \ + couch_config_writer.beam \ couch_db.beam \ couch_db_update_notifier.beam \ + couch_db_update_notifier_sup.beam \ couch_doc.beam \ couch_event_sup.beam \ couch_file.beam \ @@ -69,11 +85,36 @@ couchebin_DATA = \ couch_view.beam \ couch_db_updater.beam -couchinclude_DATA = couch_db.hrl - -EXTRA_DIST = $(couch_file_collection) $(couchinclude_DATA) +doc_base = \ + erlang.png \ + index.html \ + modules-frame.html \ + overview-summary.html \ + packages-frame.html \ + stylesheet.css -CLEANFILES = $(couchebin_DATA) +doc_modules = \ + cjson.html \ + couch_btree.html \ + couch_config.html \ + couch_config_writer.html \ + couch_db.html \ + couch_db_update_notifier.html \ + couch_db_update_notifier_sup.html \ + couch_doc.html \ + couch_event_sup.html \ + couch_file.html \ + couch_ft_query.html \ + couch_httpd.html \ + couch_key_tree.html \ + couch_log.html \ + couch_query_servers.html \ + couch_rep.html \ + couch_server.html \ + couch_server_sup.html \ + couch_stream.html \ + couch_util.html \ + couch_view.html couch.app: couch.app.tpl sed -e "s|%package_name%|@package_name@|g" \ @@ -81,6 +122,15 @@ couch.app: couch.app.tpl $@ < $< chmod +x $@ +$(dist_devdoc_DATA): $(source_files) + for file in $(source_files); do \ + if test -n "$$edoc_files"; then \ + edoc_files="$$edoc_files, "; \ + fi; \ + edoc_files="$$edoc_files \"$$file\""; \ + done; \ + erl -noshell -run edoc_run files ["$$edoc_files"] + %.beam: %.erl $(ERLC) $< diff --git a/src/couchdb/couch.app.tpl.in b/src/couchdb/couch.app.tpl.in index ba7f13c1..9766d0f9 100644 --- a/src/couchdb/couch.app.tpl.in +++ b/src/couchdb/couch.app.tpl.in @@ -17,6 +17,7 @@ couch_httpd, couch_event_sup, couch_db_update_notifier, + couch_db_update_notifier_sup, couch_ft_query, couch_log, couch_rep]}, @@ -24,5 +25,6 @@ couch_server_sup, couch_view, couch_query_servers, - couch_ft_query]}, + couch_ft_query, + couch_db_update_notifier_sup]}, {applications,[kernel,stdlib,crypto,inets,mochiweb]}]}. diff --git a/src/couchdb/couch_db_update_notifier.erl b/src/couchdb/couch_db_update_notifier.erl index 96354620..f944264c 100644 --- a/src/couchdb/couch_db_update_notifier.erl +++ b/src/couchdb/couch_db_update_notifier.erl @@ -25,8 +25,6 @@ -export([start_link/1, notify/1]). -export([init/1, terminate/2, handle_event/2, handle_call/2, handle_info/2, code_change/3,stop/1]). --define(ERR_HANDLE, {Port, {exit_status, Status}} -> {stop, {unknown_error, Status}, {unknown_error, Status}, Port}). - start_link(Exec) -> couch_event_sup:start_link(couch_db_update, {couch_db_update_notifier, make_ref()}, Exec). diff --git a/src/couchdb/couch_file.erl b/src/couchdb/couch_file.erl index 37e1d7ac..5ccb521b 100644 --- a/src/couchdb/couch_file.erl +++ b/src/couchdb/couch_file.erl @@ -394,7 +394,7 @@ handle_info(Info, Fd) -> -should_close(Fd) -> +should_close(_Fd) -> case process_info(self(), links) of {links, [_]} -> % no linkers left (except our fd port). What about monitors? diff --git a/src/couchdb/couch_ft_query.erl b/src/couchdb/couch_ft_query.erl index 2d1b9fc5..67fa5948 100644 --- a/src/couchdb/couch_ft_query.erl +++ b/src/couchdb/couch_ft_query.erl @@ -13,14 +13,12 @@ -module(couch_ft_query). -behaviour(gen_server). --export([start_link/1, execute/2]). +-export([start_link/0, execute/2]). -export([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2,code_change/3, stop/0]). --define(ERR_HANDLE, {Port, {exit_status, Status}} -> {stop, {unknown_error, Status}, {unknown_error, Status}, Port}). - -start_link(QueryExec) -> - gen_server:start_link({local, couch_ft_query}, couch_ft_query, QueryExec, []). +start_link() -> + gen_server:start_link({local, couch_ft_query}, couch_ft_query, [], []). stop() -> exit(whereis(couch_ft_query), close). @@ -28,15 +26,27 @@ stop() -> execute(DatabaseName, QueryString) -> gen_server:call(couch_ft_query, {ft_query, DatabaseName, QueryString}). -init(QueryExec) -> - Port = open_port({spawn, QueryExec}, [{line, 1000}, exit_status, hide]), - {ok, Port}. +init([]) -> + ok = couch_config:register( + fun({"Search", "QueryServer"}) -> + ?MODULE:stop() + end), + + case couch_config:get({"Search", "QueryServer"}, none) of + none -> + {ok, none}; + QueryExec -> + Port = open_port({spawn, QueryExec}, [{line, 1000}, exit_status, hide]), + {ok, Port} + end. terminate(_Reason, _Server) -> ok. +handle_call({ft_query, _Database, _QueryText}, _From, none) -> + {reply, {error, no_full_test_query_specified_in_config}, none}; handle_call({ft_query, Database, QueryText}, _From, Port) -> - %% send the database name + % send the database name true = port_command(Port, Database ++ "\n"), true = port_command(Port, QueryText ++ "\n"), case get_line(Port) of @@ -63,7 +73,9 @@ get_line(Port) -> receive {Port, {data, {eol, Line}}} -> Line; - ?ERR_HANDLE + % would love to use ?ERR_HANDLE here, but edoc doesn't like it. + % TODO: find a way to skip that. + {Port, {exit_status, Status}} -> {stop, {unknown_error, Status}, {unknown_error, Status}, Port} end. handle_cast(_Whatever, State) -> diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl index 82b09c72..51959d7d 100644 --- a/src/couchdb/couch_httpd.erl +++ b/src/couchdb/couch_httpd.erl @@ -13,7 +13,7 @@ -module(couch_httpd). -include("couch_db.hrl"). --export([start_link/3, stop/0, handle_request/2]). +-export([start_link/0, stop/0, handle_request/1, handle_request/2]). % Maximum size of document PUT request body (4GB) -define(MAX_DOC_SIZE, (4*1024*1024*1024)). @@ -38,18 +38,41 @@ group_level = 0 }). -start_link(BindAddress, Port, DocumentRoot) -> +start_link() -> + % read config and register for configuration changes + + % just stop if one of the config settings change. couch_server_sup + % will restart us and then we will pick up the new settings. + + BindAddress = couch_config:get({"HTTPd", "BindAddress"}, any), + Port = couch_config:get({"HTTPd", "Port"}, "5984"), + DocumentRoot = couch_config:get({"HTTPd", "DocumentRoot"}, "../../share/www"), + + % and off we go Loop = fun (Req) -> apply(couch_httpd, handle_request, [Req, DocumentRoot]) end, - mochiweb_http:start([ + {ok, Pid} = mochiweb_http:start([ {loop, Loop}, {name, ?MODULE}, {ip, BindAddress}, {port, Port} - ]). + ]), + ok = couch_config:register( + fun({"HTTPd", "BindAddress"}) -> + ?MODULE:stop(); + ({"HTTPd", "Port"}) -> + ?MODULE:stop(); + ({"HTTPd", "DocumentRoot"}) -> + ?MODULE:stop() + end, Pid), + + {ok, Pid}. stop() -> mochiweb_http:stop(?MODULE). +handle_request(config_change) -> + stop(). + handle_request(Req, DocumentRoot) -> % alias HEAD to GET as mochiweb takes care of stripping the body Method = case Req:get(method) of @@ -87,7 +110,7 @@ handle_request(Req, DocumentRoot) -> Path, Resp:get(code) ]). - + handle_request(Req, DocumentRoot, Method, Path) -> % Start = erlang:now(), X = handle_request0(Req, DocumentRoot, Method, Path), @@ -112,6 +135,8 @@ handle_request0(Req, DocumentRoot, Method, Path) -> ] ++ server_header(), <<>>})}; "/_utils/" ++ PathInfo -> {ok, Req:serve_file(PathInfo, DocumentRoot, server_header())}; + "/_config/" ++ Config -> + handle_config_request(Req, Method, {config, Config}); "/_" ++ _Path -> throw({not_found, unknown_private_path}); "/favicon.ico" -> @@ -771,7 +796,6 @@ handle_attachment_request(Req, Method, _DbName, Db, DocId, FileName) handle_attachment_request(_Req, _Method, _DbName, _Db, _DocId, _FileName) -> throw({method_not_allowed, "GET,HEAD,DELETE,PUT"}). - extract_header_rev(Req) -> QueryRev = proplists:get_value("rev", Req:parse_qs()), Etag = case Req:get_header_value("If-Match") of @@ -787,6 +811,69 @@ extract_header_rev(Req) -> throw({bad_request, "Document rev and etag have different values"}) end. +% Config request handlers + +handle_config_request(_Req, Method, {config, Config}) -> + [Module, Key] = string:tokens(Config, "/"), + handle_config_request(_Req, Method, {[Module, Key]}); + + +% PUT /_config/Module/Key +% "value" +handle_config_request(_Req, 'PUT', {[Module, Key]}) -> + handle_config_request(_Req, 'POST', {[Module, Key]}); + +% POST,PUT /_config/Module/Key +% "value" + +handle_config_request(Req, 'POST', {[Module, Key]}) -> + Value = binary_to_list(Req:recv_body()), + ok = couch_config:store({Module, Key}, Value), + send_json(Req, 200, {obj, [ + {ok, true}, + {module, Module}, + {key, Key}, + {value, Value} + ]}); + +% GET /_config/Module/Key +handle_config_request(Req, 'GET', {[Module, Key]}) -> + case couch_config:get({Module, Key},null) of + null -> + throw({not_found, unknown_config_value}); + Value -> + send_json(Req, 200, {obj, [ + {ok, true}, + {module, Module}, + {key, Key}, + {value, Value} + ]}) + end; + + +% DELETE /_config/Key +handle_config_request(Req, 'DELETE', {[Module, Key]}) -> + case couch_config:get({Module, Key}, null) of + null -> + throw({not_found, unknown_config_value}); + OldValue -> + couch_config:unset({Module, Key}), + send_json(Req, 200, {obj, [ + {ok, true}, + {module, Module}, + {key, Key}, + {old_value, OldValue} + ]}) + end. + + +% TODO: +% POST,PUT /_config/ +% [{Key, Value}, {K2, V2}, {K3, V3}] +% +% POST,PUT/_config/Key?value=Value + + % View request handling internals reverse_key_default(nil) -> <<>>; diff --git a/src/couchdb/couch_log.erl b/src/couchdb/couch_log.erl index 95ddc47f..0c2f9df7 100644 --- a/src/couchdb/couch_log.erl +++ b/src/couchdb/couch_log.erl @@ -13,7 +13,7 @@ -module(couch_log). -behaviour(gen_event). --export([start_link/2,stop/0]). +-export([start_link/0,stop/0]). -export([debug_on/0,info_on/0,get_level/0,get_level_integer/0, set_level/1]). -export([init/1, handle_event/2, terminate/2, code_change/3, handle_info/2, handle_call/2]). @@ -34,15 +34,29 @@ level_atom(?LEVEL_DEBUG) -> debug; level_atom(?LEVEL_TMI) -> tmi. -start_link(Filename, Level) -> - couch_event_sup:start_link({local, couch_log}, error_logger, couch_log, {Filename, Level}). +start_link() -> + couch_event_sup:start_link({local, couch_log}, error_logger, couch_log, []). stop() -> couch_event_sup:stop(couch_log). -init({Filename, Level}) -> +init([]) -> + % read config and register for configuration changes + + % just stop if one of the config settings change. couch_server_sup + % will restart us and then we will pick up the new settings. + ok = couch_config:register( + fun({"Log", "File"}) -> + ?MODULE:stop(); + ({"Log", "Level"}) -> + ?MODULE:stop() + end), + + Filename = couch_config:get({"Log", "File"}, "couchdb.log"), + Level = couch_config:get({"Log", "Level"},"info"), + {ok, Fd} = file:open(Filename, [append]), - {ok, {Fd, level_integer(Level)}}. + {ok, {Fd, level_integer(list_to_atom(Level))}}. debug_on() -> get_level_integer() =< ?LEVEL_DEBUG. diff --git a/src/couchdb/couch_query_servers.erl b/src/couchdb/couch_query_servers.erl index ffacbc26..0160e8c7 100644 --- a/src/couchdb/couch_query_servers.erl +++ b/src/couchdb/couch_query_servers.erl @@ -13,21 +13,17 @@ -module(couch_query_servers). -behaviour(gen_server). --export([start_link/1]). +-export([start_link/0]). -export([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2,code_change/3,stop/0]). -export([start_doc_map/2, map_docs/2, stop_doc_map/1]). -export([reduce/3, rereduce/3]). --export([test/0, test/1]). +-export([test/0]). -include("couch_db.hrl"). -timeout() -> - % hardcoded 5 sec timeout per document - 5000. - -start_link(QueryServerList) -> - gen_server:start_link({local, couch_query_servers}, couch_query_servers, QueryServerList, []). +start_link() -> + gen_server:start_link({local, couch_query_servers}, couch_query_servers, [], []). stop() -> exit(whereis(couch_query_servers), close). @@ -36,6 +32,14 @@ readline(Port) -> readline(Port, []). readline(Port, Acc) -> + + case get(query_server_timeout) of + undefined -> + Timeout = list_to_integer(couch_config:get( + {"CouchDB Query Server Options", "QueryTimeout"}, "5000")), + put(timeout, Timeout); + Timeout -> ok + end, receive {Port, {data, {noeol, Data}}} -> readline(Port, [Data|Acc]); @@ -44,7 +48,7 @@ readline(Port, Acc) -> {Port, Err} -> catch port_close(Port), throw({map_process_error, Err}) - after timeout() -> + after Timeout -> catch port_close(Port), throw({map_process_error, "map function timed out"}) end. @@ -166,7 +170,21 @@ reduce(Lang, RedSrcs, KVs) -> {ok, tuple_to_list(Results)}. -init(QueryServerList) -> +init([]) -> + + % read config and register for configuration changes + + % just stop if one of the config settings change. couch_server_sup + % will restart us and then we will pick up the new settings. + + ok = couch_config:register( + fun({"CouchDB Query Server" ++ _, _}) -> + ?MODULE:stop() + end), + + QueryServerList = couch_config:lookup_match( + {{"CouchDB Query Servers", '$1'}, '$2'}, []), + {ok, {QueryServerList, []}}. terminate(_Reason, _Server) -> @@ -216,18 +234,16 @@ handle_info({Port, {exit_status, Status}}, {QueryServerList, LangPorts}) -> {noreply, {QueryServerList, lists:keydelete(Port, 2, LangPorts)}}; _ -> ?LOG_ERROR("Unknown linked port/process crash: ~p", [Port]) - end; -handle_info(_Whatever, {Cmd, Ports}) -> - {noreply, {Cmd, Ports}}. + end. code_change(_OldVsn, State, _Extra) -> {ok, State}. -test() -> - test("../js/js -f main.js"). +% test() -> +% test("../js/js -f main.js"). -test(Cmd) -> - start_link(Cmd), +test() -> + start_link(), {ok, DocMap} = start_doc_map("javascript", ["function(doc) {if (doc[0] == 'a') return doc[1];}"]), {ok, Results} = map_docs(DocMap, [#doc{body={"a", "b"}}, #doc{body={"c", "d"}},#doc{body={"a", "c"}}]), io:format("Results: ~w~n", [Results]), diff --git a/src/couchdb/couch_server.erl b/src/couchdb/couch_server.erl index b89f5abc..3fa23867 100644 --- a/src/couchdb/couch_server.erl +++ b/src/couchdb/couch_server.erl @@ -14,9 +14,9 @@ -behaviour(gen_server). -behaviour(application). --export([start/0,start/1,start/2,stop/0,stop/1]). +-export([start/0,start/1,start/2,stop/0,stop/1,restart/0]). -export([open/2,create/2,delete/1,all_databases/0,get_version/0]). --export([init/1, handle_call/3,sup_start_link/2]). +-export([init/1, handle_call/3,sup_start_link/0]). -export([handle_cast/2,code_change/3,handle_info/2,terminate/2]). -export([dev_start/0,remote_restart/0]). @@ -31,18 +31,18 @@ }). start() -> - start(""). + start(["couch.ini"]). -start(IniFile) when is_atom(IniFile) -> - couch_server_sup:start_link(atom_to_list(IniFile) ++ ".ini"); -start(IniNum) when is_integer(IniNum) -> - couch_server_sup:start_link("couch" ++ integer_to_list(IniNum) ++ ".ini"); -start(IniFile) -> - couch_server_sup:start_link(IniFile). +start(IniFiles) -> + couch_server_sup:start_link(IniFiles). start(_Type, _Args) -> start(). +restart() -> + stop(), + start(). + stop() -> couch_server_sup:stop(). @@ -63,8 +63,8 @@ get_version() -> "0.0.0" end. -sup_start_link(RootDir, Options) -> - gen_server:start_link({local, couch_server}, couch_server, {RootDir, Options}, []). +sup_start_link() -> + gen_server:start_link({local, couch_server}, couch_server, [], []). open(DbName, Options) -> gen_server:call(couch_server, {open, DbName, Options}). @@ -89,7 +89,21 @@ check_dbname(#server{dbname_regexp=RegExp}, DbName) -> get_full_filename(Server, DbName) -> filename:join([Server#server.root_dir, "./" ++ DbName ++ ".couch"]). -init({RootDir, Options}) -> +init([]) -> + % read config and register for configuration changes + + % just stop if one of the config settings change. couch_server_sup + % will restart us and then we will pick up the new settings. + + RootDir = couch_config:get({"CouchDB", "RootDirectory"}, "."), + Options = couch_config:get({"CouchDB", "ServerOptions"}, []), + Self = self(), + ok = couch_config:register( + fun({"CouchDB", "RootDirectory"}) -> + exit(Self, config_change); + ({"CouchDB", "ServerOptions"}) -> + exit(Self, config_change) + end), {ok, RegExp} = regexp:parse("^[a-z][a-z0-9\\_\\$()\\+\\-\\/]*$"), ets:new(couch_dbs_by_name, [set, private, named_table]), ets:new(couch_dbs_by_pid, [set, private, named_table]), diff --git a/src/couchdb/couch_server_sup.erl b/src/couchdb/couch_server_sup.erl index 5d4099bf..b90ec07e 100644 --- a/src/couchdb/couch_server_sup.erl +++ b/src/couchdb/couch_server_sup.erl @@ -13,35 +13,31 @@ -module(couch_server_sup). -behaviour(supervisor). --define(DEFAULT_INI, "couch.ini"). --export([start_link/1,stop/0]). +-export([start_link/1,stop/0,couch_config_start_link_wrapper/2,start_primary_services/0]). -include("couch_db.hrl"). %% supervisor callbacks -export([init/1]). -start_link(IniFilename) -> +start_link(IniFiles) -> case whereis(couch_server_sup) of undefined -> - start_server(IniFilename); + start_server(IniFiles); _Else -> {error, already_started} end. -start_server("") -> - % no ini file specified, check the command line args - IniFile = - case init:get_argument(couchini) of - {ok, [CmdLineIniFilename]} -> - CmdLineIniFilename; - _Else -> - ?DEFAULT_INI - end, - start_server(IniFile); -start_server(InputIniFilename) -> +couch_config_start_link_wrapper(IniFiles, FirstConfigPid) -> + case is_process_alive(FirstConfigPid) of + true -> + link(FirstConfigPid), + {ok, FirstConfigPid}; + false -> couch_config:start_link(IniFiles) + end. +start_server(IniFiles) -> case init:get_argument(pidfile) of {ok, [PidFile]} -> case file:write_file(PidFile, os:getpid()) of @@ -50,146 +46,124 @@ start_server(InputIniFilename) -> end; _ -> ok end, - - {ok, Cwd} = file:get_cwd(), - IniFilename = couch_util:abs_pathname(InputIniFilename), - IniBin = - case file:read_file(IniFilename) of - {ok, IniBin0} -> - IniBin0; - {error, enoent} -> - Msg = io_lib:format("Couldn't find server configuration file ~s.", [InputIniFilename]), - io:format("~s~n", [Msg]), - throw({startup_error, Msg}) + + {ok, ConfigPid} = couch_config:start_link(IniFiles), + + LogLevel = couch_config:get({"Log", "Level"}, "info"), + % announce startup + io:format("Apache CouchDB ~s (LogLevel=~s) is starting.~n", [ + couch_server:get_version(), + LogLevel + ]), + case LogLevel of + "debug" -> + io:format("Configuration Settings ~p:~n", [IniFiles]), + [io:format(" [~s] ~s=~p~n", [Module, Variable, Value]) + || {{Module, Variable}, Value} <- couch_config:all()]; + _ -> ok end, - {ok, Ini} = couch_util:parse_ini(binary_to_list(IniBin)), - - ConsoleStartupMsg = proplists:get_value({"Couch", "ConsoleStartupMsg"}, Ini, "Apache CouchDB is starting."), - LogLevel = list_to_atom(proplists:get_value({"Couch", "LogLevel"}, Ini, "error")), - DbRootDir = proplists:get_value({"Couch", "DbRootDir"}, Ini, "."), - BindAddress = proplists:get_value({"Couch", "BindAddress"}, Ini, any), - Port = proplists:get_value({"Couch", "Port"}, Ini, 5984), - DocumentRoot = proplists:get_value({"Couch", "DocumentRoot"}, Ini, "share/www"), - LogFile = proplists:get_value({"Couch", "LogFile"}, Ini, "couchdb.log"), - UtilDriverDir = proplists:get_value({"Couch", "UtilDriverDir"}, Ini, ""), - UpdateNotifierExes = proplists:get_all_values({"Couch", "DbUpdateNotificationProcess"}, Ini), - FtSearchQueryServer = proplists:get_value({"Couch", "FullTextSearchQueryServer"}, Ini, ""), - RemoteRestart = list_to_atom(proplists:get_value({"Couch", "AllowRemoteRestart"}, Ini, "false")), - MaxDbsOpen = proplists:get_value({"Couch", "MaxDbsOpen"}, Ini, 100), - ServerOptions = [{remote_restart, RemoteRestart}, {max_dbs_open, MaxDbsOpen}], - QueryServers = [{Lang, QueryExe} || {{"Couch Query Servers", Lang}, QueryExe} <- Ini], - - ChildProcesses = - [{couch_log, - {couch_log, start_link, [LogFile, LogLevel]}, + + LibDir = + case couch_config:get({"CouchDB", "UtilDriverDir"}, null) of + null -> + filename:join(code:priv_dir(couch), "lib"); + LibDir0 -> LibDir0 + end, + + ok = couch_util:start_driver(LibDir), + + BaseServices = + {{one_for_all, 10, 3600}, + [{couch_config, + {couch_server_sup, couch_config_start_link_wrapper, [IniFiles, ConfigPid]}, permanent, brutal_kill, worker, - [couch_server]}, - {couch_db_update_event, - {gen_event, start_link, [{local, couch_db_update}]}, - permanent, - 1000, - supervisor, dynamic}, - {couch_server, - {couch_server, sup_start_link, [DbRootDir, ServerOptions]}, - permanent, - brutal_kill, - worker, - [couch_server]}, - {couch_query_servers, - {couch_query_servers, start_link, [QueryServers]}, - permanent, - brutal_kill, - worker, - [couch_query_servers]}, - {couch_view, - {couch_view, start_link, [DbRootDir]}, + {couch_primary_services, + {couch_server_sup, start_primary_services, []}, permanent, - brutal_kill, - worker, - [couch_view]}, - {couch_httpd, - {couch_httpd, start_link, [BindAddress, Port, DocumentRoot]}, - permanent, - 1000, + infinity, supervisor, - [couch_httpd]} - ] ++ - lists:map(fun(UpdateNotifierExe) -> - {UpdateNotifierExe, - {couch_db_update_notifier, start_link, [UpdateNotifierExe]}, - permanent, - 1000, - supervisor, - [couch_db_update_notifier]} - end, UpdateNotifierExes) - ++ - case FtSearchQueryServer of - "" -> - []; - _ -> - [{couch_ft_query, - {couch_ft_query, start_link, [FtSearchQueryServer]}, - permanent, - 1000, - supervisor, - [couch_ft_query]}] - end, - - io:format("Apache CouchDB ~s (LogLevel=~s)~n", [couch_server:get_version(), LogLevel]), - io:format("~s~n~n", [ConsoleStartupMsg]), - - couch_util:start_driver(UtilDriverDir), + [couch_server_sup]} + ]}, + % ensure these applications are running application:start(inets), application:start(crypto), - process_flag(trap_exit, true), - StartResult = (catch supervisor:start_link( - {local, couch_server_sup}, couch_server_sup, ChildProcesses)), - - ConfigInfo = io_lib:format("Config Info ~s:~n\tCurrentWorkingDir=~s~n" ++ - "\tDbRootDir=~s~n" ++ - "\tBindAddress=~p~n" ++ - "\tPort=~p~n" ++ - "\tDocumentRoot=~s~n" ++ - "\tLogFile=~s~n" ++ - "\tUtilDriverDir=~s~n" ++ - "\tDbUpdateNotificationProcesses=~s~n" ++ - "\tFullTextSearchQueryServer=~s~n" ++ - "~s", - [IniFilename, - Cwd, - DbRootDir, - BindAddress, - Port, - DocumentRoot, - LogFile, - UtilDriverDir, - UpdateNotifierExes, - FtSearchQueryServer, - [lists:flatten(io_lib:format("\t~s=~s~n", [Lang, QueryExe])) || {Lang, QueryExe} <- QueryServers]]), - ?LOG_DEBUG("~s", [ConfigInfo]), - - case StartResult of - {ok,_} -> - % only output when startup was successful - %io:format("Find Futon, the management interface, at:~nhttp://~s:~s/_utils/index.html~n~n", [BindAddress, Port]), - io:format("Apache CouchDB has started. Time to relax.~n"); - _ -> - % Since we failed startup, unconditionally dump configuration data to console - io:format("~s", [ConfigInfo]), - ok - end, - process_flag(trap_exit, false), - StartResult. + {ok, Pid} = supervisor:start_link( + {local, couch_server_sup}, couch_server_sup, BaseServices), + + % launch the icu bridge + % just restart if one of the config settings change. + + couch_config:register( + fun({"CouchDB", "UtilDriverDir"}) -> + ?MODULE:stop() + end, Pid), + + unlink(ConfigPid), + + io:format("Apache CouchDB has started. Time to relax.~n"), + + {ok, Pid}. + +start_primary_services() -> + supervisor:start_link(couch_server_sup, + {{one_for_one, 10, 3600}, + [{couch_log, + {couch_log, start_link, []}, + permanent, + brutal_kill, + worker, + [couch_log]}, + {couch_db_update_event, + {gen_event, start_link, [{local, couch_db_update}]}, + permanent, + brutal_kill, + supervisor, + dynamic}, + {couch_server, + {couch_server, sup_start_link, []}, + permanent, + brutal_kill, + supervisor, + [couch_server]}, + {couch_query_servers, + {couch_query_servers, start_link, []}, + permanent, + brutal_kill, + supervisor, + [couch_query_servers]}, + {couch_view, + {couch_view, start_link, []}, + permanent, + brutal_kill, + supervisor, + [couch_view]}, + {couch_httpd, + {couch_httpd, start_link, []}, + permanent, + brutal_kill, + supervisor, + [couch_httpd]}, + {couch_ft_query, + {couch_ft_query, start_link, []}, + permanent, + brutal_kill, + supervisor, + [couch_ft_query]}, + {couch_db_update_notifier_sup, + {couch_db_update_notifier_sup, start_link, []}, + permanent, + brutal_kill, + supervisor, + [couch_db_update_notifier_sup]}]}). stop() -> - catch exit(whereis(couch_server_sup), normal), - couch_log:stop(). + catch exit(whereis(couch_server_sup), normal). -init(ChildProcesses) -> - {ok, {{one_for_one, 10, 3600}, ChildProcesses}}. +init(ChildSpecs) -> + {ok, ChildSpecs}. diff --git a/src/couchdb/couch_util.erl b/src/couchdb/couch_util.erl index 1d058005..83f3e9a5 100644 --- a/src/couchdb/couch_util.erl +++ b/src/couchdb/couch_util.erl @@ -13,7 +13,7 @@ -module(couch_util). -export([start_driver/1]). --export([parse_ini/1,should_flush/0, should_flush/1]). +-export([should_flush/0, should_flush/1]). -export([new_uuid/0, rand32/0, implode/2, collate/2, collate/3]). -export([abs_pathname/1,abs_pathname/2, trim/1, ascii_lower/1]). -export([encodeBase64/1, decodeBase64/1, to_hex/1]). @@ -22,17 +22,18 @@ % arbitrarily chosen amount of memory to use before flushing to disk -define(FLUSH_MAX_MEM, 10000000). -start_driver("") -> - start_driver(filename:join(code:priv_dir(couch), "lib")); start_driver(LibDir) -> + % read config and register for configuration changes + case erl_ddll:load_driver(LibDir, "couch_erl_driver") of - ok -> ok; - {error, already_loaded} -> ok; - {error, Error} -> exit(erl_ddll:format_error(Error)) + ok -> + ok; + {error, already_loaded} -> + ok = erl_ddll:reload_driver(LibDir, "couch_erl_driver"); + {error, Error} -> + exit(erl_ddll:format_error(Error)) end. - - - + new_uuid() -> to_hex(crypto:rand_bytes(16)). @@ -158,34 +159,6 @@ implode([H|T], Sep, Acc) -> % {{"Another Section", "oops"}, "\"it doesn't qet quoted strings with semis quite right"}] % -parse_ini(FileContents) -> - {ok, Lines} = regexp:split(FileContents, "\r\n|\n|\r|\032"), - {_, ParsedIniValues} = - lists:foldl(fun(Line, {AccSectionName, AccValues}) -> - case string:strip(Line) of - "[" ++ Rest -> - case regexp:split(Rest, "\\]") of - {ok, [NewSectionName, ""]} -> - {NewSectionName, AccValues}; - _Else -> % end bracket not at end, ignore this line - {AccSectionName, AccValues} - end; - ";" ++ _Comment -> - {AccSectionName, AccValues}; - Line2 -> - case regexp:split(Line2, "=") of - {ok, [_SingleElement]} -> % no "=" found, ignore this line - {AccSectionName, AccValues}; - {ok, [""|_LineValues]} -> % line begins with "=", ignore - {AccSectionName, AccValues}; - {ok, [ValueName|LineValues]} -> % yeehaw, got a line! - RemainingLine = implode(LineValues, "="), - {ok, [LineValue | _Rest]} = regexp:split(RemainingLine, " ;|\t;"), % removes comments - {AccSectionName, [{{AccSectionName, ValueName}, LineValue} | AccValues]} - end - end - end, {"", []}, Lines), - {ok, lists:reverse(ParsedIniValues)}. drv_port() -> case get(couch_drv_port) of @@ -294,5 +267,3 @@ enc(C) -> dec(C) -> 62*?st(C,43) + ?st(C,47) + (C-59)*?st(C,48) - 69*?st(C,65) - 6*?st(C,97). - - diff --git a/src/couchdb/couch_view.erl b/src/couchdb/couch_view.erl index fc304dee..ec6b16cf 100644 --- a/src/couchdb/couch_view.erl +++ b/src/couchdb/couch_view.erl @@ -13,7 +13,7 @@ -module(couch_view). -behaviour(gen_server). --export([start_link/1,fold/4,fold/5,less_json/2, start_update_loop/3, start_temp_update_loop/5]). +-export([start_link/0,fold/4,fold/5,less_json/2, start_update_loop/3, start_temp_update_loop/5]). -export([init/1,terminate/2,handle_call/3,handle_cast/2,handle_info/2,code_change/3]). -export([get_reduce_view/1, get_map_view/1,get_row_count/1,reduce_to_count/1, fold_reduce/7]). @@ -43,10 +43,8 @@ {root_dir }). -start_link(RootDir) -> - gen_server:start_link({local, couch_view}, couch_view, RootDir, []). - - +start_link() -> + gen_server:start_link({local, couch_view}, couch_view, [], []). get_temp_updater(DbName, Type, MapSrc, RedSrc) -> {ok, Pid} = gen_server:call(couch_view, {start_temp_updater, DbName, Type, MapSrc, RedSrc}), @@ -222,7 +220,15 @@ fold(#view{btree=Btree}, StartKey, Dir, Fun, Acc) -> {ok, _AccResult} = couch_btree:fold(Btree, StartKey, Dir, WrapperFun, Acc). -init(RootDir) -> +init([]) -> + % read configuration settings and register for configuration changes + RootDir = couch_config:get({"CouchDB", "RootDirectory"}), + Self = self(), + ok = couch_config:register( + fun({"CouchDB", "RootDirectory"})-> + exit(Self, config_change) + end), + couch_db_update_notifier:start_link( fun({deleted, DbName}) -> gen_server:cast(couch_view, {reset_indexes, DbName}); @@ -238,7 +244,7 @@ init(RootDir) -> process_flag(trap_exit, true), {ok, #server{root_dir=RootDir}}. -terminate(_Reason, _) -> +terminate(_Reason,_State) -> ok. |