[ 
https://issues.apache.org/jira/browse/JENA-1492?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16386357#comment-16386357
 ] 

ASF GitHub Bot commented on JENA-1492:
--------------------------------------

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.


> Transactions not passed down for nested models.
> -----------------------------------------------
>
>                 Key: JENA-1492
>                 URL: https://issues.apache.org/jira/browse/JENA-1492
>             Project: Apache Jena
>          Issue Type: Bug
>          Components: Fuseki, TDB, TDB2
>    Affects Versions: Jena 3.7.0
>            Reporter: Andy Seaborne
>            Priority: Major
>         Attachments: config-tdb2-model.ttl
>
>
> From [users@ 
> email|https://lists.apache.org/thread.html/eacd92488360298a4916b2b1b0cf2d299797140be1960f84410f9b5d@%3Cusers.jena.apache.org%3E],
>  after the configuration sorted out.
> Hierarchies of models, such as inference models, on top of a TBD2 
> backed-graph do not pass down the transaction leading to
> {noformat}
> org.apache.jena.dboe.transaction.txn.TransactionException: Not in a 
> transaction
>     at 
> org.apache.jena.dboe.transaction.txn.TransactionalComponentLifecycle.checkTxn(TransactionalComponentLifecycle.java:417)
>     at 
> org.apache.jena.dboe.trans.bplustree.BPlusTree.getRootRead(BPlusTree.java:159)
>     at 
> org.apache.jena.dboe.trans.bplustree.BPlusTree.iterator(BPlusTree.java:348)
>     at 
> org.apache.jena.tdb2.store.tupletable.TupleIndexRecord.all(TupleIndexRecord.java:251)
>     at 
> org.apache.jena.tdb2.store.tupletable.TupleTable.find(TupleTable.java:148)
>     at 
> org.apache.jena.tdb2.store.nodetupletable.NodeTupleTableConcrete.find(NodeTupleTableConcrete.java:161)
>     at 
> org.apache.jena.tdb2.store.nodetupletable.NodeTupleTableConcrete.find(NodeTupleTableConcrete.java:150)
>     at 
> org.apache.jena.tdb2.store.nodetupletable.NodeTupleTableConcrete.findAsNodeIds(NodeTupleTableConcrete.java:141)
>     at org.apache.jena.tdb2.store.TripleTable.find(TripleTable.java:64)
>     at 
> org.apache.jena.tdb2.store.DatasetGraphTDB.findInDftGraph(DatasetGraphTDB.java:110)
>     at 
> org.apache.jena.sparql.core.DatasetGraphBaseFind.find(DatasetGraphBaseFind.java:47)
>     at 
> org.apache.jena.sparql.core.DatasetGraphWrapper.find(DatasetGraphWrapper.java:152)
>     at org.apache.jena.sparql.core.GraphView.graphBaseFind(GraphView.java:125)
>     at org.apache.jena.graph.impl.GraphBase.find(GraphBase.java:255)
> {noformat}
> Example configuration (full version attached):
> {noformat}
> :service  a                   fuseki:Service ;
>         fuseki:dataset                :dataset ;
>         ...
> ## Wrapper: Datset containing one TDB2-backed graph
> :dataset a ja:RDFDataset ;
>     ja:defaultGraph       :graph .
> ##  TDB2-backed graph
> :graph rdf:type tdb2:GraphTDB ;
>     tdb2:dataset :datasetTDB2 .
> ## TDB2 database
> :datasetTDB2 rdf:type tdb2:DatasetTDB2 ;
>     tdb2:location "DB2".
> {noformat}
>  



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to