You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@jena.apache.org by afs <gi...@git.apache.org> on 2018/03/03 22:45:16 UTC

[GitHub] jena pull request #369: JENA-1492: Pass down transactions from general datas...

GitHub user afs opened a pull request:

    https://github.com/apache/jena/pull/369

    JENA-1492: Pass down transactions from general datasets

    

You can merge this pull request into a Git repository by running:

    $ git pull https://github.com/afs/jena txn-general

Alternatively you can review and apply these changes as the patch at:

    https://github.com/apache/jena/pull/369.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

    This closes #369
    
----
commit e58cbcb043ed0c18f9df0f26b2da66c8745e3963
Author: Andy Seaborne <an...@...>
Date:   2018-02-26T16:42:03Z

    Align to the style of sparql.core.TransactionalLock.

commit 7a11a6b8090e0218ab02f364e2c3153edbced5b8
Author: Andy Seaborne <an...@...>
Date:   2018-02-26T16:42:32Z

    isTransactionType -> isTransactionMode

commit 4a819f41ac8d19cb9bc043e58f9997b55143f54a
Author: Andy Seaborne <an...@...>
Date:   2018-02-27T23:22:01Z

    Skolemization function

commit b15cfe922d1b803387272169f6a870195cd56e76
Author: Andy Seaborne <an...@...>
Date:   2018-03-03T16:25:48Z

    JENA-1492: Pass transactions down from general datasets

----


---

[GitHub] jena pull request #369: JENA-1492: Pass down transactions from general datas...

Posted by afs <gi...@git.apache.org>.
Github user afs commented on a diff in the pull request:

    https://github.com/apache/jena/pull/369#discussion_r172275879
  
    --- Diff: jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java ---
    @@ -0,0 +1,240 @@
    +/*
    + * 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.jena.sparql.core;
    +
    +import java.util.*;
    +import java.util.function.Consumer;
    +
    +import org.apache.jena.graph.Graph;
    +import org.apache.jena.graph.TransactionHandler;
    +import org.apache.jena.query.ReadWrite;
    +import org.apache.jena.query.TxnType;
    +import org.apache.jena.reasoner.InfGraph;
    +import org.apache.jena.shared.LockMRSW;
    +import org.apache.jena.sparql.JenaTransactionException;
    +
    +/**
    + * A {@link Transactional} that passes the transaction operations down to transactions on
    + * independent graphs.
    + * <p>
    + * There are limitations:
    + * <ul>
    + * <li>we can't atomically do all the commits together in the crash situation.
    + * <li>This {@code Transactional} maintains a MRSW policy because that is all that is
    + * required of graphs in general.
    + * </ul>
    + * It does cover the important case of one graph ({@link DatasetGraphOne}) where the one
    + * graph is an InfGraph and should work when the graphs in the dataset is not changing or
    + * when a new memory graph is added mid-transaction.
    + * <p>
    + * This is not "nested transactions" - theer is no overall "commit" or "abort". If
    + * failure/restart occurs, some graphs may have commited and others not. It is the best
    + * that can be done given for an arbitrary collection of graphs, backed by different
    + * storage and having different capabilities.
    + * <p>
    + * Best practice is to change the graph membership outside of any transaction,
    + * ideally at setup time of the object using this class. (Caution: SPARQL Update
    + * can create graphs.   
    + * @See {@link DatasetGraphMapLink}
    + * @See {@link DatasetGraphOne}
    + */
    +public class TxnDataset2Graph extends TransactionalLock {
    +    /**
    +     * Control whether to pass down transactions from the dataset to the graph in the
    +     * dataset. This should be set to "true"; setting it "false" causes the onld,
    +     * no-transaction passing behaviour.
    +     * <p>
    +     * This is temporary flag during the transition because the change at Jena 3.7.0 needs
    +     * to be proven in real deployments as well as testing. "false" restores the Jena
    +     * 3.6.0 and before behaviour (transactions not passed down). See JENA-1492.
    +     * 
    +     * @deprecated This flag will be removed.
    +     */
    +    @Deprecated
    +    public static boolean TXN_DSG_GRAPH = true;
    +    
    +    private Graph primary;
    +    // Object key may be a graph or a DSG is the graph is a GraphView.
    +    // This avoids starting a tranasction on the same storage unit twice. 
    +    private Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        
    +    private Object lock = new Object();
    +    
    +    public TxnDataset2Graph(Graph primaryGraph, Graph ... otherGraphs) {
    +        super(new LockMRSW());
    +        primary = primaryGraph;
    +        handlers = buildHandlerSet(primary, Arrays.asList(otherGraphs));
    +    }
    +    
    +    private static Map<Object, TransactionHandler> buildHandlerSet(Graph primary, Collection<Graph> graphs) {
    +        Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        addHandler(handlers, primary);
    +        graphs.forEach(g->addHandler(handlers,g));
    +        return handlers;
    +    }
    +    
    +    private static void addHandler(Map<Object, TransactionHandler> handlers, Graph graph) {
    +        TransactionHandler th = graph.getTransactionHandler();
    +        if ( ! th.transactionsSupported() )
    +            return;
    +        Object key = calcKey(graph);
    +        if ( th.transactionsSupported() )
    +            handlers.put(key, th) ;
    +    }
    +
    +    // Determine the key - an object that is the unit of transactions.
    +    // For two graphs form the same DatasetGraph, i.e. GraphView, there should be one transaction.
    +    private static Object calcKey(Graph graph) {
    +        if ( graph instanceof GraphView )
    +            // Use the database as the key so that transactions are started once-per-storage.
    +            // This the case of a graph from some storage being plavced in a general dataset.  
    +            return ((GraphView)graph).getDataset();
    +        if ( graph instanceof InfGraph )
    +            // InfGraph does actual pass done in its TransactionHandler.
    +            // This allows the base graph to be included in the dataset as well as the InfGraph. 
    +            return calcKey(((InfGraph)graph).getRawGraph());
    +        
    +//        if ( graph instanceof GraphWrapper )
    +//            return calcKey(((GraphWrapper)graph).get());
    +//        if ( graph instanceof WrappedGraph )
    +//            return calcKey(((WrappedGraph)graph).getWrapped());
    +        return graph;
    +    }
    +
    +    private static void removeHandler(Map<Object, TransactionHandler> handlers, Graph graph) {
    +        Object key = calcKey(graph);
    +        handlers.remove(graph);
    +    }
    +
    +    // Attempt to manage the graph transactions during a transaction.
    +    // Imperfect for removal, we don't know whether to call commit() or abort().
    +    // Works for adding.
    +    // Generally better not to change the graphs during a transaction, just set them once
    +    // on creation.
    +    
    +    public void addGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        if ( ! handlers.containsKey(graph) ) {
    +            // Add if new.
    +            addHandler(handlers, graph) ;
    +            if ( super.isInTransaction() ) {
    +                // If we are in a transaction, start the subtransaction. 
    +                TransactionHandler th = handlers.get(graph);
    +                if ( th != null )
    +                    th.begin();
    +            }
    +        }
    +    }
    +    
    +    public void removeGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        if ( ! super.isInTransaction() ) {
    +            // Not in transaction, do now. 
    +            removeHandler(handlers, graph);
    +            return;
    +        }
    +        // Queue to be removed at the end.
    +        Set<Graph> toBeRemoved = removedGraphs.get();
    +        if ( toBeRemoved == null ) {
    +            // Lazy set of the HashSet. 
    +            toBeRemoved = new HashSet<>();
    +            removedGraphs.set(toBeRemoved);
    +        }
    +        removedGraphs.get().add(graph);
    +    }
    +
    +    public void setPrimaryGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        removeGraph(graph);
    +        addGraph(graph);
    +    }
    +    
    +    private void handlers(Consumer<TransactionHandler> action) {
    +        synchronized (lock) {
    +            handlers.forEach((g,th)->action.accept(th));
    +        }
    +    }
    +
    +    private void checkNotReadMode() {
    +        if ( !super.isInTransaction() )
    +            return;
    +        if ( super.isTransactionMode(ReadWrite.READ) )
    +            throw new JenaTransactionException("In READ mode in transaction");
    +    }
    +
    +    private ThreadLocal<Set<Graph>> removedGraphs = ThreadLocal.withInitial(()->null);
    +    private void start() {}
    +    private void finish() {
    +        if ( ! super.isTransactionMode(ReadWrite.WRITE) )
    +            return;
    +        // This is called inside the lock of super.
    +        Set<Graph> toBeRemoved = removedGraphs.get();
    +        removedGraphs.remove();
    +        if ( toBeRemoved == null )
    +            return ;
    +        toBeRemoved.forEach(g->removeHandler(handlers, g));
    +    }
    +    
    +    // TransactionalLock.begin(ReadWrite) calls begin(TxnType)
    +    @Override
    +    public void begin(TxnType type) {
    +        super.begin(type);
    +        // Whatever the type. Graph Transactions do not allow for "read-only".
    +        start();
    +        handlers(h->h.begin());
    --- End diff --
    
    Finding for completion on the lambda argument.  Complete on `handlers(` is `Consumer<TransactionHandler> action`.


---

[GitHub] jena pull request #369: JENA-1492: Pass down transactions from general datas...

Posted by asfgit <gi...@git.apache.org>.
Github user asfgit closed the pull request at:

    https://github.com/apache/jena/pull/369


---

[GitHub] jena pull request #369: JENA-1492: Pass down transactions from general datas...

Posted by ajs6f <gi...@git.apache.org>.
Github user ajs6f commented on a diff in the pull request:

    https://github.com/apache/jena/pull/369#discussion_r172251788
  
    --- Diff: jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java ---
    @@ -0,0 +1,240 @@
    +/*
    + * 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.jena.sparql.core;
    +
    +import java.util.*;
    +import java.util.function.Consumer;
    +
    +import org.apache.jena.graph.Graph;
    +import org.apache.jena.graph.TransactionHandler;
    +import org.apache.jena.query.ReadWrite;
    +import org.apache.jena.query.TxnType;
    +import org.apache.jena.reasoner.InfGraph;
    +import org.apache.jena.shared.LockMRSW;
    +import org.apache.jena.sparql.JenaTransactionException;
    +
    +/**
    + * A {@link Transactional} that passes the transaction operations down to transactions on
    + * independent graphs.
    + * <p>
    + * There are limitations:
    + * <ul>
    + * <li>we can't atomically do all the commits together in the crash situation.
    + * <li>This {@code Transactional} maintains a MRSW policy because that is all that is
    + * required of graphs in general.
    + * </ul>
    + * It does cover the important case of one graph ({@link DatasetGraphOne}) where the one
    + * graph is an InfGraph and should work when the graphs in the dataset is not changing or
    + * when a new memory graph is added mid-transaction.
    + * <p>
    + * This is not "nested transactions" - theer is no overall "commit" or "abort". If
    + * failure/restart occurs, some graphs may have commited and others not. It is the best
    + * that can be done given for an arbitrary collection of graphs, backed by different
    + * storage and having different capabilities.
    + * <p>
    + * Best practice is to change the graph membership outside of any transaction,
    + * ideally at setup time of the object using this class. (Caution: SPARQL Update
    + * can create graphs.   
    + * @See {@link DatasetGraphMapLink}
    + * @See {@link DatasetGraphOne}
    + */
    +public class TxnDataset2Graph extends TransactionalLock {
    +    /**
    +     * Control whether to pass down transactions from the dataset to the graph in the
    +     * dataset. This should be set to "true"; setting it "false" causes the onld,
    --- End diff --
    
    `onld` => `old`


---

[GitHub] jena issue #369: JENA-1492: Pass down transactions from general datasets

Posted by ajs6f <gi...@git.apache.org>.
Github user ajs6f commented on the issue:

    https://github.com/apache/jena/pull/369
  
    There is some new `Assembler` stuff and reformatting, so I'm not totally clear what the thrust of this PR is: is it to ensure that `Model`s wrapping other `Model`s (e.g. for inferencing) correctly pass transaction control calls into their wrapped models (which should pass them into their underlying `Dataset`)?


---

[GitHub] jena pull request #369: JENA-1492: Pass down transactions from general datas...

Posted by ajs6f <gi...@git.apache.org>.
Github user ajs6f commented on a diff in the pull request:

    https://github.com/apache/jena/pull/369#discussion_r172276168
  
    --- Diff: jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java ---
    @@ -0,0 +1,240 @@
    +/*
    + * 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.jena.sparql.core;
    +
    +import java.util.*;
    +import java.util.function.Consumer;
    +
    +import org.apache.jena.graph.Graph;
    +import org.apache.jena.graph.TransactionHandler;
    +import org.apache.jena.query.ReadWrite;
    +import org.apache.jena.query.TxnType;
    +import org.apache.jena.reasoner.InfGraph;
    +import org.apache.jena.shared.LockMRSW;
    +import org.apache.jena.sparql.JenaTransactionException;
    +
    +/**
    + * A {@link Transactional} that passes the transaction operations down to transactions on
    + * independent graphs.
    + * <p>
    + * There are limitations:
    + * <ul>
    + * <li>we can't atomically do all the commits together in the crash situation.
    + * <li>This {@code Transactional} maintains a MRSW policy because that is all that is
    + * required of graphs in general.
    + * </ul>
    + * It does cover the important case of one graph ({@link DatasetGraphOne}) where the one
    + * graph is an InfGraph and should work when the graphs in the dataset is not changing or
    + * when a new memory graph is added mid-transaction.
    + * <p>
    + * This is not "nested transactions" - theer is no overall "commit" or "abort". If
    + * failure/restart occurs, some graphs may have commited and others not. It is the best
    + * that can be done given for an arbitrary collection of graphs, backed by different
    + * storage and having different capabilities.
    + * <p>
    + * Best practice is to change the graph membership outside of any transaction,
    + * ideally at setup time of the object using this class. (Caution: SPARQL Update
    + * can create graphs.   
    + * @See {@link DatasetGraphMapLink}
    + * @See {@link DatasetGraphOne}
    + */
    +public class TxnDataset2Graph extends TransactionalLock {
    +    /**
    +     * Control whether to pass down transactions from the dataset to the graph in the
    +     * dataset. This should be set to "true"; setting it "false" causes the onld,
    +     * no-transaction passing behaviour.
    +     * <p>
    +     * This is temporary flag during the transition because the change at Jena 3.7.0 needs
    +     * to be proven in real deployments as well as testing. "false" restores the Jena
    +     * 3.6.0 and before behaviour (transactions not passed down). See JENA-1492.
    +     * 
    +     * @deprecated This flag will be removed.
    +     */
    +    @Deprecated
    +    public static boolean TXN_DSG_GRAPH = true;
    +    
    +    private Graph primary;
    +    // Object key may be a graph or a DSG is the graph is a GraphView.
    +    // This avoids starting a tranasction on the same storage unit twice. 
    +    private Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        
    +    private Object lock = new Object();
    +    
    +    public TxnDataset2Graph(Graph primaryGraph, Graph ... otherGraphs) {
    +        super(new LockMRSW());
    +        primary = primaryGraph;
    +        handlers = buildHandlerSet(primary, Arrays.asList(otherGraphs));
    +    }
    +    
    +    private static Map<Object, TransactionHandler> buildHandlerSet(Graph primary, Collection<Graph> graphs) {
    +        Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        addHandler(handlers, primary);
    +        graphs.forEach(g->addHandler(handlers,g));
    +        return handlers;
    +    }
    +    
    +    private static void addHandler(Map<Object, TransactionHandler> handlers, Graph graph) {
    +        TransactionHandler th = graph.getTransactionHandler();
    +        if ( ! th.transactionsSupported() )
    +            return;
    +        Object key = calcKey(graph);
    +        if ( th.transactionsSupported() )
    +            handlers.put(key, th) ;
    +    }
    +
    +    // Determine the key - an object that is the unit of transactions.
    +    // For two graphs form the same DatasetGraph, i.e. GraphView, there should be one transaction.
    +    private static Object calcKey(Graph graph) {
    +        if ( graph instanceof GraphView )
    +            // Use the database as the key so that transactions are started once-per-storage.
    +            // This the case of a graph from some storage being plavced in a general dataset.  
    +            return ((GraphView)graph).getDataset();
    +        if ( graph instanceof InfGraph )
    +            // InfGraph does actual pass done in its TransactionHandler.
    +            // This allows the base graph to be included in the dataset as well as the InfGraph. 
    +            return calcKey(((InfGraph)graph).getRawGraph());
    +        
    +//        if ( graph instanceof GraphWrapper )
    +//            return calcKey(((GraphWrapper)graph).get());
    +//        if ( graph instanceof WrappedGraph )
    +//            return calcKey(((WrappedGraph)graph).getWrapped());
    +        return graph;
    +    }
    +
    +    private static void removeHandler(Map<Object, TransactionHandler> handlers, Graph graph) {
    +        Object key = calcKey(graph);
    +        handlers.remove(graph);
    +    }
    +
    +    // Attempt to manage the graph transactions during a transaction.
    +    // Imperfect for removal, we don't know whether to call commit() or abort().
    +    // Works for adding.
    +    // Generally better not to change the graphs during a transaction, just set them once
    +    // on creation.
    +    
    +    public void addGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        if ( ! handlers.containsKey(graph) ) {
    +            // Add if new.
    +            addHandler(handlers, graph) ;
    +            if ( super.isInTransaction() ) {
    +                // If we are in a transaction, start the subtransaction. 
    +                TransactionHandler th = handlers.get(graph);
    +                if ( th != null )
    +                    th.begin();
    +            }
    +        }
    +    }
    +    
    +    public void removeGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        if ( ! super.isInTransaction() ) {
    +            // Not in transaction, do now. 
    +            removeHandler(handlers, graph);
    +            return;
    +        }
    +        // Queue to be removed at the end.
    +        Set<Graph> toBeRemoved = removedGraphs.get();
    +        if ( toBeRemoved == null ) {
    +            // Lazy set of the HashSet. 
    +            toBeRemoved = new HashSet<>();
    +            removedGraphs.set(toBeRemoved);
    +        }
    +        removedGraphs.get().add(graph);
    +    }
    +
    +    public void setPrimaryGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        removeGraph(graph);
    +        addGraph(graph);
    +    }
    +    
    +    private void handlers(Consumer<TransactionHandler> action) {
    +        synchronized (lock) {
    +            handlers.forEach((g,th)->action.accept(th));
    +        }
    +    }
    +
    +    private void checkNotReadMode() {
    +        if ( !super.isInTransaction() )
    +            return;
    +        if ( super.isTransactionMode(ReadWrite.READ) )
    +            throw new JenaTransactionException("In READ mode in transaction");
    +    }
    +
    +    private ThreadLocal<Set<Graph>> removedGraphs = ThreadLocal.withInitial(()->null);
    +    private void start() {}
    +    private void finish() {
    +        if ( ! super.isTransactionMode(ReadWrite.WRITE) )
    +            return;
    +        // This is called inside the lock of super.
    +        Set<Graph> toBeRemoved = removedGraphs.get();
    +        removedGraphs.remove();
    +        if ( toBeRemoved == null )
    +            return ;
    +        toBeRemoved.forEach(g->removeHandler(handlers, g));
    +    }
    +    
    +    // TransactionalLock.begin(ReadWrite) calls begin(TxnType)
    +    @Override
    +    public void begin(TxnType type) {
    +        super.begin(type);
    +        // Whatever the type. Graph Transactions do not allow for "read-only".
    +        start();
    +        handlers(h->h.begin());
    --- End diff --
    
    Yeah, Eclipse (Cmd-space) gives me that, no problem. 


---

[GitHub] jena pull request #369: JENA-1492: Pass down transactions from general datas...

Posted by ajs6f <gi...@git.apache.org>.
Github user ajs6f commented on a diff in the pull request:

    https://github.com/apache/jena/pull/369#discussion_r172272325
  
    --- Diff: jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java ---
    @@ -0,0 +1,240 @@
    +/*
    + * 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.jena.sparql.core;
    +
    +import java.util.*;
    +import java.util.function.Consumer;
    +
    +import org.apache.jena.graph.Graph;
    +import org.apache.jena.graph.TransactionHandler;
    +import org.apache.jena.query.ReadWrite;
    +import org.apache.jena.query.TxnType;
    +import org.apache.jena.reasoner.InfGraph;
    +import org.apache.jena.shared.LockMRSW;
    +import org.apache.jena.sparql.JenaTransactionException;
    +
    +/**
    + * A {@link Transactional} that passes the transaction operations down to transactions on
    + * independent graphs.
    + * <p>
    + * There are limitations:
    + * <ul>
    + * <li>we can't atomically do all the commits together in the crash situation.
    + * <li>This {@code Transactional} maintains a MRSW policy because that is all that is
    + * required of graphs in general.
    + * </ul>
    + * It does cover the important case of one graph ({@link DatasetGraphOne}) where the one
    + * graph is an InfGraph and should work when the graphs in the dataset is not changing or
    + * when a new memory graph is added mid-transaction.
    + * <p>
    + * This is not "nested transactions" - theer is no overall "commit" or "abort". If
    + * failure/restart occurs, some graphs may have commited and others not. It is the best
    + * that can be done given for an arbitrary collection of graphs, backed by different
    + * storage and having different capabilities.
    + * <p>
    + * Best practice is to change the graph membership outside of any transaction,
    + * ideally at setup time of the object using this class. (Caution: SPARQL Update
    + * can create graphs.   
    + * @See {@link DatasetGraphMapLink}
    + * @See {@link DatasetGraphOne}
    + */
    +public class TxnDataset2Graph extends TransactionalLock {
    +    /**
    +     * Control whether to pass down transactions from the dataset to the graph in the
    +     * dataset. This should be set to "true"; setting it "false" causes the onld,
    +     * no-transaction passing behaviour.
    +     * <p>
    +     * This is temporary flag during the transition because the change at Jena 3.7.0 needs
    +     * to be proven in real deployments as well as testing. "false" restores the Jena
    +     * 3.6.0 and before behaviour (transactions not passed down). See JENA-1492.
    +     * 
    +     * @deprecated This flag will be removed.
    +     */
    +    @Deprecated
    +    public static boolean TXN_DSG_GRAPH = true;
    +    
    +    private Graph primary;
    +    // Object key may be a graph or a DSG is the graph is a GraphView.
    +    // This avoids starting a tranasction on the same storage unit twice. 
    +    private Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        
    +    private Object lock = new Object();
    +    
    +    public TxnDataset2Graph(Graph primaryGraph, Graph ... otherGraphs) {
    +        super(new LockMRSW());
    +        primary = primaryGraph;
    +        handlers = buildHandlerSet(primary, Arrays.asList(otherGraphs));
    +    }
    +    
    +    private static Map<Object, TransactionHandler> buildHandlerSet(Graph primary, Collection<Graph> graphs) {
    +        Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        addHandler(handlers, primary);
    +        graphs.forEach(g->addHandler(handlers,g));
    +        return handlers;
    +    }
    +    
    +    private static void addHandler(Map<Object, TransactionHandler> handlers, Graph graph) {
    +        TransactionHandler th = graph.getTransactionHandler();
    +        if ( ! th.transactionsSupported() )
    +            return;
    +        Object key = calcKey(graph);
    +        if ( th.transactionsSupported() )
    +            handlers.put(key, th) ;
    +    }
    +
    +    // Determine the key - an object that is the unit of transactions.
    +    // For two graphs form the same DatasetGraph, i.e. GraphView, there should be one transaction.
    +    private static Object calcKey(Graph graph) {
    +        if ( graph instanceof GraphView )
    +            // Use the database as the key so that transactions are started once-per-storage.
    +            // This the case of a graph from some storage being plavced in a general dataset.  
    +            return ((GraphView)graph).getDataset();
    +        if ( graph instanceof InfGraph )
    +            // InfGraph does actual pass done in its TransactionHandler.
    +            // This allows the base graph to be included in the dataset as well as the InfGraph. 
    +            return calcKey(((InfGraph)graph).getRawGraph());
    +        
    +//        if ( graph instanceof GraphWrapper )
    +//            return calcKey(((GraphWrapper)graph).get());
    +//        if ( graph instanceof WrappedGraph )
    +//            return calcKey(((WrappedGraph)graph).getWrapped());
    +        return graph;
    +    }
    +
    +    private static void removeHandler(Map<Object, TransactionHandler> handlers, Graph graph) {
    +        Object key = calcKey(graph);
    +        handlers.remove(graph);
    +    }
    +
    +    // Attempt to manage the graph transactions during a transaction.
    +    // Imperfect for removal, we don't know whether to call commit() or abort().
    +    // Works for adding.
    +    // Generally better not to change the graphs during a transaction, just set them once
    +    // on creation.
    +    
    +    public void addGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        if ( ! handlers.containsKey(graph) ) {
    +            // Add if new.
    +            addHandler(handlers, graph) ;
    +            if ( super.isInTransaction() ) {
    +                // If we are in a transaction, start the subtransaction. 
    +                TransactionHandler th = handlers.get(graph);
    +                if ( th != null )
    +                    th.begin();
    +            }
    +        }
    +    }
    +    
    +    public void removeGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        if ( ! super.isInTransaction() ) {
    +            // Not in transaction, do now. 
    +            removeHandler(handlers, graph);
    +            return;
    +        }
    +        // Queue to be removed at the end.
    +        Set<Graph> toBeRemoved = removedGraphs.get();
    +        if ( toBeRemoved == null ) {
    +            // Lazy set of the HashSet. 
    +            toBeRemoved = new HashSet<>();
    +            removedGraphs.set(toBeRemoved);
    +        }
    +        removedGraphs.get().add(graph);
    +    }
    +
    +    public void setPrimaryGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        removeGraph(graph);
    +        addGraph(graph);
    +    }
    +    
    +    private void handlers(Consumer<TransactionHandler> action) {
    +        synchronized (lock) {
    +            handlers.forEach((g,th)->action.accept(th));
    +        }
    +    }
    +
    +    private void checkNotReadMode() {
    +        if ( !super.isInTransaction() )
    +            return;
    +        if ( super.isTransactionMode(ReadWrite.READ) )
    +            throw new JenaTransactionException("In READ mode in transaction");
    +    }
    +
    +    private ThreadLocal<Set<Graph>> removedGraphs = ThreadLocal.withInitial(()->null);
    +    private void start() {}
    +    private void finish() {
    +        if ( ! super.isTransactionMode(ReadWrite.WRITE) )
    +            return;
    +        // This is called inside the lock of super.
    +        Set<Graph> toBeRemoved = removedGraphs.get();
    +        removedGraphs.remove();
    +        if ( toBeRemoved == null )
    +            return ;
    +        toBeRemoved.forEach(g->removeHandler(handlers, g));
    +    }
    +    
    +    // TransactionalLock.begin(ReadWrite) calls begin(TxnType)
    +    @Override
    +    public void begin(TxnType type) {
    +        super.begin(type);
    +        // Whatever the type. Graph Transactions do not allow for "read-only".
    +        start();
    +        handlers(h->h.begin());
    --- End diff --
    
    It's fine not to, but what kind of IDE are you using that can't trace method references? Eclipse certainly can. 


---

[GitHub] jena issue #369: JENA-1492: Pass down transactions from general datasets

Posted by ajs6f <gi...@git.apache.org>.
Github user ajs6f commented on the issue:

    https://github.com/apache/jena/pull/369
  
    Okay. `::wrap` vs. `::create` is not what I am referring to as reformatting. It's stuff like [this](https://github.com/apache/jena/pull/369/files#diff-27cb8b74e951d2872a91a48066930e2d) or [this](https://github.com/apache/jena/pull/369/files#diff-9d27f4f9095b5d190b6fe8977b6c393b).
    
    From what I understand, this all makes sense. Datasets that are "collected" from multiple graphs can, at best, pass the transaction through to their graphs, those that are in control of their own data can retain the responsibility for transactions themselves. That does seem to be the least surprising behavior and the one that veers towards "dataset as the fundamental unit" which we have been working towards.



---

[GitHub] jena issue #369: JENA-1492: Pass down transactions from general datasets

Posted by afs <gi...@git.apache.org>.
Github user afs commented on the issue:

    https://github.com/apache/jena/pull/369
  
    "Skolemization function" is not related; it is a small clarification done in passing.


---

[GitHub] jena pull request #369: JENA-1492: Pass down transactions from general datas...

Posted by ajs6f <gi...@git.apache.org>.
Github user ajs6f commented on a diff in the pull request:

    https://github.com/apache/jena/pull/369#discussion_r172254303
  
    --- Diff: jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java ---
    @@ -0,0 +1,240 @@
    +/*
    + * 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.jena.sparql.core;
    +
    +import java.util.*;
    +import java.util.function.Consumer;
    +
    +import org.apache.jena.graph.Graph;
    +import org.apache.jena.graph.TransactionHandler;
    +import org.apache.jena.query.ReadWrite;
    +import org.apache.jena.query.TxnType;
    +import org.apache.jena.reasoner.InfGraph;
    +import org.apache.jena.shared.LockMRSW;
    +import org.apache.jena.sparql.JenaTransactionException;
    +
    +/**
    + * A {@link Transactional} that passes the transaction operations down to transactions on
    + * independent graphs.
    + * <p>
    + * There are limitations:
    + * <ul>
    + * <li>we can't atomically do all the commits together in the crash situation.
    + * <li>This {@code Transactional} maintains a MRSW policy because that is all that is
    + * required of graphs in general.
    + * </ul>
    + * It does cover the important case of one graph ({@link DatasetGraphOne}) where the one
    + * graph is an InfGraph and should work when the graphs in the dataset is not changing or
    + * when a new memory graph is added mid-transaction.
    + * <p>
    + * This is not "nested transactions" - theer is no overall "commit" or "abort". If
    + * failure/restart occurs, some graphs may have commited and others not. It is the best
    + * that can be done given for an arbitrary collection of graphs, backed by different
    + * storage and having different capabilities.
    + * <p>
    + * Best practice is to change the graph membership outside of any transaction,
    + * ideally at setup time of the object using this class. (Caution: SPARQL Update
    + * can create graphs.   
    + * @See {@link DatasetGraphMapLink}
    + * @See {@link DatasetGraphOne}
    + */
    +public class TxnDataset2Graph extends TransactionalLock {
    +    /**
    +     * Control whether to pass down transactions from the dataset to the graph in the
    +     * dataset. This should be set to "true"; setting it "false" causes the onld,
    +     * no-transaction passing behaviour.
    +     * <p>
    +     * This is temporary flag during the transition because the change at Jena 3.7.0 needs
    +     * to be proven in real deployments as well as testing. "false" restores the Jena
    +     * 3.6.0 and before behaviour (transactions not passed down). See JENA-1492.
    +     * 
    +     * @deprecated This flag will be removed.
    +     */
    +    @Deprecated
    +    public static boolean TXN_DSG_GRAPH = true;
    +    
    +    private Graph primary;
    +    // Object key may be a graph or a DSG is the graph is a GraphView.
    +    // This avoids starting a tranasction on the same storage unit twice. 
    +    private Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        
    +    private Object lock = new Object();
    +    
    +    public TxnDataset2Graph(Graph primaryGraph, Graph ... otherGraphs) {
    +        super(new LockMRSW());
    +        primary = primaryGraph;
    +        handlers = buildHandlerSet(primary, Arrays.asList(otherGraphs));
    +    }
    +    
    +    private static Map<Object, TransactionHandler> buildHandlerSet(Graph primary, Collection<Graph> graphs) {
    +        Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        addHandler(handlers, primary);
    +        graphs.forEach(g->addHandler(handlers,g));
    +        return handlers;
    +    }
    +    
    +    private static void addHandler(Map<Object, TransactionHandler> handlers, Graph graph) {
    +        TransactionHandler th = graph.getTransactionHandler();
    +        if ( ! th.transactionsSupported() )
    +            return;
    +        Object key = calcKey(graph);
    +        if ( th.transactionsSupported() )
    +            handlers.put(key, th) ;
    +    }
    +
    +    // Determine the key - an object that is the unit of transactions.
    +    // For two graphs form the same DatasetGraph, i.e. GraphView, there should be one transaction.
    +    private static Object calcKey(Graph graph) {
    +        if ( graph instanceof GraphView )
    +            // Use the database as the key so that transactions are started once-per-storage.
    +            // This the case of a graph from some storage being plavced in a general dataset.  
    +            return ((GraphView)graph).getDataset();
    +        if ( graph instanceof InfGraph )
    +            // InfGraph does actual pass done in its TransactionHandler.
    +            // This allows the base graph to be included in the dataset as well as the InfGraph. 
    +            return calcKey(((InfGraph)graph).getRawGraph());
    +        
    +//        if ( graph instanceof GraphWrapper )
    +//            return calcKey(((GraphWrapper)graph).get());
    +//        if ( graph instanceof WrappedGraph )
    +//            return calcKey(((WrappedGraph)graph).getWrapped());
    +        return graph;
    +    }
    +
    +    private static void removeHandler(Map<Object, TransactionHandler> handlers, Graph graph) {
    +        Object key = calcKey(graph);
    +        handlers.remove(graph);
    +    }
    +
    +    // Attempt to manage the graph transactions during a transaction.
    +    // Imperfect for removal, we don't know whether to call commit() or abort().
    +    // Works for adding.
    +    // Generally better not to change the graphs during a transaction, just set them once
    +    // on creation.
    +    
    +    public void addGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        if ( ! handlers.containsKey(graph) ) {
    +            // Add if new.
    +            addHandler(handlers, graph) ;
    +            if ( super.isInTransaction() ) {
    +                // If we are in a transaction, start the subtransaction. 
    +                TransactionHandler th = handlers.get(graph);
    +                if ( th != null )
    +                    th.begin();
    +            }
    +        }
    +    }
    +    
    +    public void removeGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        if ( ! super.isInTransaction() ) {
    +            // Not in transaction, do now. 
    +            removeHandler(handlers, graph);
    +            return;
    +        }
    +        // Queue to be removed at the end.
    +        Set<Graph> toBeRemoved = removedGraphs.get();
    +        if ( toBeRemoved == null ) {
    +            // Lazy set of the HashSet. 
    +            toBeRemoved = new HashSet<>();
    +            removedGraphs.set(toBeRemoved);
    +        }
    +        removedGraphs.get().add(graph);
    +    }
    +
    +    public void setPrimaryGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        removeGraph(graph);
    +        addGraph(graph);
    +    }
    +    
    +    private void handlers(Consumer<TransactionHandler> action) {
    +        synchronized (lock) {
    +            handlers.forEach((g,th)->action.accept(th));
    +        }
    +    }
    +
    +    private void checkNotReadMode() {
    +        if ( !super.isInTransaction() )
    +            return;
    +        if ( super.isTransactionMode(ReadWrite.READ) )
    +            throw new JenaTransactionException("In READ mode in transaction");
    +    }
    +
    +    private ThreadLocal<Set<Graph>> removedGraphs = ThreadLocal.withInitial(()->null);
    +    private void start() {}
    +    private void finish() {
    +        if ( ! super.isTransactionMode(ReadWrite.WRITE) )
    +            return;
    +        // This is called inside the lock of super.
    +        Set<Graph> toBeRemoved = removedGraphs.get();
    +        removedGraphs.remove();
    +        if ( toBeRemoved == null )
    +            return ;
    +        toBeRemoved.forEach(g->removeHandler(handlers, g));
    +    }
    +    
    +    // TransactionalLock.begin(ReadWrite) calls begin(TxnType)
    +    @Override
    +    public void begin(TxnType type) {
    +        super.begin(type);
    +        // Whatever the type. Graph Transactions do not allow for "read-only".
    +        start();
    +        handlers(h->h.begin());
    --- End diff --
    
    No big deal, but this and things like it could be `handlers(TransactionHandler::begin)`, which I think reads a bit easier.


---

[GitHub] jena pull request #369: JENA-1492: Pass down transactions from general datas...

Posted by ajs6f <gi...@git.apache.org>.
Github user ajs6f commented on a diff in the pull request:

    https://github.com/apache/jena/pull/369#discussion_r172252112
  
    --- Diff: jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java ---
    @@ -0,0 +1,240 @@
    +/*
    + * 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.jena.sparql.core;
    +
    +import java.util.*;
    +import java.util.function.Consumer;
    +
    +import org.apache.jena.graph.Graph;
    +import org.apache.jena.graph.TransactionHandler;
    +import org.apache.jena.query.ReadWrite;
    +import org.apache.jena.query.TxnType;
    +import org.apache.jena.reasoner.InfGraph;
    +import org.apache.jena.shared.LockMRSW;
    +import org.apache.jena.sparql.JenaTransactionException;
    +
    +/**
    + * A {@link Transactional} that passes the transaction operations down to transactions on
    + * independent graphs.
    + * <p>
    + * There are limitations:
    + * <ul>
    + * <li>we can't atomically do all the commits together in the crash situation.
    + * <li>This {@code Transactional} maintains a MRSW policy because that is all that is
    + * required of graphs in general.
    + * </ul>
    + * It does cover the important case of one graph ({@link DatasetGraphOne}) where the one
    + * graph is an InfGraph and should work when the graphs in the dataset is not changing or
    + * when a new memory graph is added mid-transaction.
    + * <p>
    + * This is not "nested transactions" - theer is no overall "commit" or "abort". If
    + * failure/restart occurs, some graphs may have commited and others not. It is the best
    + * that can be done given for an arbitrary collection of graphs, backed by different
    + * storage and having different capabilities.
    + * <p>
    + * Best practice is to change the graph membership outside of any transaction,
    + * ideally at setup time of the object using this class. (Caution: SPARQL Update
    + * can create graphs.   
    + * @See {@link DatasetGraphMapLink}
    + * @See {@link DatasetGraphOne}
    + */
    +public class TxnDataset2Graph extends TransactionalLock {
    +    /**
    +     * Control whether to pass down transactions from the dataset to the graph in the
    +     * dataset. This should be set to "true"; setting it "false" causes the onld,
    +     * no-transaction passing behaviour.
    +     * <p>
    +     * This is temporary flag during the transition because the change at Jena 3.7.0 needs
    +     * to be proven in real deployments as well as testing. "false" restores the Jena
    +     * 3.6.0 and before behaviour (transactions not passed down). See JENA-1492.
    +     * 
    +     * @deprecated This flag will be removed.
    +     */
    +    @Deprecated
    +    public static boolean TXN_DSG_GRAPH = true;
    +    
    +    private Graph primary;
    +    // Object key may be a graph or a DSG is the graph is a GraphView.
    +    // This avoids starting a tranasction on the same storage unit twice. 
    +    private Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        
    +    private Object lock = new Object();
    +    
    +    public TxnDataset2Graph(Graph primaryGraph, Graph ... otherGraphs) {
    +        super(new LockMRSW());
    +        primary = primaryGraph;
    +        handlers = buildHandlerSet(primary, Arrays.asList(otherGraphs));
    +    }
    +    
    +    private static Map<Object, TransactionHandler> buildHandlerSet(Graph primary, Collection<Graph> graphs) {
    +        Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        addHandler(handlers, primary);
    +        graphs.forEach(g->addHandler(handlers,g));
    +        return handlers;
    +    }
    +    
    +    private static void addHandler(Map<Object, TransactionHandler> handlers, Graph graph) {
    +        TransactionHandler th = graph.getTransactionHandler();
    +        if ( ! th.transactionsSupported() )
    +            return;
    +        Object key = calcKey(graph);
    +        if ( th.transactionsSupported() )
    +            handlers.put(key, th) ;
    +    }
    +
    +    // Determine the key - an object that is the unit of transactions.
    +    // For two graphs form the same DatasetGraph, i.e. GraphView, there should be one transaction.
    --- End diff --
    
    `form`=>`from`


---

[GitHub] jena issue #369: JENA-1492: Pass down transactions from general datasets

Posted by afs <gi...@git.apache.org>.
Github user afs commented on the issue:

    https://github.com/apache/jena/pull/369
  
    Yes, this is models passing down transactions.  It only became possible with dataset-transaction promotion (new to 3.7.0).  
    
    This all has a lot of knock on consequences. The wrap/create distinction is quite important and that touches a lot of files; it is not reformatting. "wrap" a model and the dataset can not have named graphs added, "create" with a model and it starts with that model and can have named graphs added. We only used to have "create" which has overhead and complexity as well as occasionally leading to unexpected behaviour with SPARQL Updates (updates work but the new NG are in-memory and disappear on restart - better to have a closed dataset and catch the issue at update time).
    
    While testing, I found lack of test coverage and integration testing, which is the root of the users@ email that triggered this, was the place to put this. There as a lack of assemblers for certain other special cases so I included them as well as they arose naturally from checking test coverage.
    



---

[GitHub] jena pull request #369: JENA-1492: Pass down transactions from general datas...

Posted by afs <gi...@git.apache.org>.
Github user afs commented on a diff in the pull request:

    https://github.com/apache/jena/pull/369#discussion_r172271582
  
    --- Diff: jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java ---
    @@ -0,0 +1,240 @@
    +/*
    + * 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.jena.sparql.core;
    +
    +import java.util.*;
    +import java.util.function.Consumer;
    +
    +import org.apache.jena.graph.Graph;
    +import org.apache.jena.graph.TransactionHandler;
    +import org.apache.jena.query.ReadWrite;
    +import org.apache.jena.query.TxnType;
    +import org.apache.jena.reasoner.InfGraph;
    +import org.apache.jena.shared.LockMRSW;
    +import org.apache.jena.sparql.JenaTransactionException;
    +
    +/**
    + * A {@link Transactional} that passes the transaction operations down to transactions on
    + * independent graphs.
    + * <p>
    + * There are limitations:
    + * <ul>
    + * <li>we can't atomically do all the commits together in the crash situation.
    + * <li>This {@code Transactional} maintains a MRSW policy because that is all that is
    + * required of graphs in general.
    + * </ul>
    + * It does cover the important case of one graph ({@link DatasetGraphOne}) where the one
    + * graph is an InfGraph and should work when the graphs in the dataset is not changing or
    + * when a new memory graph is added mid-transaction.
    + * <p>
    + * This is not "nested transactions" - theer is no overall "commit" or "abort". If
    + * failure/restart occurs, some graphs may have commited and others not. It is the best
    + * that can be done given for an arbitrary collection of graphs, backed by different
    + * storage and having different capabilities.
    + * <p>
    + * Best practice is to change the graph membership outside of any transaction,
    + * ideally at setup time of the object using this class. (Caution: SPARQL Update
    + * can create graphs.   
    + * @See {@link DatasetGraphMapLink}
    + * @See {@link DatasetGraphOne}
    + */
    +public class TxnDataset2Graph extends TransactionalLock {
    +    /**
    +     * Control whether to pass down transactions from the dataset to the graph in the
    +     * dataset. This should be set to "true"; setting it "false" causes the onld,
    +     * no-transaction passing behaviour.
    +     * <p>
    +     * This is temporary flag during the transition because the change at Jena 3.7.0 needs
    +     * to be proven in real deployments as well as testing. "false" restores the Jena
    +     * 3.6.0 and before behaviour (transactions not passed down). See JENA-1492.
    +     * 
    +     * @deprecated This flag will be removed.
    +     */
    +    @Deprecated
    +    public static boolean TXN_DSG_GRAPH = true;
    +    
    +    private Graph primary;
    +    // Object key may be a graph or a DSG is the graph is a GraphView.
    +    // This avoids starting a tranasction on the same storage unit twice. 
    +    private Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        
    +    private Object lock = new Object();
    +    
    +    public TxnDataset2Graph(Graph primaryGraph, Graph ... otherGraphs) {
    +        super(new LockMRSW());
    +        primary = primaryGraph;
    +        handlers = buildHandlerSet(primary, Arrays.asList(otherGraphs));
    +    }
    +    
    +    private static Map<Object, TransactionHandler> buildHandlerSet(Graph primary, Collection<Graph> graphs) {
    +        Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        addHandler(handlers, primary);
    +        graphs.forEach(g->addHandler(handlers,g));
    +        return handlers;
    +    }
    +    
    +    private static void addHandler(Map<Object, TransactionHandler> handlers, Graph graph) {
    +        TransactionHandler th = graph.getTransactionHandler();
    +        if ( ! th.transactionsSupported() )
    +            return;
    +        Object key = calcKey(graph);
    +        if ( th.transactionsSupported() )
    +            handlers.put(key, th) ;
    +    }
    +
    +    // Determine the key - an object that is the unit of transactions.
    +    // For two graphs form the same DatasetGraph, i.e. GraphView, there should be one transaction.
    +    private static Object calcKey(Graph graph) {
    +        if ( graph instanceof GraphView )
    +            // Use the database as the key so that transactions are started once-per-storage.
    +            // This the case of a graph from some storage being plavced in a general dataset.  
    +            return ((GraphView)graph).getDataset();
    +        if ( graph instanceof InfGraph )
    +            // InfGraph does actual pass done in its TransactionHandler.
    +            // This allows the base graph to be included in the dataset as well as the InfGraph. 
    +            return calcKey(((InfGraph)graph).getRawGraph());
    +        
    +//        if ( graph instanceof GraphWrapper )
    +//            return calcKey(((GraphWrapper)graph).get());
    +//        if ( graph instanceof WrappedGraph )
    +//            return calcKey(((WrappedGraph)graph).getWrapped());
    +        return graph;
    +    }
    +
    +    private static void removeHandler(Map<Object, TransactionHandler> handlers, Graph graph) {
    +        Object key = calcKey(graph);
    +        handlers.remove(graph);
    +    }
    +
    +    // Attempt to manage the graph transactions during a transaction.
    +    // Imperfect for removal, we don't know whether to call commit() or abort().
    +    // Works for adding.
    +    // Generally better not to change the graphs during a transaction, just set them once
    +    // on creation.
    +    
    +    public void addGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        if ( ! handlers.containsKey(graph) ) {
    +            // Add if new.
    +            addHandler(handlers, graph) ;
    +            if ( super.isInTransaction() ) {
    +                // If we are in a transaction, start the subtransaction. 
    +                TransactionHandler th = handlers.get(graph);
    +                if ( th != null )
    +                    th.begin();
    +            }
    +        }
    +    }
    +    
    +    public void removeGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        if ( ! super.isInTransaction() ) {
    +            // Not in transaction, do now. 
    +            removeHandler(handlers, graph);
    +            return;
    +        }
    +        // Queue to be removed at the end.
    +        Set<Graph> toBeRemoved = removedGraphs.get();
    +        if ( toBeRemoved == null ) {
    +            // Lazy set of the HashSet. 
    +            toBeRemoved = new HashSet<>();
    +            removedGraphs.set(toBeRemoved);
    +        }
    +        removedGraphs.get().add(graph);
    +    }
    +
    +    public void setPrimaryGraph(Graph graph) {
    +        checkNotReadMode();
    +        if ( graph == null )
    +            return;
    +        removeGraph(graph);
    +        addGraph(graph);
    +    }
    +    
    +    private void handlers(Consumer<TransactionHandler> action) {
    +        synchronized (lock) {
    +            handlers.forEach((g,th)->action.accept(th));
    +        }
    +    }
    +
    +    private void checkNotReadMode() {
    +        if ( !super.isInTransaction() )
    +            return;
    +        if ( super.isTransactionMode(ReadWrite.READ) )
    +            throw new JenaTransactionException("In READ mode in transaction");
    +    }
    +
    +    private ThreadLocal<Set<Graph>> removedGraphs = ThreadLocal.withInitial(()->null);
    +    private void start() {}
    +    private void finish() {
    +        if ( ! super.isTransactionMode(ReadWrite.WRITE) )
    +            return;
    +        // This is called inside the lock of super.
    +        Set<Graph> toBeRemoved = removedGraphs.get();
    +        removedGraphs.remove();
    +        if ( toBeRemoved == null )
    +            return ;
    +        toBeRemoved.forEach(g->removeHandler(handlers, g));
    +    }
    +    
    +    // TransactionalLock.begin(ReadWrite) calls begin(TxnType)
    +    @Override
    +    public void begin(TxnType type) {
    +        super.begin(type);
    +        // Whatever the type. Graph Transactions do not allow for "read-only".
    +        start();
    +        handlers(h->h.begin());
    --- End diff --
    
    I don't think it always does :-) `h.` means the IDE method finding works.


---

[GitHub] jena pull request #369: JENA-1492: Pass down transactions from general datas...

Posted by ajs6f <gi...@git.apache.org>.
Github user ajs6f commented on a diff in the pull request:

    https://github.com/apache/jena/pull/369#discussion_r172252346
  
    --- Diff: jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java ---
    @@ -0,0 +1,240 @@
    +/*
    + * 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.jena.sparql.core;
    +
    +import java.util.*;
    +import java.util.function.Consumer;
    +
    +import org.apache.jena.graph.Graph;
    +import org.apache.jena.graph.TransactionHandler;
    +import org.apache.jena.query.ReadWrite;
    +import org.apache.jena.query.TxnType;
    +import org.apache.jena.reasoner.InfGraph;
    +import org.apache.jena.shared.LockMRSW;
    +import org.apache.jena.sparql.JenaTransactionException;
    +
    +/**
    + * A {@link Transactional} that passes the transaction operations down to transactions on
    + * independent graphs.
    + * <p>
    + * There are limitations:
    + * <ul>
    + * <li>we can't atomically do all the commits together in the crash situation.
    + * <li>This {@code Transactional} maintains a MRSW policy because that is all that is
    + * required of graphs in general.
    + * </ul>
    + * It does cover the important case of one graph ({@link DatasetGraphOne}) where the one
    + * graph is an InfGraph and should work when the graphs in the dataset is not changing or
    + * when a new memory graph is added mid-transaction.
    + * <p>
    + * This is not "nested transactions" - theer is no overall "commit" or "abort". If
    + * failure/restart occurs, some graphs may have commited and others not. It is the best
    + * that can be done given for an arbitrary collection of graphs, backed by different
    + * storage and having different capabilities.
    + * <p>
    + * Best practice is to change the graph membership outside of any transaction,
    + * ideally at setup time of the object using this class. (Caution: SPARQL Update
    + * can create graphs.   
    + * @See {@link DatasetGraphMapLink}
    + * @See {@link DatasetGraphOne}
    + */
    +public class TxnDataset2Graph extends TransactionalLock {
    +    /**
    +     * Control whether to pass down transactions from the dataset to the graph in the
    +     * dataset. This should be set to "true"; setting it "false" causes the onld,
    +     * no-transaction passing behaviour.
    +     * <p>
    +     * This is temporary flag during the transition because the change at Jena 3.7.0 needs
    +     * to be proven in real deployments as well as testing. "false" restores the Jena
    +     * 3.6.0 and before behaviour (transactions not passed down). See JENA-1492.
    +     * 
    +     * @deprecated This flag will be removed.
    +     */
    +    @Deprecated
    +    public static boolean TXN_DSG_GRAPH = true;
    +    
    +    private Graph primary;
    +    // Object key may be a graph or a DSG is the graph is a GraphView.
    +    // This avoids starting a tranasction on the same storage unit twice. 
    +    private Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        
    +    private Object lock = new Object();
    +    
    +    public TxnDataset2Graph(Graph primaryGraph, Graph ... otherGraphs) {
    +        super(new LockMRSW());
    +        primary = primaryGraph;
    +        handlers = buildHandlerSet(primary, Arrays.asList(otherGraphs));
    +    }
    +    
    +    private static Map<Object, TransactionHandler> buildHandlerSet(Graph primary, Collection<Graph> graphs) {
    +        Map<Object, TransactionHandler> handlers = new HashMap<>();
    +        addHandler(handlers, primary);
    +        graphs.forEach(g->addHandler(handlers,g));
    +        return handlers;
    +    }
    +    
    +    private static void addHandler(Map<Object, TransactionHandler> handlers, Graph graph) {
    +        TransactionHandler th = graph.getTransactionHandler();
    +        if ( ! th.transactionsSupported() )
    +            return;
    +        Object key = calcKey(graph);
    +        if ( th.transactionsSupported() )
    +            handlers.put(key, th) ;
    +    }
    +
    +    // Determine the key - an object that is the unit of transactions.
    +    // For two graphs form the same DatasetGraph, i.e. GraphView, there should be one transaction.
    +    private static Object calcKey(Graph graph) {
    +        if ( graph instanceof GraphView )
    +            // Use the database as the key so that transactions are started once-per-storage.
    +            // This the case of a graph from some storage being plavced in a general dataset.  
    +            return ((GraphView)graph).getDataset();
    +        if ( graph instanceof InfGraph )
    +            // InfGraph does actual pass done in its TransactionHandler.
    --- End diff --
    
    Not sure what this comment means?


---

[GitHub] jena pull request #369: JENA-1492: Pass down transactions from general datas...

Posted by ajs6f <gi...@git.apache.org>.
Github user ajs6f commented on a diff in the pull request:

    https://github.com/apache/jena/pull/369#discussion_r172254605
  
    --- Diff: jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java ---
    @@ -0,0 +1,240 @@
    +/*
    + * 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.jena.sparql.core;
    +
    +import java.util.*;
    +import java.util.function.Consumer;
    +
    +import org.apache.jena.graph.Graph;
    +import org.apache.jena.graph.TransactionHandler;
    +import org.apache.jena.query.ReadWrite;
    +import org.apache.jena.query.TxnType;
    +import org.apache.jena.reasoner.InfGraph;
    +import org.apache.jena.shared.LockMRSW;
    +import org.apache.jena.sparql.JenaTransactionException;
    +
    +/**
    + * A {@link Transactional} that passes the transaction operations down to transactions on
    + * independent graphs.
    + * <p>
    + * There are limitations:
    + * <ul>
    + * <li>we can't atomically do all the commits together in the crash situation.
    + * <li>This {@code Transactional} maintains a MRSW policy because that is all that is
    + * required of graphs in general.
    + * </ul>
    + * It does cover the important case of one graph ({@link DatasetGraphOne}) where the one
    + * graph is an InfGraph and should work when the graphs in the dataset is not changing or
    + * when a new memory graph is added mid-transaction.
    + * <p>
    + * This is not "nested transactions" - theer is no overall "commit" or "abort". If
    + * failure/restart occurs, some graphs may have commited and others not. It is the best
    + * that can be done given for an arbitrary collection of graphs, backed by different
    + * storage and having different capabilities.
    + * <p>
    + * Best practice is to change the graph membership outside of any transaction,
    + * ideally at setup time of the object using this class. (Caution: SPARQL Update
    + * can create graphs.   
    --- End diff --
    
    typo missing `)`


---