Couchbase server allowing concurrent document mutation, even with CAS set? - java

I have an object, SomeObject, which represents an object stored as a document in Couchbase. SomeObject has a cas variable for containing the CAS value.
I have code like this:
/* Get two identical objects from Couchbase, they'll have identical CAS value */
SomeObject someObjectA = getSomeObjectFromCouchbase(sameId);
SomeObject someObjectB = getSomeObjectFromCouchbase(sameId);
/* Make arbitrary modifications to the objects */
someObjectA.getListInObject().add(arbitraryValue1);
someObjectB.getListInObject().add(arbitraryValue2);
/* Convert SomeObject objects to JsonDocument objects, ensuring the CAS value is set */
JsonDocument jsonDocA = JsonDocument.create(someObjectA.getId(), JsonObject.fromJson(mapper.writeValueAsString(someObjectA)), someObjectA.getCas());
JsonDocument jsonDocB = JsonDocument.create(someObjectB.getId(), JsonObject.fromJson(mapper.writeValueAsString(someObjectB)), someObjectB.getCas());
/* Perform upserts on both JsonDocument objects; expectation is the second one should fail with CASMismatchException because the CAS value should have changed after the first upsert */
couchbaseDao.getDatasource().getBucket().upsert(jsonDocA, writeTimeout, TimeUnit.MILLISECONDS);
couchbaseDao.getDatasource().getBucket().upsert(jsonDocB, writeTimeout, TimeUnit.MILLISECONDS);
Despite my expectation that the second upsert should fail with CASMismatchException, which I attempt to catch by wrapping the code in a try/catch block, it does not happen. Both upserts succeed, and the server does indeed change the CAS value after both upserts. It's as if it's not even checking the CAS value upon upsert, just blindly accepting anything and then updating the CAS value.
The end result is that the list in the Couchbase document only contains arbitraryValue2, and is missing arbitraryValue1, whereas I expected it to have arbitraryValue1 and not arbitraryValue2 (as the second upsert should have thrown CASMismatchException). Am I doing something wrong, or is something wrong with the server such that it is not dealing with CAS properly?

CAS is just used in the replace method:
JsonDocument doc = userRepository.getCouchbaseOperations().getCouchbaseBucket().get("1");
JsonDocument doc2 = userRepository.getCouchbaseOperations().getCouchbaseBucket().get("1");
doc.content().put("username", "Michael");
userRepository.getCouchbaseOperations().getCouchbaseBucket().replace(doc);
doc2.content().put("username", "denis");
userRepository.getCouchbaseOperations().getCouchbaseBucket().replace(doc2);
User userResult2 = userRepository.findById("1").get();
System.out.println(userResult2.getUsername());
If you try to execute the code above you will get the following exception:
aused by: com.couchbase.client.java.error.CASMismatchException: null
at com.couchbase.client.java.bucket.api.Mutate$3$1.call(Mutate.java:333) ~[java-client-2.7.11.jar:na]
at com.couchbase.client.java.bucket.api.Mutate$3$1.call(Mutate.java:308) ~[java-client-2.7.11.jar:na]
at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69) ~[rxjava-1.3.8.jar:1.3.8]
at rx.observers.Subscribers$5.onNext(Subscribers.java:235) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onNext(OnSubscribeDoOnEach.java:101) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.producers.SingleProducer.request(SingleProducer.java:65) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.producers.ProducerArbiter.setProducer(ProducerArbiter.java:126) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OnSubscribeTimeoutTimedWithFallback$TimeoutMainSubscriber.setProducer(OnSubscribeTimeoutTimedWithFallback.java:155) ~[rxjava-1.3.8.jar:1.3.8]
at rx.Subscriber.setProducer(Subscriber.java:205) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OnSubscribeMap$MapSubscriber.setProducer(OnSubscribeMap.java:102) ~[rxjava-1.3.8.jar:1.3.8]
at rx.Subscriber.setProducer(Subscriber.java:205) ~[rxjava-1.3.8.jar:1.3.8]
at rx.Subscriber.setProducer(Subscriber.java:205) ~[rxjava-1.3.8.jar:1.3.8]
at rx.subjects.AsyncSubject.onCompleted(AsyncSubject.java:103) ~[rxjava-1.3.8.jar:1.3.8]
at com.couchbase.client.core.endpoint.AbstractGenericHandler.completeResponse(AbstractGenericHandler.java:508) ~[core-io-1.7.11.jar:na]
at com.couchbase.client.core.endpoint.AbstractGenericHandler.access$000(AbstractGenericHandler.java:86) ~[core-io-1.7.11.jar:na]
at com.couchbase.client.core.endpoint.AbstractGenericHandler$1.call(AbstractGenericHandler.java:526) ~[core-io-1.7.11.jar:na]
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) ~[rxjava-1.3.8.jar:1.3.8]
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:835) ~[na:na]
Caused by: rx.exceptions.OnErrorThrowable$OnNextValue: OnError while emitting onNext value: com.couchbase.client.core.message.kv.ReplaceResponse.class

For the GO users here, I am using upsertSpec with CAS to figure out any parallel mutation on the doc. It does seem to work pretty well for me. Not sure about the java SDK though, Ideally behavior should be the same irrespective of language.

Related

Getting reactor.pool.PoolShutdownException during save in database

Service is using org.springframework.r2dbc.core.DatabaseClient with reactor-pool and r2dbc-mysql driver.
I'm doing inserts in the database every 5-10 seconds (50-100 insert statements) and randomly after every 2-3 hours I'm getting reactor.pool.PoolShutdownException: Pool has been shut down, what might be the reason for this behavior?
Dependecy versions:
r2dbc-pool: 0.8.8.RELEASE
r2dbc-mysql: 0.8.2
spring-r2dbc: 5.3.15
Stacktrace:
org.springframework.dao.DataAccessResourceFailureException: Failed to obtain R2DBC Connection; nested exception is reactor.pool.PoolShutdownException: Pool has been shut down
at org.springframework.r2dbc.connection.ConnectionFactoryUtils.lambda$getConnection$0(ConnectionFactoryUtils.java:88)
at reactor.core.publisher.Mono.lambda$onErrorMap$31(Mono.java:3733)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:106)
at reactor.core.publisher.FluxRetry$RetrySubscriber.onError(FluxRetry.java:95)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:172)
at reactor.pool.AbstractPool$Borrower.fail(AbstractPool.java:477)
at reactor.pool.SimpleDequePool.doAcquire(SimpleDequePool.java:264)
at reactor.pool.AbstractPool$Borrower.request(AbstractPool.java:432)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:110)
at reactor.pool.SimpleDequePool$QueueBorrowerMono.subscribe(SimpleDequePool.java:676)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
at reactor.core.publisher.FluxRetry$RetrySubscriber.resubscribe(FluxRetry.java:117)
at reactor.core.publisher.MonoRetry.subscribeOrReturn(MonoRetry.java:50)
at reactor.core.publisher.Mono.subscribe(Mono.java:4385)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:172)
at reactor.core.publisher.FluxMap$MapSubscriber.onError(FluxMap.java:132)
at reactor.core.publisher.Operators.error(Operators.java:198)
at reactor.core.publisher.MonoError.subscribe(MonoError.java:53)
at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55)
at reactor.core.publisher.Mono.subscribe(Mono.java:4400)
at reactor.core.publisher.FluxUsingWhen.subscribe(FluxUsingWhen.java:104)
at reactor.core.publisher.Flux.subscribe(Flux.java:8469)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:255)
at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51)
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:157)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
at reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:251)
at reactor.core.publisher.MonoZip$ZipInner.onNext(MonoZip.java:336)
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74)
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
at reactor.core.publisher.MonoCompletionStage.lambda$subscribe$0(MonoCompletionStage.java:83)
at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(Unknown Source)
at java.base/java.util.concurrent.CompletableFuture.uniWhenCompleteStage(Unknown Source)
at java.base/java.util.concurrent.CompletableFuture.whenComplete(Unknown Source)
at java.base/java.util.concurrent.CompletableFuture.whenComplete(Unknown Source)
at reactor.core.publisher.MonoCompletionStage.subscribe(MonoCompletionStage.java:58)
at reactor.core.publisher.Mono.subscribe(Mono.java:4400)
at reactor.core.publisher.MonoZip.subscribe(MonoZip.java:128)
at reactor.core.publisher.Mono.subscribe(Mono.java:4400)
at reactor.core.publisher.MonoZip.subscribe(MonoZip.java:128)
at reactor.core.publisher.Mono.subscribe(Mono.java:4400)
at reactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.run(MonoSubscribeOn.java:126)
at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:84)
at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:37)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Basically, it happens when you have too many pending acquired connections
Example: your connection pool is 100 but you are trying to do 500 parallel
inserts, where 400 will be in pending status).
In this situation, reactor-pool disposes connection pool. So to avoid such an issue, I’m controlling the number of parallel executions.
Actually I see two ways to handle this case:
(The right way in my opinion) Is to control the flow of incoming messages by specifying concurrency parameter on the operator (concurrency =< then pool size)
flux
//some intermediate operators
.flatMap( {databaseOperation(it) }, poolSize)
In this case, you won't have more parallel executions than your connection pool can afford.
using delayUntil operator, which delays elements until there are unused connections (you can use connection pool metrics to retrieve that). I wouldn't recommend this approach because you can end up with of memory exception if you are not controlling back-pressure, or you will have to drop some items if the buffer overflows.
Method that delays message until there are available connections
fun <T> Flux<T>.delayUntilHasAvailableConnections(pool: ConnectionPool): Flux<T> {
val hasAvailableConnections = Supplier {
val metrics = pool.metrics.get()
metrics.pendingAcquireSize() <= metrics.maxAllocatedSize
}
val connectionsExistsMono = Mono.fromSupplier(hasAvailableConnections)
val hasConnectionMono = connectionsExistsMono
.handle { hasConnections, sink: SynchronousSink<Boolean> ->
if (hasConnections) {
sink.next(hasConnections)
} else {
sink.error(RuntimeException("No Connections"))
}
}.retryWhen(Retry.fixedDelay(Long.MAX_VALUE, Duration.ofSeconds(5)))
return delayUntil { hasConnectionMono }
}
Usage:
flux
//some intermediate operators
.delayUntilHasAvailableConnections(connectionPool)
.flatMap { databaseOperation(it) }
As Vladlen pointed out, reactor-pool has the ability to some kind of refuse new connections to the pool if there is a large queue for acquiring DB connections. In case of a Spring application with spring-r2dbc, this functionality is disabled by default and all connection attempts get queued.
Nevertheless I got the same exception in my Spring application. In my case it was a more special condition but if someone else stumbles upon this: The Spring actuator health check also checks the connectivity to the database. If you have your application deployed to Kubernetes, the request to the /actuator/health endpoint may not return in time if there is a large queue in front of the DB pool. This causes the readiness-/liveness-probes of Kubernetes to fail with "Connection Timed out" making Kubernetes think the application is unhealthy (which is some kind of true). In the end, Kubernetes kills the application and all existing connections to the DB pool get terminated with the above exception.
My solution was to manually limit the load similar to what Vladlen pointed out:
val coroutineLimit = Semaphore(dbPoolSize)
workItems.forEach {
launch {
coroutineLimit.withPermit {
// My DB operation
}
}
}

What does Retry mean in context of Java Couchbase SDK?

I am using Java couchbase sdk in my application. While setting up the DefaultCouchbaseEnvironment, I came across the property RetryStrategy. Now I am using the default configuration for which the retry strategy is BestEffortRetryStrategy. According to documentation
BestEffortRetryStrategy will retry the operation until it either succeeds or the maximum request lifetime is reached
By default the maximum request lifetime is 75 seconds.
Now what i what i want to understand here is what does retry mean here. Does retry mean retrying the request whenever an exception occurs or does it mean it will retry to allocate this request to some node to process the request in case it can't and it will keep retrying for 75 seconds?
I am looking at my application logs for different exceptions to understand this and I could see that TemporaryFailureException wasn't retried and i could also see that in some instances RequestCancelledException was being thrown after 75 seconds. Is it fair to assume that couchbase retries a request to assign it to node to process it and not actually retries on any exception once it makes it to the node that will process this request?
StackTrace for TemporaryFailureException-
stackTrace: com.couchbase.client.java.error.TemporaryFailureException: null
at com.couchbase.client.java.bucket.api.Mutate$2$1.call(Mutate.java:246)
at com.couchbase.client.java.bucket.api.Mutate$2$1.call(Mutate.java:220)
at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:69)
at rx.observers.Subscribers$5.onNext(Subscribers.java:235)
at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onNext(OnSubscribeDoOnEach.java:101)
at rx.internal.producers.SingleProducer.request(SingleProducer.java:65)
at rx.Subscriber.setProducer(Subscriber.java:211)
at rx.internal.operators.OnSubscribeMap$MapSubscriber.setProducer(OnSubscribeMap.java:102)
at rx.Subscriber.setProducer(Subscriber.java:205)
at rx.internal.operators.OnSubscribeMap$MapSubscriber.setProducer(OnSubscribeMap.java:102)
at rx.Subscriber.setProducer(Subscriber.java:205)
at rx.Subscriber.setProducer(Subscriber.java:205)
at rx.subjects.AsyncSubject.onCompleted(AsyncSubject.java:103)
at com.couchbase.client.core.endpoint.AbstractGenericHandler.completeResponse(AbstractGenericHandler.java:508)
at com.couchbase.client.core.endpoint.AbstractGenericHandler.access$000(AbstractGenericHandler.java:86)
at com.couchbase.client.core.endpoint.AbstractGenericHandler$1.call(AbstractGenericHandler.java:526)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java)
at java.lang.Thread.run(Thread.java:748)
Caused by: rx.exceptions.OnErrorThrowable$OnNextValue: OnError while emitting onNext value: com.couchbase.client.core.message.kv.UpsertResponse.class
at rx.exceptions.OnErrorThrowable.addValueAsLastCause(OnErrorThrowable.java:118)
at rx.internal.operators.OnSubscribeMap$MapSubscriber.onNext(OnSubscribeMap.java:73)
... 21 common frames omitted```
BestEffortRetryStrategy should retry until the the request is cancelled by the timeout.
FailFastRetryStrategy should not retry. It should fail immediately.
If you have a TemporaryFailureException and have BestEffortRetryStrategy, that should have been retried. If you had one that was not retried can you share the stacktrace?
Mike

new WebView() causes "java.lang.UnsupportedOperationException: not implemented"

I cannot create a new WebView programatically. Simply calling new WebView() causes the following exception:
java.lang.ExceptionInInitializerError
at javafx.web/javafx.scene.web.WebEngine.<clinit>(WebEngine.java:339)
at javafx.web/javafx.scene.web.WebView.<init>(WebView.java:260)
// omitting unrelated stacktraces
at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventFilterRecord.handleCapturingEvent(CompositeEventHandler.java:282)
at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchCapturingEvent(CompositeEventHandler.java:98)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchCapturingEvent(EventHandlerManager.java:223)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchCapturingEvent(EventHandlerManager.java:180)
at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchCapturingEvent(CompositeEventDispatcher.java:43)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:52)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
at javafx.graphics/javafx.scene.Scene$KeyHandler.process(Scene.java:4058)
at javafx.graphics/javafx.scene.Scene$KeyHandler.access$1500(Scene.java:4004)
at javafx.graphics/javafx.scene.Scene.processKeyEvent(Scene.java:2121)
at javafx.graphics/javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2595)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:217)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:149)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleKeyEvent$1(GlassViewEventHandler.java:248)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:390)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleKeyEvent(GlassViewEventHandler.java:247)
at javafx.graphics/com.sun.glass.ui.View.handleKeyEvent(View.java:547)
at javafx.graphics/com.sun.glass.ui.View.notifyKey(View.java:971)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.UnsupportedOperationException: not implemented
at javafx.base/com.sun.javafx.logging.PlatformLogger.getName(PlatformLogger.java:121)
at javafx.web/com.sun.webkit.perf.PerfLogger.fullName(PerfLogger.java:155)
at javafx.web/com.sun.webkit.perf.PerfLogger.registerProbe(PerfLogger.java:201)
at javafx.web/com.sun.webkit.perf.PerfLogger.startCount(PerfLogger.java:227)
at javafx.web/com.sun.webkit.perf.PerfLogger.<init>(PerfLogger.java:90)
at javafx.web/com.sun.webkit.perf.PerfLogger.getLogger(PerfLogger.java:57)
at javafx.web/com.sun.webkit.perf.PerfLogger.getLogger(PerfLogger.java:84)
at javafx.web/com.sun.webkit.Invoker.<clinit>(Invoker.java:34)
... 37 more
Is it a bug? I am using OpenJFX 11.0.1 and OpenJDK 11.0.1.
Updates:
As mentioned in the comments this bug will be fixed in OpenJFX 12.
GitHub Issue #334 (submitted by questioner)
GitHub Pull Request #343
JBS Issue JDK-8216470
Here's the problem code from com.sun.javafx.logging.PlatformLogger:
#Override
public String getName() {
throw new UnsupportedOperationException("not implemented");
}
As you can see, it does nothing but throw the observed exception. This is true of all the "implemented" java.lang.System.Logger methods.
If you follow the code, as outlined by the stack trace you provide, the getName method is invoked ultimately because a com.sun.webkit.perf.PerfLogger is instantiated during the process of initializing the WebEngine and, by extension, com.sun.webkit.Invoker classes. However, getName is only called when the PerfLogger is enabled.
From a cursory glance, a PerfLogger wraps a com.sun.javafx.logging.PlatformLogger which itself wraps a System.Logger. The PerfLogger is enabled if the System.Logger returns true for isLoggable(System.Logger.Level.FINE) at the time the PerfLogger is constructed.
In this case, this process results in a System.Logger with the name "com.sun.webkit.perf.Locks" being created. What this means is that you can avoid the UnsupportedOperationException if you configure this System.Logger to not log anything at Level.FINE or "lower". Doing this will make the PerfLogger "disabled" which prevents getName from being called down the line.
Note: This is based on the source code of OpenJFX 11.0.1. Everything mentioned is an implementation detail and can change without notice.

Protobuf 3.0 Map causes java.lang.ExceptionInInitializerError: null in J9VM

Here is my proto:
proto
message TransactionRecord {
……
map<string, string> parameters = 19;
……
}
Then, I use following code to get a builder
java
final TransactionRecord.Builder metaDataBuilder = requestBuilder.getMetaData().toBuilder();
This code run fine in many JVMs, but fail in J9VM
Exception as following:
java.lang.ExceptionInInitializerError: null
at java.lang.J9VMInternals.initialize(J9VMInternals.java:222) ~[na:1.6.0]
at com.blueware.deps.com.google.protobuf.MapField.mergeFrom(MapField.java:204) ~[oneapm.jar:2.0]
at com.blueware.monitor.collector.grpc.TransactionRecord$Builder.mergeFrom(TransactionRecord.java:1323) ~[oneapm.jar:2.0]
at com.blueware.monitor.collector.grpc.TransactionRecord.toBuilder(TransactionRecord.java:1062) ~[oneapm.jar:2.0]
at com.blueware.monitor.transaction.TransactionDataCompressorFactoryService.compressMetaData(TransactionDataCompressorFactoryS
ervice.java:401) ~[oneapm.jar:2.0]
at com.blueware.monitor.transaction.TransactionDataCompressorFactoryService.compress(TransactionDataCompressorFactoryService.j
at weblogic.deploy.internal.targetserver.BasicDeployment.activateFromServerLifecycle(BasicDeployment.java:361) [weblogic.jar:1
0.3.6.0]
at weblogic.management.deploy.internal.ConfiguredDeployments.deployPreStandbyInternalApps(ConfiguredDeployments.java:85) [webl
ogic.jar:10.3.6.0]
at weblogic.management.deploy.internal.DeploymentServerService.deployPreStandbyInternalApps(DeploymentServerService.java:168)
[weblogic.jar:10.3.6.0]
at weblogic.management.deploy.internal.DeploymentPreStandbyServerService.start(DeploymentPreStandbyServerService.java:27) [web
logic.jar:10.3.6.0]
at weblogic.t3.srvr.SubsystemRequest.run(SubsystemRequest.java:64) [weblogic.jar:10.3.6.0]
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:256) [com.bea.core.weblogic.workmanager_1.11.0.0.jar:1.11.0.0]
at weblogic.work.ExecuteThread.run(ExecuteThread.java:221) [com.bea.core.weblogic.workmanager_1.11.0.0.jar:1.11.0.0]
Caused by: java.lang.UnsupportedOperationException: null
at com.blueware.deps.com.google.protobuf.MapFieldLite.ensureMutable(MapFieldLite.java:221) ~[oneapm.jar:2.0]
at com.blueware.deps.com.google.protobuf.MapFieldLite.putAll(MapFieldLite.java:99) ~[oneapm.jar:2.0]
at java.util.LinkedHashMap.<init>(LinkedHashMap.java:112) ~[na:na]
at com.blueware.deps.com.google.protobuf.MapFieldLite.<init>(MapFieldLite.java:56) ~[oneapm.jar:2.0]
at com.blueware.deps.com.google.protobuf.MapFieldLite.<clinit>(MapFieldLite.java:61) ~[oneapm.jar:2.0]
at java.lang.J9VMInternals.initializeImpl(Native Method) ~[na:1.6.0]
at java.lang.J9VMInternals.initialize(J9VMInternals.java:200) ~[na:1.6.0]
... 45 common frames omitted
I have tried to remove map parameters for define, it goes fine.
Instead, I use final TransactionRecord.Builder metaDataBuilder = requestBuilder.getMetaDataBuilder();, that can't fix this issue. still failure on J9VM
How did this bug happen?
Is this a bug for map?
I have solve my problem. Have been confirmed, this is protobuf bug, when it's running on J9VM.
When static field EMPTY_MAP_FIELD in MapFieldLite is initilized on IBM J9, putAll which is overrided and calls ensureMutable. And ensureMutable is called in its super class LinkedHashMap, that causes UnsupportedOperationException is thrown, when isMutable has set to false at that time.
More info, you can read this RP.
RP has been merged by protobuf-java, RP is here

spring-data-couchbase throws DocumentDoesNotExistException for non-existent documents

I am using spring-data-couchbase 2.1.2 with spring-boot 1.4.0.RC1 and couchbase-spring-cache
It works fine when caching is disabled as it returns NULL object When caching is enabled and try to find a non-existing document in the bucket it throws an exception:
com.couchbase.client.java.error.DocumentDoesNotExistException: null
at com.couchbase.client.java.CouchbaseAsyncBucket$22.call(CouchbaseAsyncBucket.java:684) ~[java-client-2.2.8.jar:na]
at com.couchbase.client.java.CouchbaseAsyncBucket$22.call(CouchbaseAsyncBucket.java:671) ~[java-client-2.2.8.jar:na]
at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:54) ~[rxjava-1.0.17.jar:1.0.17]
at rx.observers.Subscribers$5.onNext(Subscribers.java:234) ~[rxjava-1.0.17.jar:1.0.17]
at rx.subjects.SubjectSubscriptionManager$SubjectObserver.onNext(SubjectSubscriptionManager.java:223) ~[rxjava-1.0.17.jar:1.0.17]
at rx.subjects.AsyncSubject.onCompleted(AsyncSubject.java:101) ~[rxjava-1.0.17.jar:1.0.17]
at com.couchbase.client.core.endpoint.AbstractGenericHandler.completeResponse(AbstractGenericHandler.java:354) ~[core-io-1.2.9.jar:na]
at com.couchbase.client.core.endpoint.AbstractGenericHandler.access$000(AbstractGenericHandler.java:72) ~[core-io-1.2.9.jar:na]
at com.couchbase.client.core.endpoint.AbstractGenericHandler$1.call(AbstractGenericHandler.java:372) ~[core-io-1.2.9.jar:na]
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) ~[rxjava-1.0.17.jar:1.0.17]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) ~[na:1.7.0_80]
at java.util.concurrent.FutureTask.run(FutureTask.java:262) ~[na:1.7.0_80]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178) ~[na:1.7.0_80]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292) ~[na:1.7.0_80]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) ~[na:1.7.0_80]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) ~[na:1.7.0_80]
at java.lang.Thread.run(Thread.java:745) ~[na:1.7.0_80]
Caused by: rx.exceptions.OnErrorThrowable$OnNextValue: OnError while emitting onNext value: com.couchbase.client.core.message.kv.RemoveResponse.class
at rx.exceptions.OnErrorThrowable.addValueAsLastCause(OnErrorThrowable.java:109) ~[rxjava-1.0.17.jar:1.0.17]
at rx.exceptions.Exceptions.throwOrReport(Exceptions.java:188) ~[rxjava-1.0.17.jar:1.0.17]
at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:56) ~[rxjava-1.0.17.jar:1.0.17]
... 14 common frames omitted
Is it because of AsyncBucket? Is it possible to disable AsyncBucket?
Source code https://github.com/maverickmicky/spring-couchbase-cache
This is an issue indeed... As soon as an eviction attempt for a key that isn't in cache is made, the exception will be thrown :(
I've created an issue for this (with work arounds).
edit: To work around the issue, you should be able to declare a CacheErrorHandler that has a handleCacheEvictError method that simply catches DocumentDoesNotExistException.
See the documentation section on configuring the cache abstraction here and the CachingConfigurer javadoc.
I am across the same exception and use case is I am caching the resource which return null, for example:
#Override
#Cacheable(value = "EMPLOYEE_", key = "#id")
public Employee getEmployee(int id) {
return null;
}
And fixed it using the unless attribute of #Cacheable which is available as of Spring 3.2, as follows:
#Override
#Cacheable(value = "EMPLOYEE_", key = "#id", unless = "#result == null")
public Employee getEmployee(int id) {
return null;
}
Have written an article as well which describe my problem and fix for the same - Fixing com.couchbase.client.java.error.DocumentDoesNotExistException – Couchbase

Categories