This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-01-11
Channels
- # announcements (1)
- # babashka (70)
- # beginners (8)
- # calva (5)
- # cider (9)
- # clojure (48)
- # clojure-austin (68)
- # clojure-europe (29)
- # clojure-norway (30)
- # clojure-uk (5)
- # clojuredesign-podcast (2)
- # cursive (19)
- # datomic (10)
- # emacs (11)
- # events (2)
- # exercism (4)
- # fulcro (2)
- # hyperfiddle (29)
- # introduce-yourself (2)
- # jobs-discuss (4)
- # kaocha (1)
- # leiningen (8)
- # lsp (8)
- # malli (2)
- # matcher-combinators (20)
- # nrepl (15)
- # off-topic (33)
- # reagent (7)
- # releases (4)
- # shadow-cljs (42)
- # spacemacs (6)
- # sql (6)
- # squint (10)
- # vim (3)
I'm using clojure as an embedded scripting language in a scala project, and it's generally working well-- I am able to load clojure code via scala, and call a function defined in clojure code, and parse out the returned clojure map to my domain model in scala without too much difficulty. The one thing I have not been able to do is set the namespace-- either in clojure itself or through the java api. The error I get is: "Can't change/establish root binding of: ns with set". Does anyone know what this could be?
If that fails, try pushThreadBindings
, similar to https://clojurians.slack.com/archives/C03S1KBA2/p1647456143132099?thread_ts=1647454953.184489&cid=C03S1KBA2
you have to bind the *ns*
dynamic var in the current thread to be able to use set!
on it, which in-ns
is doing
pushThreadBindings will indeed help here (since that is what binding is also doing, + pop)
The OP is evaluating clojure code, at least that is what I understood from the original post
Thank you @U2FRKM4TW and @U04V15CAJ (I use Jet constantly btw), this got me rolling. For future reference of anyone who may have a similar problem I have posted the code I came up with below. I am able to make 2 identical calls to execClojure without error, and get a correct return result, but this code is being used in a console utility where I only make one call for the life of the application, and I don't greatly care about the hygiene of the clojure environment after I've got my results, so everyone please use at your own risk:
def execClojure(
clojurePath: Path,
fullyQualifiedFnName: String,
args: AnyRef*
): Either[Throwable, AnyRef] = {
val attempt = Try {
Var.pushThreadBindings(
RT.mapUniqueKeys(RT.CURRENT_NS, RT.CURRENT_NS.deref)
)
val eval = Clojure.`var`("clojure.core", "eval")
val clojureFn = Clojure.`var`(fullyQualifiedFnName)
val clojureCode = s"(do ${os.read(clojurePath)})"
val readString = Clojure.`var`("clojure.core", "read-string")
val clojureForm = readString.invoke(clojureCode)
eval.invoke(clojureForm)
val parameterClasses = List.fill(args.size)(classOf[Object])
val clojureFnInvokeMethod =
clojureFn.getClass.getMethod("invoke", parameterClasses *)
val result = clojureFnInvokeMethod.invoke(clojureFn, args *)
Var.popThreadBindings()
result
}
attempt.toEither
}
@U06DE7HR6US If you run into other difficulties, you could also use #C015LCR9MHD for evaluation/scripting. It runs as a sandboxed environment so it doesn't pollute the global Clojure environment and it's configurable in the sense that you can plug in libraries that other can call
I think this is the main difference:
user> (def things [3 (delay 4) 5])
#'user/things
user> (map force things)
(3 4 5)
user> (map deref things)
Error printing return value (ClassCastException) at clojure.core/deref-future (core.clj:2315).
class java.lang.Long cannot be cast to class java.util.concurrent.Future...
yeah, it is the same but with that condition :
public class Delay implements IDeref, IPending{
...
static public Object force(Object x) {
return (x instanceof Delay) ?
((Delay) x).deref()
: x;
}
}
rich has considered making deref
work like that - bottoming out at returning its argument when not IDeref
I guess I might as well ask my real question then:
(def ec2-instance-id
"Is only supposed to be called when running on an EC2 instance."
(delay (EC2MetadataUtils/getInstanceId)))
(defn server-name
"Does an http request to retrieve the metadata, if running outside of an EC2 instance context,
it will fail."
[]
(try
(force ec2-instance-id)
(catch Exception _e
(log/info {:at :exception-tracker-cant-fetch-ec2-id})
;; return nil then to not include the :server-name key in Sentry config
nil)))
We're seeing a com.amazonaws.SdkClientException
bubbling up (and causing server startup to fail) in the prod logs pointing to the (force ec2-instance-id)
call.
But I don't see how this is possible β shouldn't the blanket try/catch swallow any exceptions?Maybe it has to do with the sneakyThrow in Delay deref ?
public Object deref() {
if(fn != null)
{
synchronized(this)
{
//double check
if(fn!=null)
{
try
{
val = fn.invoke();
}
catch(Throwable t)
{
exception = t;
}
fn = null;
}
}
}
if(exception != null)
throw Util.sneakyThrow(exception);
return val;
}
@U06F82LES always start with your real question π
Yes, although when I'm stuck it takes me a while to figure out what my real question is :person_in_lotus_position:
it's always the question you were working on when you became stuck (corollary of "you'll find it in the last place you look" π)
@U0739PUFQ following your lead β’ https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Delay.java#L61C14-L61C25 β’ https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Util.java#L234
apologies, the stacktrace is messed up, partial and reversed due to bad parsing
Hypothesis based on @U0739PUFQ's comment β’ The exception gets rethrown (sneakily?) as a Throwable β’ This Throwable isn't caught as an Exception Has some plausibility to it but I can't understand it
Trouble is, I can't repro in the REPL
dev=> (try (force (delay (throw (com.amazonaws.SdkClientException. "boom")))) (catch Exception _e (prn :caught)))
:caught
nil
Yeah, so something unexpected must be happening, either in Delay.delay or in the AWS SDK https://github.com/aws/aws-sdk-java/blob/1.12.416/aws-java-sdk-core/src/main/java/com/amazonaws/internal/EC2ResourceFetcher.java#L100
Could you try to log into the instance and try to retrieve the metadata manually, just to rule out any "environment" issue ? My googling returns a few results hinting that not all EC2 instances are created equal (meta-data access restrictions due to VPC, public/private ip, etc). "instance-id" seems pretty benign but worth a check.
"Failed to connect" is kinda weird because it's supposed to only access the metadata locally.
Next check could be taking a look at the env vars https://github.com/aws/aws-sdk-java/blob/31ae884d3be64298d28967508e5848680a738bd0/aws-java-sdk-core/src/main/java/com/amazonaws/util/EC2MetadataUtils.java#L442
The fact that retrieving metadata fails β and that an exception is thrown β is expected (it's a bug that I need to fix). This job is running on ECS, which does not have access to EC2 metadata. What's puzzling to me is that the try/catch form doesn't manage to catch the exception
(Sorry, should've explained that earlier)
Oops, my bad, didn't read properly π
are you running any instrumentation agents? are you sure what is running in prod is what you expect? (decompile the server_name class)
> are you sure what is running in prod is what you expect? Fairly sure, but yeah I could inspect the docker image.
Not running on JDK21 + virtual threads are you? The (1.11) synchronized block could cause the JDK to print a stack trace in case the carrier thread is pinned, not sure about the actual behavior because it depends on flag. Granted this would not explain the startup failure, it would only be a dump. Just wondering if you're very very (sorry π) sure that this is the real startup failure cause and not a smoke screen.
Nah, scratch that, your stack doesn't seem to be related to that, sorry for the noise.
You might be onto something It might be that startup isn't actually failing because of this, and that the surprise is just that the exception gets printed to the logs for some reason (but it's properly caught)
Maybe the AWS SDK is printing the exception, or something else I'm not thinking of
the failure to start up may be something else altogether β in fact I just removed the call to server-name and it's failing health checks for some other reason
thanks for your help everyone, I may have been chasing a wild goose here
(Sticking some marketing in there, give https://github.com/studistcorporation/sleepydog a try for datadog instrumentation)
I guess I might as well ask my real question then:
(def ec2-instance-id
"Is only supposed to be called when running on an EC2 instance."
(delay (EC2MetadataUtils/getInstanceId)))
(defn server-name
"Does an http request to retrieve the metadata, if running outside of an EC2 instance context,
it will fail."
[]
(try
(force ec2-instance-id)
(catch Exception _e
(log/info {:at :exception-tracker-cant-fetch-ec2-id})
;; return nil then to not include the :server-name key in Sentry config
nil)))
We're seeing a com.amazonaws.SdkClientException
bubbling up (and causing server startup to fail) in the prod logs pointing to the (force ec2-instance-id)
call.
But I don't see how this is possible β shouldn't the blanket try/catch swallow any exceptions?I'm getting this error from a graalvm native-image build in windows. From what I've read it seems like a java error found in many places. Asking here as maybe more people have run into it:
Fatal error: java.lang.UnsatisfiedLinkError: no management in system library path: C:\Users\ingy\AppData\Local\Temp\graalvm-oracle-21\bin
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2432)
at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:916)
at java.base/java.lang.System.loadLibrary(System.java:2059)