Repository: cayenne Updated Branches: refs/heads/master f7e3bc614 -> 9382e4733
CAY-2382 Lack of synchronization in DataContext serialization Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/9382e473 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/9382e473 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/9382e473 Branch: refs/heads/master Commit: 9382e47338c65d0208f731a153d571c7a2789faf Parents: f7e3bc6 Author: Nikita Timofeev <stari...@gmail.com> Authored: Thu Nov 30 12:03:25 2017 +0300 Committer: Nikita Timofeev <stari...@gmail.com> Committed: Thu Nov 30 12:03:25 2017 +0300 ---------------------------------------------------------------------- .../org/apache/cayenne/access/DataContext.java | 6 +- .../cayenne/DataContextConcurrencyIT.java | 119 +++++++++++++++++++ docs/doc/src/main/resources/RELEASE-NOTES.txt | 1 + 3 files changed, 124 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/9382e473/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java index de66464..f0b580d 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java @@ -1060,8 +1060,10 @@ public class DataContext extends BaseContext { // --------------------------------------------- private void writeObject(ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - + // See CAY-2382 + synchronized (getObjectStore()) { + out.defaultWriteObject(); + } // Serialize local snapshots cache if (!isUsingSharedSnapshotCache()) { out.writeObject(objectStore.getDataRowCache()); http://git-wip-us.apache.org/repos/asf/cayenne/blob/9382e473/cayenne-server/src/test/java/org/apache/cayenne/DataContextConcurrencyIT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/DataContextConcurrencyIT.java b/cayenne-server/src/test/java/org/apache/cayenne/DataContextConcurrencyIT.java new file mode 100644 index 0000000..91aff7e --- /dev/null +++ b/cayenne-server/src/test/java/org/apache/cayenne/DataContextConcurrencyIT.java @@ -0,0 +1,119 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ + +package org.apache.cayenne; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.apache.cayenne.di.Inject; +import org.apache.cayenne.testdo.testmap.Artist; +import org.apache.cayenne.unit.di.server.CayenneProjects; +import org.apache.cayenne.unit.di.server.ServerCase; +import org.apache.cayenne.unit.di.server.UseServerRuntime; +import org.apache.cayenne.util.Util; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.junit.Assert.assertTrue; + +/** + * @since 4.1 + */ +@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) +public class DataContextConcurrencyIT extends ServerCase { + + private static final Logger logger = LoggerFactory.getLogger(DataContextConcurrencyIT.class); + + @Inject + private ObjectContext context; + + /** + * see https://issues.apache.org/jira/browse/CAY-2382 + * + * This test is probabilistic as it tries to catch concurrency problem. + * It can be false-negative (e.g. Travis build not always fails) + */ + @Test + public void testCMEInContextSerialization() throws Exception { + + // add some content to context, so it will be serializing slowly + for(int i=0; i<1000; i++) { + Artist artist = context.newObject(Artist.class); + artist.setArtistName("name " + i); + } + + // add some barriers so threads will try to start synchronously + CountDownLatch startSignal = new CountDownLatch(1); + CountDownLatch serializationStartSignal = new CountDownLatch(1); + + ExecutorService service = Executors.newFixedThreadPool(2); + + // this task will modify context + Future<Boolean> resultObjectCreation = service.submit(() -> { + try { + // this thread will start when context serialized at least once, + // as it should be faster (but who knows...) + serializationStartSignal.await(); + } catch (InterruptedException e) { + return false; + } + for(int i=0; i<1000; i++) { + Artist artist = context.newObject(Artist.class); + context.deleteObject(artist); + } + return true; + }); + + // this task will serialize context + Future<Boolean> resultSerialization = service.submit(() -> { + try { + // wait for outer thread start this one + startSignal.await(); + } catch (InterruptedException e) { + return false; + } + for(int i=0; i<100; i++) { + try { + // make one serialization, before starting modifying thread + Util.cloneViaSerialization(context); + if(i == 0) { + serializationStartSignal.countDown(); + } + } catch (Exception e) { + logger.error("Serialization failed", e); + return false; + } + } + + return true; + }); + + // make sure that everyone are ready + Thread.sleep(10); + startSignal.countDown(); + + assertTrue(resultObjectCreation.get(20, TimeUnit.SECONDS)); + assertTrue(resultSerialization.get(20, TimeUnit.SECONDS)); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/9382e473/docs/doc/src/main/resources/RELEASE-NOTES.txt ---------------------------------------------------------------------- diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt index 0762b1f..7b98895 100644 --- a/docs/doc/src/main/resources/RELEASE-NOTES.txt +++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt @@ -18,6 +18,7 @@ Bug Fixes: CAY-2370 ValueObjectType for byte[] fails lookup CAY-2380 ReferenceMap should not store or return null values +CAY-2382 Lack of synchronization in DataContext serialization ---------------------------------- Release: 4.1.M1