Кролик и одновременная модификация - PullRequest
3 голосов
/ 13 июля 2011

После того, как мы провели некоторое тестирование производительности для нашего приложения, которое использует jackrabbit, мы столкнулись с огромной проблемой с одновременным изменением хранилища jackrabbit.Проблема появляется, когда мы добавляем узлы или редактируем их в многопоточной эмуляции.Затем я написал очень простой тест, который показывает нам, что проблема не в нашей среде.

Есть это:

Простой Stateless Bean


     @Stateless
        @Local(TestFacadeLocal.class)
        @Remote(TestFacadeRemote.class)
        public class TestFacadeBean implements TestFacadeRemote, TestFacadeLocal {
            public void doAction(int name) throws Exception {
                new TestSynch().doAction(name);
            }
        }

Простой класс


    public class TestSynch {
        public void doAction(int name) throws Exception {
            Session session = ((Repository) new InitialContext().
                    lookup("java:jcr/local")).login(
                    new SimpleCredentials("username", "pwd".toCharArray()));
            List added = new ArrayList();
            Node folder = session.getRootNode().getNode("test");
            for (int i = 0; i <= 100; i++) {
                Node child = folder.addNode("" + System.currentTimeMillis(), 
                              "nt:folder");
                child.addMixin("mix:versionable");

                added.add(child);
            }
            // saving butch changes
            session.save();

            //checking in all created nodes
            for (Node node : added) {
                session.getWorkspace().getVersionManager().checkin(node.getPath());
            }
        }
    }

И тестовый класс


    public class Test {
        private int c = 0;
        private int countAll = 50;
        private ExecutorService executor = Executors.newFixedThreadPool(5);

        public ExecutorService getExecutor() {
            return executor;
        }

        public static void main(String[] args) {
            Test test = new Test();
            try {
                test.start();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        private void start() throws Exception {
            long time = System.currentTimeMillis();
            TestFacadeRemote testBean = (TestFacadeRemote) getContext().
                                        lookup( "test/TestFacadeBean/remote");
            for (int i = 0; i < countAll; i++) {
                getExecutor().execute(new TestInstallerThread(i, testBean));
            }

            getExecutor().shutdown();
            while (!getExecutor().isTerminated()) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(c + " shutdown " + 
                               (System.currentTimeMillis() - time));

        }

        class TestInstallerThread implements Runnable {
            private int number = 0;
            TestFacadeRemote testBean;

            public TestInstallerThread(int number, TestFacadeRemote testBean) {
                this.number = number;
                this.testBean = testBean;
            }

            @Override
            public void run() {
                try {
                    System.out.println("Installing data " + number);
                    testBean.doAction(number);
                    System.out.println("STOP" + number);
                } catch (Exception e) {
                    e.printStackTrace();
                    c++;
                }
            }

        }

        public Context getContext() throws NamingException {
            Properties properties = new Properties();
            //init props
            ..............
            return new InitialContext(properties);
        }
    }

Если я инициализировал executor с 1 потоком в пуле, все делалось без ошибок.Если я инициализировал executor с 5-нитью, я иногда получал ошибки:

на клиенте

    java.lang.RuntimeException: javax.transaction.RollbackException: [com.arjuna.ats.internal.jta.transaction.arjunacore.commitwhenaborted] [com.arjuna.ats.internal.jta.transaction.arjunacore.commitwhenaborted] Can't commit because the transaction is in aborted state
        at org.jboss.aspects.tx.TxPolicy.handleEndTransactionException(TxPolicy.java:198)

на сервере вначале предупреждают

    ItemStateReferenceCache [ItemStateReferenceCache.java:176] overwriting cached entry 187554a7-4c41-404b-b6ee-3ce2a9796a70

, а затем

    javax.jcr.RepositoryException: org.apache.jackrabbit.core.state.ItemStateException: there's already a property state instance with id 52fb4b2c-3ef4-4fc5-9b79-f20a6b2e9ea3/{http://www.jcp.org/jcr/1.0}created
        at org.apache.jackrabbit.core.PropertyImpl.restoreTransient(PropertyImpl.java:195) ~[jackrabbit-core-2.2.7.jar:2.2.7]
        at org.apache.jackrabbit.core.ItemSaveOperation.restoreTransientItems(ItemSaveOperation.java:879) [jackrabbit-core-2.2.7.jar:2.2.7]

Мы попытались синхронизировать этот метод и другие рабочие процессы для обработки многопоточных вызовов как одного потока.Ничего не помогает.

И еще одна вещь - когда мы провели аналогичный тест без слоя ejb - все работало нормально.Выглядит как контейнер, завернутый в собственную транзакцию, а затем все падает.

Может быть, кто-то сталкивался с такой проблемой.Заранее спасибо.

1 Ответ

5 голосов
/ 07 ноября 2011

Из Jackrabbit Wiki :

В спецификации JCR прямо указано, что Session не является поточно-ориентированным (JCR 1.0, раздел 7.5, и JCR 2.0, раздел 4.1.2). Следовательно, Jackrabbit не поддерживает одновременное чтение или запись в один и тот же сеанс нескольких потоков. Каждый сеанс должен быть доступен только из одного потока.

... Если вам необходимо выполнять одновременную запись в один и тот же узел, вам нужно использовать несколько сеансов и использовать блокировку JCR, чтобы избежать конфликта.

...