You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2021/04/18 02:27:59 UTC
[couchdb] 01/01: feat: work around get_stacktrace
deprecation/removal
This is an automated email from the ASF dual-hosted git repository.
vatamane pushed a commit to branch erlang-23-for-main
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 4cbd1de51d8a62ff9001a6e32450a96844d0af6e
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Sat Mar 13 18:00:12 2021 +0100
feat: work around get_stacktrace deprecation/removal
This patch introduces a macro and inserts it everywhere we catch errors
and then generatre a stacktrace.
So far the only thing that is a little bit ugly is that in two places,
I had to add a header include dependency on couch_db.erl where those
modules didn’t have any ties to couchdb/* before, alas. I’d be willing
to duplicate the macros in those modules, if we don’t want the include
dependency.
---
Makefile | 2 +-
rebar.config.script | 4 +--
src/chttpd/src/chttpd.erl | 40 ++++++++++------------
src/chttpd/src/chttpd_handlers.erl | 3 +-
src/chttpd/src/chttpd_stats.erl | 5 +--
src/couch/include/couch_db.hrl | 27 +++++++++++++++
src/couch/src/couch_proc_manager.erl | 3 +-
src/couch_js/src/couch_js_proc_manager.erl | 3 +-
src/couch_replicator/src/couch_replicator_job.erl | 23 ++++++-------
.../src/couch_replicator_parse.erl | 13 +++----
src/couch_views/src/couch_views_indexer.erl | 3 +-
src/couch_views/src/couch_views_updater.erl | 3 +-
src/ctrace/src/ctrace.erl | 3 +-
src/fabric/src/fabric2_db_expiration.erl | 3 +-
src/fabric/src/fabric2_index.erl | 6 ++--
src/fabric/src/fabric2_util.erl | 16 +--------
src/mango/src/mango_httpd.erl | 3 +-
17 files changed, 79 insertions(+), 81 deletions(-)
diff --git a/Makefile b/Makefile
index 7914118..e3939fd 100644
--- a/Makefile
+++ b/Makefile
@@ -187,7 +187,7 @@ exunit: export ERL_AFLAGS = -config $(shell pwd)/rel/files/eunit.config
exunit: export COUCHDB_QUERY_SERVER_JAVASCRIPT = $(shell pwd)/bin/couchjs $(shell pwd)/share/server/main.js
exunit: export COUCHDB_TEST_ADMIN_PARTY_OVERRIDE=1
exunit: couch elixir-init setup-eunit elixir-check-formatted elixir-credo
- @mix test --cover --trace $(EXUNIT_OPTS)
+ @mix test --trace $(EXUNIT_OPTS)
setup-eunit: export BUILDDIR = $(shell pwd)
setup-eunit: export ERL_AFLAGS = -config $(shell pwd)/rel/files/eunit.config
diff --git a/rebar.config.script b/rebar.config.script
index 0f40cf0..3156b09 100644
--- a/rebar.config.script
+++ b/rebar.config.script
@@ -152,7 +152,7 @@ DepDescs = [
{hyper, "hyper", {tag, "CouchDB-2.2.0-6"}},
{ibrowse, "ibrowse", {tag, "CouchDB-4.0.1-2"}},
{jaeger_passage, "jaeger-passage", {tag, "CouchDB-0.1.14-1"}},
-{jiffy, "jiffy", {tag, "CouchDB-1.0.4-1"}},
+{jiffy, "jiffy", {tag, "CouchDB-1.0.5-1"}},
{local, "local", {tag, "0.2.1"}},
{mochiweb, "mochiweb", {tag, "v2.20.0"}},
{meck, "meck", {tag, "0.8.8"}},
@@ -191,7 +191,7 @@ ErlOpts = case os:getenv("ERL_OPTS") of
end.
AddConfig = [
- {require_otp_vsn, "19|20|21|22"},
+ {require_otp_vsn, "20|21|22|23"},
{deps_dir, "src"},
{deps, lists:map(MakeDep, DepDescs ++ OptionalDeps)},
{sub_dirs, SubDirs},
diff --git a/src/chttpd/src/chttpd.erl b/src/chttpd/src/chttpd.erl
index 8567ada..135c4ec 100644
--- a/src/chttpd/src/chttpd.erl
+++ b/src/chttpd/src/chttpd.erl
@@ -278,16 +278,15 @@ before_request(HttpReq) ->
{ok, HttpReq1} = chttpd_plugin:before_request(HttpReq),
chttpd_stats:init(HttpReq1),
{ok, HttpReq1}
- catch Tag:Error ->
- {error, catch_error(HttpReq, Tag, Error)}
+ catch ?STACKTRACE(Tag, Error, Stack)
+ {error, catch_error(HttpReq, Tag, Error, Stack)}
end.
after_request(HttpReq, HttpResp0) ->
{ok, HttpResp1} =
try
chttpd_plugin:after_request(HttpReq, HttpResp0)
- catch _Tag:Error ->
- Stack = erlang:get_stacktrace(),
+ catch ?STACKTRACE(_Tag, Error, Stack)
send_error(HttpReq, {Error, nil, Stack}),
{ok, HttpResp0#httpd_resp{status = aborted}}
end,
@@ -320,8 +319,8 @@ process_request(#httpd{mochi_req = MochiReq} = HttpReq) ->
Response ->
{HttpReq, Response}
end
- catch Tag:Error ->
- {HttpReq, catch_error(HttpReq, Tag, Error)}
+ catch ?STACKTRACE(Tag, Error, Stack)
+ {HttpReq, catch_error(HttpReq, Tag, Error, Stack)}
end.
handle_req_after_auth(HandlerKey, HttpReq) ->
@@ -333,17 +332,17 @@ handle_req_after_auth(HandlerKey, HttpReq) ->
AuthorizedReq = chttpd_auth:authorize(possibly_hack(HttpReq),
fun chttpd_auth_request:authorize_request/1),
{AuthorizedReq, HandlerFun(AuthorizedReq)}
- catch Tag:Error ->
- {HttpReq, catch_error(HttpReq, Tag, Error)}
+ catch ?STACKTRACE(Tag, Error, Stack)
+ {HttpReq, catch_error(HttpReq, Tag, Error, Stack)}
end.
-catch_error(_HttpReq, throw, {http_head_abort, Resp}) ->
+catch_error(_HttpReq, throw, {http_head_abort, Resp}, _Stack) ->
{ok, Resp};
-catch_error(_HttpReq, throw, {http_abort, Resp, Reason}) ->
+catch_error(_HttpReq, throw, {http_abort, Resp, Reason}, _Stack) ->
{aborted, Resp, Reason};
-catch_error(HttpReq, throw, {invalid_json, _}) ->
+catch_error(HttpReq, throw, {invalid_json, _}, _Stack) ->
send_error(HttpReq, {bad_request, "invalid UTF-8 JSON"});
-catch_error(HttpReq, exit, {mochiweb_recv_error, E}) ->
+catch_error(HttpReq, exit, {mochiweb_recv_error, E}, _Stack) ->
#httpd{
mochi_req = MochiReq,
peer = Peer,
@@ -355,22 +354,21 @@ catch_error(HttpReq, exit, {mochiweb_recv_error, E}) ->
MochiReq:get(raw_path),
E]),
exit(normal);
-catch_error(HttpReq, exit, {uri_too_long, _}) ->
+catch_error(HttpReq, exit, {uri_too_long, _}, _Stack) ->
send_error(HttpReq, request_uri_too_long);
-catch_error(HttpReq, exit, {body_too_large, _}) ->
+catch_error(HttpReq, exit, {body_too_large, _}, _Stack) ->
send_error(HttpReq, request_entity_too_large);
-catch_error(HttpReq, throw, Error) ->
+catch_error(HttpReq, throw, Error, _Stack) ->
send_error(HttpReq, Error);
-catch_error(HttpReq, error, database_does_not_exist) ->
+catch_error(HttpReq, error, database_does_not_exist, _Stack) ->
send_error(HttpReq, database_does_not_exist);
-catch_error(HttpReq, error, decryption_failed) ->
+catch_error(HttpReq, error, decryption_failed, _Stack) ->
send_error(HttpReq, decryption_failed);
-catch_error(HttpReq, error, not_ciphertext) ->
+catch_error(HttpReq, error, not_ciphertext, _Stack) ->
send_error(HttpReq, not_ciphertext);
-catch_error(HttpReq, error, {erlfdb_error, _} = Error) ->
+catch_error(HttpReq, error, {erlfdb_error, _} = Error, _Stack) ->
send_error(HttpReq, Error);
-catch_error(HttpReq, Tag, Error) ->
- Stack = erlang:get_stacktrace(),
+catch_error(HttpReq, Tag, Error, Stack) ->
% TODO improve logging and metrics collection for client disconnects
case {Tag, Error, Stack} of
{exit, normal, [{mochiweb_request, send, _, _} | _]} ->
diff --git a/src/chttpd/src/chttpd_handlers.erl b/src/chttpd/src/chttpd_handlers.erl
index 17d2952..43631c0 100644
--- a/src/chttpd/src/chttpd_handlers.erl
+++ b/src/chttpd/src/chttpd_handlers.erl
@@ -44,8 +44,7 @@ handler_info(HttpReq) ->
Default = {'unknown.unknown', #{}},
try
select(collect(handler_info, [Method, PathParts, HttpReq]), Default)
- catch Type:Reason ->
- Stack = erlang:get_stacktrace(),
+ catch ?STACKTRACE(Type, Reason, Stack)
couch_log:error("~s :: handler_info failure for ~p : ~p:~p :: ~p", [
?MODULE,
get(nonce),
diff --git a/src/chttpd/src/chttpd_stats.erl b/src/chttpd/src/chttpd_stats.erl
index 27e9c31..ae1efa4 100644
--- a/src/chttpd/src/chttpd_stats.erl
+++ b/src/chttpd/src/chttpd_stats.erl
@@ -12,6 +12,8 @@
-module(chttpd_stats).
+% for the stacktrace macro only so far
+-include_lib("couch/include/couch_db.hrl").
-export([
init/1,
@@ -61,8 +63,7 @@ report(HttpResp) ->
_ ->
ok
end
- catch T:R ->
- S = erlang:get_stacktrace(),
+ catch ?STACKTRACE(T, R, S)
Fmt = "Failed to report chttpd request stats: ~p:~p ~p",
couch_log:error(Fmt, [T, R, S])
end.
diff --git a/src/couch/include/couch_db.hrl b/src/couch/include/couch_db.hrl
index 2289089..3f4d70a 100644
--- a/src/couch/include/couch_db.hrl
+++ b/src/couch/include/couch_db.hrl
@@ -178,3 +178,30 @@
-define(record_to_keyval(Name, Record),
lists:zip(record_info(fields, Name), tl(tuple_to_list(Record)))).
+
+%% Erlang/OTP 21 deprecates and 23 removes get_stacktrace(), so
+%% we have to monkey around until we can drop support < 21.
+%% h/t https://github.com/erlang/otp/pull/1783#issuecomment-386190970
+
+%% use like so:
+% try function1(Arg1)
+% catch
+% ?STACKTRACE(exit, badarg, ErrorStackTrace)
+% % do stuff with ErrorStackTrace
+% % ...
+% end,
+
+% for features specific to Erlang/OTP version 20.x (and later versions)
+-ifdef(ERLANG_OTP_VERSION_20).
+-else.
+-define(ERLANG_OTP_VERSION_21_FEATURES, true).
+-endif.
+% Get the stacktrace in a way that is backwards compatible
+-ifdef(ERLANG_OTP_VERSION_21_FEATURES).
+-define(STACKTRACE(ErrorType, Error, ErrorStackTrace),
+ ErrorType:Error:ErrorStackTrace ->).
+-else.
+-define(STACKTRACE(ErrorType, Error, ErrorStackTrace),
+ ErrorType:Error ->
+ ErrorStackTrace = erlang:get_stacktrace(),).
+-endif.
diff --git a/src/couch/src/couch_proc_manager.erl b/src/couch/src/couch_proc_manager.erl
index b83d788..6a0322c 100644
--- a/src/couch/src/couch_proc_manager.erl
+++ b/src/couch/src/couch_proc_manager.erl
@@ -321,8 +321,7 @@ find_proc(#client{lang = Lang, ddoc = DDoc, ddoc_key = DDocKey} = Client) ->
find_proc(Lang, Fun) ->
try iter_procs(Lang, Fun)
- catch error:Reason ->
- StackTrace = erlang:get_stacktrace(),
+ catch ?STACKTRACE(error, Reason, StackTrace)
couch_log:error("~p ~p ~p", [?MODULE, Reason, StackTrace]),
{error, Reason}
end.
diff --git a/src/couch_js/src/couch_js_proc_manager.erl b/src/couch_js/src/couch_js_proc_manager.erl
index db5c492..4b3354d 100644
--- a/src/couch_js/src/couch_js_proc_manager.erl
+++ b/src/couch_js/src/couch_js_proc_manager.erl
@@ -320,8 +320,7 @@ find_proc(#client{lang = Lang, ddoc = DDoc, ddoc_key = DDocKey} = Client) ->
find_proc(Lang, Fun) ->
try iter_procs(Lang, Fun)
- catch error:Reason ->
- StackTrace = erlang:get_stacktrace(),
+ catch ?STACKTRACE(error, Reason, StackTrace)
couch_log:error("~p ~p ~p", [?MODULE, Reason, StackTrace]),
{error, Reason}
end.
diff --git a/src/couch_replicator/src/couch_replicator_job.erl b/src/couch_replicator/src/couch_replicator_job.erl
index c8c143a..d11bdb7 100644
--- a/src/couch_replicator/src/couch_replicator_job.erl
+++ b/src/couch_replicator/src/couch_replicator_job.erl
@@ -221,12 +221,12 @@ handle_info(timeout, delayed_init) ->
{ok, State} -> {noreply, State};
{stop, Reason, State} -> {stop, Reason, State}
catch
- exit:{shutdown, Exit} when Exit =:= finished orelse Exit =:= halt ->
- Stack = erlang:get_stacktrace(),
- {stop, {shutdown, Exit}, {init_error, Stack}};
- _Tag:Error ->
+ ?STACKTRACE(exit, {shutdown, finished}, Stack)
+ {stop, {shutdown, finished}, {init_error, Stack}};
+ ?STACKTRACE(exit, {shtudown, halt}, Stack)
+ {stop, {shutdown, halt}, {init_error, Stack}};
+ ?STACKTRACE(_Tag, Error, Stack)
ShutdownReason = {error, replication_start_error(Error)},
- Stack = erlang:get_stacktrace(),
{stop, {shutdown, ShutdownReason}, {init_error, Stack}}
end;
@@ -406,16 +406,15 @@ delayed_init() ->
try do_init(Job, JobData) of
State = #rep_state{} -> {ok, State}
catch
- exit:{http_request_failed, _, _, max_backoff} ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(exit, {http_request_failed, _, _, max_backoff}, Stack)
reschedule_on_error(undefined, Job, JobData, max_backoff),
{stop, {shutdown, max_backoff}, {init_error, Stack}};
- exit:{shutdown, Exit} when Exit =:= finished orelse Exit =:= halt ->
- Stack = erlang:get_stacktrace(),
- {stop, {shutdown, Exit}, {init_error, Stack}};
- _Tag:Error ->
+ ?STACKTRACE(exit, {shutdown, finished}, Stack)
+ {stop, {shutdown, finished}, {init_error, Stack}};
+ ?STACKTRACE(exit, {shutdown, halt}, Stack)
+ {stop, {shutdown, halt}, {init_error, Stack}};
+ ?STACKTRACE(_Tag, Error, Stack)
Reason = {error, replication_start_error(Error)},
- Stack = erlang:get_stacktrace(),
ErrMsg = "~p : job ~p failed during startup ~p stack:~p",
couch_log:error(ErrMsg, [?MODULE, Job, Reason, Stack]),
reschedule_on_error(undefined, Job, JobData, Reason),
diff --git a/src/couch_replicator/src/couch_replicator_parse.erl b/src/couch_replicator/src/couch_replicator_parse.erl
index 5996ec5..79c778d 100644
--- a/src/couch_replicator/src/couch_replicator_parse.erl
+++ b/src/couch_replicator/src/couch_replicator_parse.erl
@@ -22,6 +22,7 @@
-include_lib("ibrowse/include/ibrowse.hrl").
+-include_lib("couch/include/couch_db.hrl").
-include("couch_replicator.hrl").
@@ -59,13 +60,11 @@ parse_rep_doc(RepDoc) ->
{ok, Rep} = try
parse_rep(RepDoc, null)
catch
- throw:{error, Reason} ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(throw, {error, Reason}, Stack)
LogErr1 = "~p parse_rep_doc fail ~p ~p",
couch_log:error(LogErr1, [?MODULE, Reason, Stack]),
throw({bad_rep_doc, Reason});
- Tag:Err ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(Tag, Err, Stack)
LogErr2 = "~p parse_rep_doc fail ~p:~p ~p",
couch_log:error(LogErr2, [?MODULE, Tag, Err, Stack]),
throw({bad_rep_doc, couch_util:to_binary({Tag, Err})})
@@ -83,13 +82,11 @@ parse_transient_rep(#{} = Body, UserName) ->
{ok, Rep} = try
parse_rep(Body, UserName)
catch
- throw:{error, Reason} ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(throw, {error, Reason}, Stack)
LogErr1 = "~p parse_transient_rep fail ~p ~p",
couch_log:error(LogErr1, [?MODULE, Reason, Stack]),
throw({bad_request, Reason});
- Tag:Err ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(Tag, Err, Stack)
LogErr2 = "~p parse_transient_rep fail ~p ~p",
couch_log:error(LogErr2, [?MODULE, Tag, Err, Stack]),
throw({bad_request, couch_util:to_binary({Tag, Err})})
diff --git a/src/couch_views/src/couch_views_indexer.erl b/src/couch_views/src/couch_views_indexer.erl
index 88b1ff6..2164a58 100644
--- a/src/couch_views/src/couch_views_indexer.erl
+++ b/src/couch_views/src/couch_views_indexer.erl
@@ -121,8 +121,7 @@ init() ->
ok;
error:database_does_not_exist ->
fail_job(Job, Data, db_deleted, "Database was deleted");
- Error:Reason ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(Error, Reason, Stack)
Fmt = "Error building view for ddoc ~s in ~s: ~p:~p ~p",
couch_log:error(Fmt, [DbName, DDocId, Error, Reason, Stack]),
diff --git a/src/couch_views/src/couch_views_updater.erl b/src/couch_views/src/couch_views_updater.erl
index defdb6b..7c96bb8 100644
--- a/src/couch_views/src/couch_views_updater.erl
+++ b/src/couch_views/src/couch_views_updater.erl
@@ -31,8 +31,7 @@ index(Db, #doc{id = Id, revs = Revs} = Doc, _NewWinner, _OldWinner, NewRevId,
index_int(Db, Doc, Seq)
end
catch
- error:{erlfdb_error, ErrCode} when is_integer(ErrCode) ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(error, {erlfdb_error, ErrCode}, Stack)
DbName = fabric2_db:name(Db),
couch_log:error("Mango index erlfdb error Db ~s Doc ~p ~p",
[DbName, Id, ErrCode]),
diff --git a/src/ctrace/src/ctrace.erl b/src/ctrace/src/ctrace.erl
index 5521901..8ae51c4 100644
--- a/src/ctrace/src/ctrace.erl
+++ b/src/ctrace/src/ctrace.erl
@@ -109,8 +109,7 @@ with_span(Operation, Options, Fun) ->
try
start_span(Operation, Options),
Fun()
- catch Type:Reason ->
- Stack = erlang:get_stacktrace(),
+ catch ?STACKTRACE(Type, Reason, Stack)
log(#{
?LOG_FIELD_ERROR_KIND => Type,
?LOG_FIELD_MESSAGE => Reason,
diff --git a/src/fabric/src/fabric2_db_expiration.erl b/src/fabric/src/fabric2_db_expiration.erl
index 92f22e7..94cb61b 100644
--- a/src/fabric/src/fabric2_db_expiration.erl
+++ b/src/fabric/src/fabric2_db_expiration.erl
@@ -131,8 +131,7 @@ cleanup(true) ->
{ok, Job1, Data1} = ?MODULE:process_expirations(Job, Data),
ok = resubmit_job(Job1, Data1, schedule_sec())
catch
- _Tag:Error ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(_Tag, Error, Stack)
couch_log:error("~p : processing error ~p ~p ~p",
[?MODULE, Job, Error, Stack]),
ok = resubmit_job(Job, Data, ?ERROR_RESCHEDULE_SEC),
diff --git a/src/fabric/src/fabric2_index.erl b/src/fabric/src/fabric2_index.erl
index 25c31a8..3c59cd7 100644
--- a/src/fabric/src/fabric2_index.erl
+++ b/src/fabric/src/fabric2_index.erl
@@ -67,8 +67,7 @@ cleanup(Db) ->
catch
error:database_does_not_exist ->
ok;
- Tag:Reason ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(Tag, Reason, Stack)
DbName = fabric2_db:name(Db),
LogMsg = "~p failed to cleanup indices for `~s` ~p:~p ~p",
couch_log:error(LogMsg, [?MODULE, DbName, Tag, Reason, Stack])
@@ -168,8 +167,7 @@ process_updates_iter([Db | Rest], Cont) ->
catch
error:database_does_not_exist ->
ok;
- Tag:Reason ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(Tag, Reason, Stack)
LogMsg = "~p failed to build indices for `~s` ~p:~p ~p",
couch_log:error(LogMsg, [?MODULE, Db, Tag, Reason, Stack])
end,
diff --git a/src/fabric/src/fabric2_util.erl b/src/fabric/src/fabric2_util.erl
index 7c21252..f95e410 100644
--- a/src/fabric/src/fabric2_util.erl
+++ b/src/fabric/src/fabric2_util.erl
@@ -383,23 +383,9 @@ pmap(Fun, Args, Opts) ->
end, Refs).
-% OTP_RELEASE is defined in OTP 21+ only
--ifdef(OTP_RELEASE).
-
pmap_exec(Fun, Arg) ->
try
{'$res', Fun(Arg)}
- catch Tag:Reason:Stack ->
+ catch ?STACKTRACE(Tag, Reason, Stack)
{'$err', Tag, Reason, Stack}
end.
-
--else.
-
-pmap_exec(Fun, Arg) ->
- try
- {'$res', Fun(Arg)}
- catch Tag:Reason ->
- {'$err', Tag, Reason, erlang:get_stacktrace()}
- end.
-
--endif.
diff --git a/src/mango/src/mango_httpd.erl b/src/mango/src/mango_httpd.erl
index 8d5a212..9ac4dfc 100644
--- a/src/mango/src/mango_httpd.erl
+++ b/src/mango/src/mango_httpd.erl
@@ -36,10 +36,9 @@ handle_req(#httpd{} = Req, Db) ->
try
handle_req_int(Req, Db)
catch
- throw:{mango_error, Module, Reason} ->
+ ?STACKTRACE(throw, {mango_error, Module, Reason}, Stack)
case mango_error:info(Module, Reason) of
{500, ErrorStr, ReasonStr} ->
- Stack = erlang:get_stacktrace(),
chttpd:send_error(Req, {ErrorStr, ReasonStr, Stack});
{Code, ErrorStr, ReasonStr} ->
chttpd:send_error(Req, Code, ErrorStr, ReasonStr)