clojure-dev

Issues: https://clojure.atlassian.net/browse/CLJ | Guide: https://insideclojure.org/2015/05/01/contributing-clojure/
souenzzo 2021-02-16T15:01:57.121100Z

There is some issue about (name nil) => NPE ? On REPL it not occur, but in my app, when I do (name nil), it throws with an empty stacktrace.

2021-02-16T15:02:44.121500Z

it happens in my REPL

(ins)user=> (name nil)
Execution error (NullPointerException) at user/eval144 (REPL:1).
null

bronsa 2021-02-16T15:04:08.121700Z

it should always NPE

souenzzo 2021-02-16T15:04:35.121900Z

But (.printStackTrace *e) should show a stack for you From my cloudwatch

:cause nil
:via
[{:type clojure.lang.ExceptionInfo
:message "java.lang.NullPointerException in Interceptor :my-cool-route - "
:data {:execution-id 123, :stage :enter, :interceptor :my-cool-route, :exception-type :java.lang.NullPointerException, :exception #error {
:cause nil
:via
[{:type java.lang.NullPointerException
:message nil}]
:trace
[]}}
:at [io.pedestal.interceptor.chain$throwable__GT_ex_info invokeStatic "chain.clj" 35]}
{:type java.lang.NullPointerException
:message nil}]
:trace
Not sure if it's something with pedestal error-handling

bronsa 2021-02-16T15:04:44.122300Z

if the problem are the empty stack traces, it's because of

OmitStackTraceInFastThrow
which is enabled by default

2021-02-16T15:04:47.122400Z

right- is that the objection, that it doesn't print the whole trace?

bronsa 2021-02-16T15:04:59.122800Z

you can set

-XX:-OmitStackTraceInFastThrow
to disable it

souenzzo 2021-02-16T15:05:00.122900Z

:trace [] ???

💡 1
souenzzo 2021-02-16T15:05:25.123600Z

should I use -XX:-OmitStackTraceInFastThrow in prod?

bronsa 2021-02-16T15:06:43.124700Z

well, it will cost you a very marginal performance hit as you're disabling an optimisation

bronsa 2021-02-16T15:06:57.125100Z

but depending on what you're deploying it may make no noticeable difference

2021-02-16T15:10:33.125600Z

I've definitely lost a lot of time from hard to reproduce prod errors with elided stack traces

➕ 1
alexmiller 2021-02-16T15:11:14.125900Z

many people do set this in prod

alexmiller 2021-02-16T15:11:57.126800Z

seems like it should only be a cost if you are throwing a lot of exceptions (in which case you might want to know why that is :)

bronsa 2021-02-16T15:13:03.126900Z

maybe using core.match in a hot loop :P

2021-02-16T15:15:21.129100Z

If some code decided to use JVM exceptions for normal expected case control flow, catching them internally, that could have a performance degradation, but hopefully there are very few libraries that use exceptions that way

✔️ 1
2021-02-16T16:30:43.129800Z

It seems I’ve only ever seen NPE exceptions affected by the OmitStackTraceInFastThrow default

2021-02-17T17:25:02.162700Z

Interesting to know. thanks!

2021-02-16T16:30:55.130100Z

which is interesting. I’d think it’d apply to more cases

2021-02-16T16:31:02.130300Z

In practice though, somehow I’ve never seen it

2021-02-16T16:34:42.130400Z

at one point there were common jvm internals that worked that way, but I would be surprised if that wasn't optimized

2021-02-16T16:45:20.130600Z

I too recall finding some potentially hotter paths I thought were going to use try/catch logic for control flow in the core. I can’t remember specifics now. I actually assumed some of that hasn’t likely changed.

2021-02-16T16:46:54.130800Z

Some class loader logic is based on catching ClassNotFoundException I believe - but perhaps that isn’t considered a “hot path”

dominicm 2021-02-16T16:58:09.131Z

I'm pretty sure a lot of this was optimized

dominicm 2021-02-16T16:58:15.131200Z

they're nowhere near as expensive as they once were.

2021-02-16T16:59:02.131400Z

@mikerod I'm trying to think of a case where I'd load new classes in a loop as the most perf critical part of my program

2021-02-16T16:59:06.131600Z

😄

2021-02-16T16:59:49.131800Z

I guess if my program is meant to be short lived, that would in fact be the case, but then why am I even using clojure on the jvm in that case...

seancorfield 2021-02-16T17:07:28.132100Z

If your process is very long-lived, you can hit the stacktrace optimization fairly quickly even if you're only dealing with occasional exceptions. We have that optimization disabled in production for that reason -- and I disable it in dev too because my REPL tends to run for days so I hit that optimization in the REPL too (my main work REPL has "only" been running since last Thursday, but my current open source project REPL has been running for over two weeks at this point!).

👍 2
2021-02-16T17:12:48.132300Z

Yeah @noisesmith I think the general advice of it’s probably fine to disable OmitStackTraceInFastThrow makes sense to me too. I think Alex basically said it, but it’s probably one of those things that would make more sense not optimized until/unless you actually find some unavoidable issue. I find that it’s weird that it’s on by default. That said, all production app’s I’ve ran have always left the default on - and we’ve had a bit of pain before with these empty stacks in our logs. 💀

alexmiller 2021-02-16T17:55:08.132600Z

it might make sense for Clojure CLI to set this when starting a repl

2021-02-16T17:59:19.132800Z

If Clojure CLI doesn't do that by default, then this certainly seems like a strong example motivating some kind of "alias that is enabled by default" in deps.edn, or something similar.

alexmiller 2021-02-16T18:06:17.133Z

well, could just be hardcoded, doesn't necessarily need that

alexmiller 2021-02-16T18:11:11.133200Z

Quote from release notes about this flag: "The compiler in the server VM now provides correct stack backtraces for all "cold" built-in exceptions. For performance purposes, when such an exception is thrown a few times, the method may be recompiled. After recompilation, the compiler may choose a faster tactic using preallocated exceptions that do not provide a stack trace. To disable completely the use of preallocated exceptions, use this new flag: -XX:-OmitStackTraceInFastThrow." which is kind of interesting.

alexmiller 2021-02-16T18:12:44.133400Z

so first, they "bake in" prebuilt stacktraces for built in exceptions! and second, once a particular exception has been thrown enough, it's only on recompilation that the compiler chooses the faster omitted stack throw.

alexmiller 2021-02-16T18:13:15.133600Z

which implies a lot more nuanced implementation than what I pictured in my head

seancorfield 2021-02-16T18:21:18.135700Z

Coming from @alexmiller’s comment in a thread on the 1.10.3-rc1 announcement: when I talk to tooling maintainers, they pretty much all say that prepl is too limiting for them to use -- because you can't interrupt evaluation and you can't "partially consume" a potentially infinite sequence.

seancorfield 2021-02-16T18:25:28.137800Z

I'd love to see more tooling based on just plain socket REPL or prepl but it seems that without those features, tooling maintainers are mostly going to stick with nREPL. Even in Chlorine/Clover -- which can connect to a plain socket REPL -- it side-loads https://github.com/Unrepl/unrepl so that it can provide both of those features, but that also adds a lot of complexity that I know Mauricio finds frustrating.

seancorfield 2021-02-16T18:30:38.141100Z

I know Rich favors a simple, streaming REPL -- he's mentioned that several times, both in person, and on the Clojure mailing list -- but it seems that controlling long-running evaluation/printing is an important feature for developers, which seems somewhat at odds with socket REPL/prepl. Has any thought been given to addressing that inside in a future release of Clojure? I think Alex has talked about making it easier to get to an editor-connected-REPL state so it sounds like something is in the hammock, at least?

2021-02-16T18:33:13.141200Z

I know you are just relaying stuff others have said, but isn't it wild to refer to a tool that bottoms out at calling eval as too limitting?

😄 1
seancorfield 2021-02-16T18:38:52.141800Z

🙂 Well, only insofar as you lose control of things if the evaluation/printing "hangs" (runs too long, never completes). Personally, I find unrepl's "helpful" attempts to limit rendering of values more of a nuisance since it doesn't expand enough of a large returned results -- but at least it protects me from accidental infinite sequences or hung evaluation.

2021-02-16T18:44:38.142Z

I think I had a discussion, maybe with ghadi, in this channel a while back about interruptable eval. It is kind of a rock and a hard place. Users really want it, but all the jvm underlying methods to support are marked with things like "deprecated, don't use, very bad"

2021-02-16T18:48:06.142200Z

it is possible to interrupt a prepl eval though, just open another prepl, find the thread call that hideous .stop method on it

2021-02-16T18:48:25.142400Z

and run away before anything finds out it was you

😄 1
2021-02-16T18:49:14.142600Z

so the usage pattern becomes: open a prepl, ask it for its thread id, then send it stuff to eval, and if you need to interrupt it you have the id

seancorfield 2021-02-16T18:52:23.142800Z

That's an interesting idea... and I guess handling infinite printing just through the usual print length/depth stuff?

2021-02-16T18:53:14.143Z

I dunno, there are some nrepl tickets that discussed that showed issues with the length and depth settings, I think the solution there was just printing to a fixed length buffer

2021-02-16T18:53:57.143200Z

which like, you can use a repl to define a function for that, and then launch a new repl server (in the same process) that serves repls with your new printing function

2021-02-16T18:54:55.143400Z

I think to some degree this is tool writers see an existing fixed pattern for how to do this in nrepl, and not wanting to change anything, asking for the same pattern to be available in prepl

2021-02-16T18:55:26.143600Z

I, of course, am not the man in the arena, so I may be missing a lot

seancorfield 2021-02-16T18:56:32.143800Z

I guess you could even set up a new connection for every evaluation as long as you closed out old connections as results came in or got killed...

jumar 2021-02-16T19:24:44.145200Z

There are about 5 exception types that are optimised this way. Another common is ClassCastException

👍 1
thheller 2021-02-16T20:25:56.147500Z

Is this really something that needs to be in "a future release of Clojure"? I mean this can be done perfectly fine in a library. IMHO prepl should have been a library too but oh well

seancorfield 2021-02-16T20:41:11.150200Z

@thheller It was more a question of "is this primitive considered sufficient/complete and might it get future enhancements?" -- I only know of two community projects that used prepl: Reveal and Conjure and the latter has migrated away from it because of these limitations. My understanding of it appearing in core was that there was an expectation more tooling would be built on top of it, but since that isn't happening I'm wondering if it needs to be enhanced in core for it to gain adoption.

alexmiller 2021-02-16T20:50:16.150800Z

the benefit of both prepl and the socket server being "in the box" means you can rely on always having them available

alexmiller 2021-02-16T20:51:19.151800Z

which means you can add some Java properties to your clojure program, without touching the program itself, and get access to it externally

alexmiller 2021-02-16T20:52:52.152800Z

at any rate, that's why they are not libraries

thheller 2021-02-16T20:54:51.153100Z

makes sense given how small they are I guess

alexmiller 2021-02-16T20:57:58.154300Z

I think it's a good long range bet that prepl will continue to receive attention. hard to say if it's a good short to medium range bet.

seancorfield 2021-02-16T21:02:27.155Z

Thanks @alexmiller -- I hope that long range attention includes thinking about interruptible execution 🙂

2021-02-16T21:03:06.155100Z

https://gist.github.com/1789849d21be38310694dbf214d60d34 is an example, it sends @(promise) to a prepl, and then kills that after a second

seancorfield 2021-02-16T21:09:30.155300Z

That's not too bad. I can see building something in front of that which always keeps an execution socket process open and a control socket process that can be used to kill/restart the current execution process as a prepl proxy for it...

2021-02-16T21:12:39.155500Z

it is basically shifting the machinery from the server into the client