xtdb

avi 2024-07-04T16:19:16.170289Z

šŸ‘‹ hi all, I’m trying to get XTDB v2 working locally via the standalone Docker image and I’m having some trouble with basic functionality… 🧵

avi 2024-07-04T16:19:45.301849Z

• I’ve cloned the repo so I’m running from tip of main (`9a19326c577490f9a7ec4bb1f2f7088ad3b5ab63`) • I ran docker/bin/build-standalone-image.sh • I started a container using that image • I ran xtsql (the Python script) I then sent this statement, which seemed to work:

xtdb-> INSERT INTO people (_id, name) VALUES (6, 'fred');

| systemTime                     | txId  |
|--------------------------------|-------|
| '2024-07-04T16:05:11.962697Z'  | 1309  |
But the subsequent SELECT does not work:
xtdb-> select * from people;
500 Server Error
clojure.lang.ExceptionInfo: Ingestion stopped: Cannot invoke "java.lang.Class.getName()" because the return value of "clojure.lang.IFn.invoke(Object)" is null {}
Here’s what’s showing up in the container logs:
16:17:54 | DEBUG xtdb.server | response error (class java.util.concurrent.ExecutionException): 'clojure.lang.ExceptionInfo: Ingestion stopped: Cannot invoke "java.lang.Class.getName()" because the return value of "clojure.lang.IFn.invoke(Object)" is null {}'
java.util.concurrent.ExecutionException: clojure.lang.ExceptionInfo: Ingestion stopped: Cannot invoke "java.lang.Class.getName()" because the return value of "clojure.lang.IFn.invoke(Object)" is null {}
	at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
	at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073)
	at clojure.core$deref_future.invokeStatic(core.clj:2317)
	at clojure.core$deref.invokeStatic(core.clj:2337)
	at clojure.core$deref.invoke(core.clj:2323)
	at xtdb.indexer.Indexer.awaitTx(indexer.clj:567)
	at xtdb.node.impl.Node.prepareQuery(impl.clj:132)
	at xtdb.node.impl.Node.open_sql_query(impl.clj:114)
	at xtdb.server$eval18037$fn__18038$fn__18040.invoke(server.clj:268)
	at sieppari.interceptor$eval17547$fn__17548$fn__17549.invoke(interceptor.cljc:33)
	at sieppari.core$_try.invokeStatic(core.cljc:20)
	at sieppari.core$_try.invoke(core.cljc:17)
	at sieppari.core$enter.invokeStatic(core.cljc:62)
	at sieppari.core$enter.invoke(core.cljc:49)
	at sieppari.core$execute$fn__17753.invoke(core.cljc:125)
	at sieppari.core$execute.invokeStatic(core.cljc:123)
	at sieppari.core$execute.invoke(core.cljc:117)
	at reitit.interceptor.sieppari$reify__17762.execute(sieppari.clj:18)
	at reitit.http$ring_handler$fn__16820.invoke(http.cljc:165)
	at clojure.lang.AFn.applyToHelper(AFn.java:160)
	at clojure.lang.AFn.applyTo(AFn.java:144)
	at clojure.lang.AFunction$1.doInvoke(AFunction.java:31)
	at clojure.lang.RestFn.invoke(RestFn.java:436)
	at ring.adapter.jetty9.handlers.async$_doHandle.invokeStatic(async.clj:34)
	at ring.adapter.jetty9.handlers.async$_doHandle.invoke(async.clj:22)
	at ring.adapter.jetty9.handlers.AsyncProxyHandler.doHandle(Unknown Source)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1381)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
	at ring.adapter.jetty9.handlers.AsyncProxyHandler.doScope(Unknown Source)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1303)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
	at org.eclipse.jetty.server.Server.handle(Server.java:563)
	at org.eclipse.jetty.server.HttpChannel$RequestDispatchable.dispatch(HttpChannel.java:1598)
	at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:753)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:501)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:287)
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
	at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
	at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:421)
	at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:390)
	at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:277)
	at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:199)
	at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)
	at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: clojure.lang.ExceptionInfo: Ingestion stopped: Cannot invoke "java.lang.Class.getName()" because the return value of "clojure.lang.IFn.invoke(Object)" is null
	at xtdb.await$__GT_ingester_ex.invokeStatic(await.clj:18)
	at xtdb.await$__GT_ingester_ex.invoke(await.clj:17)
	at xtdb.await$await_tx_async$fn__23993.invoke(await.clj:31)
	at xtdb.await$await_tx_async.invokeStatic(await.clj:26)
	at xtdb.await$await_tx_async.invoke(await.clj:22)
	at xtdb.indexer.Indexer.awaitTx(indexer.clj:678)
	... 46 common frames omitted
Caused by: java.lang.NullPointerException: Cannot invoke "java.lang.Class.getName()" because the return value of "clojure.lang.IFn.invoke(Object)" is null
	at xtdb.trie$__GT_iid.invokeStatic(trie.clj:41)
	at xtdb.trie$__GT_iid.invoke(trie.clj:33)
	at xtdb.indexer$$reify__32668.indexOp(indexer.clj:327)
	at xtdb.indexer$query_indexer$eval_query$reify__32723.accept(indexer.clj:399)
	at xtdb.operator.project.ProjectCursor$reify__27625.accept(project.clj:130)
	at xtdb.operator.rename.RenameCursor$reify__28434.accept(rename.clj:39)
	at xtdb.operator.table.TableCursor$fn__28775.invoke(table.clj:37)
	at xtdb.operator.table.TableCursor.tryAdvance(table.clj:35)
	at xtdb.operator.rename.RenameCursor.tryAdvance(rename.clj:29)
	at xtdb.operator.project.ProjectCursor.tryAdvance(project.clj:116)
	at xtdb.query$wrap_cursor$reify__32484.tryAdvance(query.clj:78)
	at java.base/java.util.Spliterator.forEachRemaining(Spliterator.java:332)
	at xtdb.indexer$query_indexer$eval_query__32722.invoke(indexer.clj:396)
	at xtdb.indexer$wrap_sql_args$fn__32729.invoke(indexer.clj:419)
	at xtdb.indexer$foreach_arg_row.invokeStatic(indexer.clj:403)
	at xtdb.indexer$foreach_arg_row.invokePrim(indexer.clj)
	at xtdb.indexer$$reify__32734.indexOp(indexer.clj:401)
	at xtdb.indexer.Indexer$fn__32784$index_tx_ops__32787$fn__32800.invoke(indexer.clj:622)
	at clojure.lang.AFn.call(AFn.java:18)
	at io.micrometer.core.instrument.AbstractTimer.recordCallable(AbstractTimer.java:175)
	at xtdb.indexer.Indexer$fn__32784$index_tx_ops__32787.invoke(indexer.clj:619)
	at xtdb.indexer.Indexer$fn__32784$fn__32808.invoke(indexer.clj:633)
	at xtdb.indexer.Indexer$fn__32784.invoke(indexer.clj:632)
	at xtdb.indexer.Indexer.indexTx(indexer.clj:596)
	at xtdb.log.watcher$watch_log_BANG_$reify__33854.acceptRecord(watcher.clj:53)
	at xtdb.log$tx_handler$fn__11137.invoke(log.clj:37)
	at clojure.core.protocols$fn__8249.invokeStatic(protocols.clj:168)
	at clojure.core.protocols$fn__8249.invoke(protocols.clj:124)
	at clojure.core.protocols$fn__8204$G__8199__8213.invoke(protocols.clj:19)
	at clojure.core.protocols$seq_reduce.invokeStatic(protocols.clj:31)
	at clojure.core.protocols$fn__8236.invokeStatic(protocols.clj:75)
	at clojure.core.protocols$fn__8236.invoke(protocols.clj:75)
	at clojure.core.protocols$fn__8178$G__8173__8191.invoke(protocols.clj:13)
	at clojure.core$reduce.invokeStatic(core.clj:6886)
	at clojure.core$reduce.invoke(core.clj:6868)
	at xtdb.log.NotifyingSubscriberHandler$fn__11170.invoke(log.clj:96)
	at clojure.lang.AFn.run(AFn.java:22)
	... 1 common frames omitted
…what am I missing?

refset 2024-07-04T17:05:07.739219Z

Hey @aviflax I don't recognise that error, we'll take a look. Thanks for sharing the full stack trace. Did you have success using the published image at least?

avi 2024-07-04T17:18:04.413379Z

Thanks Jeremy! That sounds great. > Thanks for sharing the full stack trace. My pleasure. > Did you have success using the published image at least? I don’t think so; I did try it at first and had a problem, which is when I checked how recent the image was and it seemed pretty old. That’s when I thought I’d try building the image from source with the latest code. (I probably should have just written a Dockerfile that would download the latest snapshot JAR from somewhere (?) and run it when started, but, I dunno, this is what I did.) In a few minutes I’ll try switching back to the publicly available image from and I’ll let you know what happens.

avi 2024-07-04T17:25:50.320639Z

Interesting… I switched back, and did a fresh pull, created a new container, etc — and now the simple statements are indeed working. However, now I’m running into my original problem again, which is that my slightly more complex statements are not working. And not only that — they seem to be putting the DB into a failure state! This is my insert statement: insert into partners (_id, name, default_location_id, partner_types) values ("89355da0-5841-e4e0-fd7f-328877cb603e", "Aptera", "2fda6433-609e-cb05-27e0-37cf5ef213f8", ["vendor", "customer"]); that seems to work; it returns:

| systemTime                     | txId  |
|--------------------------------|-------|
| '2024-07-04T17:20:53.314482Z'  | 1173  |
but then a subsequent select * from partner; fails — *as does* select * from people; which seems, you know, not great. The response to both is:
500 Server Error
clojure.lang.ExceptionInfo: Ingestion stopped: Cannot invoke "java.lang.Class.getName()" because the return value of "clojure.lang.IFn.invoke(Object)" is null {}
Which I’ll assume has more or less the same stack trace as above. Since I’m new to XTDB, I wonder if you were to review my insert statement — at a glance, does there seem to be something obviously problematic about it? Thanks!

refset 2024-07-04T17:27:28.869489Z

Thanks for the confirmations. Your latest issue looks to be your use of double-quotes - in XTDB we follow the SQL spec very strictly here (e.g. unlike MySQL), I think you want to use single quotes across that insert statement instead

1
refset 2024-07-04T17:28:07.527949Z

i.e. insert into partners (_id, name, default_location_id, partner_types) values ('89355da0-5841-e4e0-fd7f-328877cb603e', 'Aptera', '2fda6433-609e-cb05-27e0-37cf5ef213f8', ['vendor', 'customer']);

refset 2024-07-04T17:29:27.285259Z

In general though "ingestion stopped" without any kind of rollback is bad UX (you have no choice but to start with a fresh db - apologies!) and we are still working through the error handling flows on this front

šŸ‘ 1
avi 2024-07-04T17:30:04.669009Z

That all makes a ton of sense. Thank you!!!

šŸ™ 1
avi 2024-07-04T17:44:50.566789Z

BTW, quick follow-up… I think part of why I ended up using double-quotes accidentally in my SQL statement is because in my application code, my db client, I have a JSON object, and really all I want to do is save that JSON object to the db. So it’d be really convenient if there were an API that accepted an id and a JSON object as a document. In lieu of that, I’m building a SQL statement with a StringBuilder, which is… well, let’s say, at the very least it’s inelegant. My impression is that, at least for now, XTDB has no such API. Unless… maybe I’m missing something?

refset 2024-07-04T17:53:40.976249Z

That's right, unfortunately there's no "here's some arbitrary JSON" SQL function...yet. Would supplying the JSON as a string input to a function be acceptable for you?

avi 2024-07-04T17:55:12.597869Z

> That's right, unfortunately there's no "here's some arbitrary JSON" SQL function...yet. OK, thanks, that’s what I thought but the confirmation is helpful. > Would supplying the JSON as a string input to a function be acceptable for you? Yes, absolutely. In a way, I guess that’s kinda what the function I’m writing does.

avi 2024-07-04T17:56:23.324879Z

BTW I’m currently writing Kotlin code to use the (v2) Web API directly because I couldn’t get the Kotlin driver library to work. (I’m also new to Kotlin šŸ˜… but I know Java so I thought ā€œhow hard could it beā€). It would be incredibly helpful if you’d publish some full, working examples of using your driver libraries somewhere. I did a bunch of searches in multiple places (GitHub, Discourse, Slack, the docs, the repo, the Web) but couldn’t find any.

šŸ‘ 1
refset 2024-07-04T18:16:00.032969Z

Oh interesting! So, if you want to be at the real bleeding edge with Kotlin and XT2, you could try using our pgwire interface. We're working closely with one of our Design Partner users on this currently, and they're using https://github.com/cashapp/sqldelight (in case that approach is of interest!)

avi 2024-07-04T18:17:15.435919Z

Thanks! I’ll take a look.

avi 2024-07-04T19:03:13.545429Z

@taylor.jeremydavid sorry to keep bothering you but… I’m not quite getting this working yet. Can you see anything wrong with this statement?

insert into partners (_id, category_refs, name, default_location_id, partner_group_refs, partner_types)
values ('876a7d0e-f087-4dd0-9d2a-699999925e8b', {}, '\u001a\u0011', '4baf6d8f-f99b-3300-98c4-25d571373ae1', [{'id':'876a7d0e-f087-4dd0-9d2a-699999925e8b'}], ['vendor','customer']);
…it seems to succeed, but a subsequent select * from partners returns No data returned.

Oliver Marshall 2024-07-04T19:48:19.731309Z

@aviflax FYI, a helpful debugging tool can be querying the _txs table. Transactions might be submitted but not necessarily have been committed successfully as in this case.

avi 2024-07-04T19:52:28.931509Z

Aha that’s great help — thank you!

Oliver Marshall 2024-07-04T19:53:45.968109Z

In this case, the error in _txs isn't so helpful. But to fix it just make the key in the map a symbol not a string like so: {id: ...}

āœ… 1
avi 2024-07-04T19:54:10.997039Z

Thanks! I thought I tried that, but I will double check.