Hello,
I am using Couchbase Community 4.5.1 server with Java client in version 2.4.7. I try to concurrently update value for N different properties in one document. All requests succeed. At the end I get the whole document and observe that some updates were lost. Problem is easy to reproduce when more than one instance of my service is working. The service uses Java SDK and operate on Couchbase cluster. Below unit test simulates this situation even with single Couchbase node (an average of 25% of the update is lost):
@Test
public void shouldUpdateManyPartsOfDocumentInParallel() throws InterruptedException
{
//configuration
final int propertiesToCreate = 200;
final int propertiesPerThread = 10;//given final JsonObject data = JsonObject.create().put("initialKey", "initialValue"); final String docuId = "testDocument"; final List<String> keys = IntStream.rangeClosed(1, propertiesToCreate).boxed() // .map(i -> "testKey_" + i) // .collect(Collectors.toList()); // each thread simulate separated instance of service and will try to create 10 new properties in document final List updates = IntStream.range(0, propertiesToCreate / propertiesPerThread).boxed() // .map(i -> (Callable) () -> { CouchbaseCluster cluster = CouchbaseCluster.create(); Bucket bucket = cluster.openBucket("default"); final int startIndex = i * propertiesPerThread; keys.subList(startIndex, startIndex + propertiesPerThread) .forEach(path -> { // try { final String value = path.replace("Key", "Value"); bucket.mutateIn(docuId) // .upsert(path, value, SubdocOptionsBuilder.builder().createParents(true)) // .execute(); } catch (final Exception e) { System.out.println(path + ": " + e.toString()); } }); return ""; }).collect(Collectors.toList()); final ExecutorService executorService = Executors.newWorkStealingPool(updates.size()); //CREATING DOCUMENT //when final JsonDocument document = JsonDocument.create(docuId, data); try { bucket.upsert(document); } catch (final Exception e) { System.out.println("Error during creating document" + e.getMessage()); throw e; } //UPDATING DOCUMENT executorService.invokeAll(updates) .stream() .map(future -> { try { return future; } catch (Exception e) { throw new IllegalStateException(e); } }) .forEach(System.out::println); // CHECK RESULT OF OPERATIONS final Map<String, Object> receivedDocument = bucket.get(docuId).content().toMap(); int errors = 0; final ArrayList<Object> failedUpdates = new ArrayList<>(); for(String key: keys) { if(!receivedDocument.containsKey(key)) { errors++; failedUpdates.add(key); } } System.out.println(String.format("Number of errors: %d. Failed to create following properties: %s", errors, failedUpdates.toString())); }