I'm having issues integrating with Firestore in the Google App Engine Standard environment (using Java8, Kotlin and Ktor). The Firestore client hangs when I attempt to perform any operation. It works fine when running in the dev appserver but doesn't when running in production.
I'm running:
- Ktor 1.1.2
- Kotlin 1.3.21
- com.google.cloud:google-cloud-firestore (version 0.81.0-beta)
- Firestore in native mode
I've tested with this simple route:
get("/test") {
try {
val firestore = FirestoreOptions.getDefaultInstance().service
val user = firestore.collection("user").document("testusername").get().get()
call.respond(user.getString("name")!!)
} catch (ex: Exception) {
call.respond("Exception: " + ex)
}
}
Visiting /test
in a web browser causes the site to hang for a minute before a 500 error is returned.
The following exception is logged in Google Cloud Platform:
[e~website/20190306t125554.416577836925362605].<stdout>: 2019-03-06 13:01:31.306 [RequestEDA5B9B5] ERROR Application - Unhandled: GET - /test
java.lang.InterruptedException: null
at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:506)
at com.google.common.util.concurrent.AbstractFuture$TrustedFuture.get(AbstractFuture.java:83)
at com.google.common.util.concurrent.ForwardingFuture.get(ForwardingFuture.java:62)
at com.example.MainApplicationKt$run$4$2.invokeSuspend(MainApplication.kt:93)
at com.example.MainApplicationKt$run$4$2.invoke(MainApplication.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:278)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:63)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:137)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:157)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:23)
at io.ktor.routing.Routing.executeResult(Routing.kt:148)
at io.ktor.routing.Routing.interceptor(Routing.kt:29)
at io.ktor.routing.Routing$Feature$install$1.invokeSuspend(Routing.kt:93)
at io.ktor.routing.Routing$Feature$install$1.invoke(Routing.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:278)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:63)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:137)
at io.ktor.features.ContentNegotiation$Feature$install$1.invokeSuspend(ContentNegotiation.kt:63)
at io.ktor.features.ContentNegotiation$Feature$install$1.invoke(ContentNegotiation.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:278)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:63)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:137)
at io.ktor.features.CallLogging$Feature$install$2.invokeSuspend(CallLogging.kt:130)
at io.ktor.features.CallLogging$Feature$install$2.invoke(CallLogging.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:278)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:63)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:137)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:157)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:23)
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invokeSuspend(DefaultEnginePipeline.kt:106)
at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invoke(DefaultEnginePipeline.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:278)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:63)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:137)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:157)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:23)
at io.ktor.server.servlet.KtorServlet$blockingService$1.invokeSuspend(KtorServlet.kt:125)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
at kotlinx.coroutines.DispatchedKt.resumeCancellable(Dispatched.kt:410)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:25)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:160)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:52)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at io.ktor.server.servlet.KtorServlet.blockingService(KtorServlet.kt:96)
at io.ktor.server.servlet.KtorServlet.service(KtorServlet.kt:57)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:848)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1772)
at com.google.apphosting.utils.servlet.JdbcMySqlConnectionCleanupFilter.doFilter(JdbcMySqlConnectionCleanupFilter.java:60)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:582)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:524)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
at com.google.apphosting.runtime.jetty9.ParseBlobUploadHandler.handle(ParseBlobUploadHandler.java:119)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1182)
at com.google.apphosting.runtime.jetty9.AppEngineWebAppContext.doHandle(AppEngineWebAppContext.java:183)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:512)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1112)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at com.google.apphosting.runtime.jetty9.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:296)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
at org.eclipse.jetty.server.Server.handle(Server.java:539)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:333)
at com.google.apphosting.runtime.jetty9.RpcConnection.handle(RpcConnection.java:202)
at com.google.apphosting.runtime.jetty9.RpcConnector.serviceRequest(RpcConnector.java:81)
at com.google.apphosting.runtime.jetty9.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:123)
at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.dispatchServletRequest(JavaRuntime.java:695)
at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.dispatchRequest(JavaRuntime.java:658)
at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:628)
at com.google.apphosting.runtime.JavaRuntime$NullSandboxRequestRunnable.run(JavaRuntime.java:820)
at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:269)
at java.lang.Thread.run(Thread.java:748)
Here are the contents of my application.conf file:
ktor {
application {
modules = [com.example.MainApplicationKt.main]
}
}
I've tested:
- Enabling billing
- Creating a new project from scratch
- Using the com.google.firebase:firebase-admin package (version 6.7.0) instead (with the same result)
- Using Datastore instead (which seems to work fine but switching from Firestore would require me to move the whole project and rewrite a lot of code)