]> source.dussan.org Git - jgit.git/commitdiff
Support per-BatchRefUpdate atomic transactions 62/70962/2
authorDave Borowitz <dborowitz@google.com>
Tue, 19 Apr 2016 13:24:18 +0000 (09:24 -0400)
committerDave Borowitz <dborowitz@google.com>
Tue, 19 Apr 2016 14:01:57 +0000 (10:01 -0400)
Repurpose RefDatabase#performsAtomicTransactions() slightly, to
indicate that the backend _supports_ atomic transactions, rather than
the current definition, which is that the backend always _uses_ atomic
transactions regardless of whether or not the caller actually wants
them. Allow BatchRefUpdate callers to turn off atomic transactions by
calling setAtomic(false). Defaulting to true means this is backwards
compatible.

Change-Id: I6df78d7df65ab147b4cce7764bd3101db985491c

org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeBatch.java
org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java

index 992e10bad65322a4f37373e32b3222a0a60dce6d..a99bd7e9115d207514f1c86e7996e5ee228d7543 100644 (file)
@@ -21,6 +21,7 @@ atLeastOnePathIsRequired=At least one path is required.
 atLeastOnePatternIsRequired=At least one pattern is required.
 atLeastTwoFiltersNeeded=At least two filters needed.
 atomicPushNotSupported=Atomic push not supported.
+atomicRefUpdatesNotSupported=Atomic ref updates not supported
 authenticationNotSupported=authentication not supported
 badBase64InputCharacterAt=Bad Base64 input character at {0} : {1} (decimal)
 badEntryDelimiter=Bad entry delimiter
index 7740a2bb80cc120a1798ac189d04a0e830856615..dc640fe314a0741146ce7d9ab6f5b4c25138af72 100644 (file)
@@ -80,6 +80,7 @@ public class JGitText extends TranslationBundle {
        /***/ public String atLeastOnePatternIsRequired;
        /***/ public String atLeastTwoFiltersNeeded;
        /***/ public String atomicPushNotSupported;
+       /***/ public String atomicRefUpdatesNotSupported;
        /***/ public String authenticationNotSupported;
        /***/ public String badBase64InputCharacterAt;
        /***/ public String badEntryDelimiter;
index ccf1b42b3d37e42af8dd730540b6651316e6aa32..de18eadb22fb02fd079c0c1ab489866287824642 100644 (file)
@@ -280,7 +280,7 @@ public class InMemoryRepository extends DfsRepository {
                                @Override
                                public void execute(RevWalk walk, ProgressMonitor monitor)
                                                throws IOException {
-                                       if (performsAtomicTransactions()) {
+                                       if (performsAtomicTransactions() && isAtomic()) {
                                                try {
                                                        lock.writeLock().lock();
                                                        batch(getCommands());
index a55a9f51e7313a5447cd57596540f41b1f886083..1cccd79810aecfd3794e75b3f3c5b22330e1558f 100644 (file)
@@ -98,8 +98,12 @@ class RefTreeBatch extends BatchRefUpdate {
                                }
                                if (c.getType() == UPDATE_NONFASTFORWARD) {
                                        c.setResult(REJECTED_NONFASTFORWARD);
-                                       ReceiveCommand.abort(getCommands());
-                                       return;
+                                       if (isAtomic()) {
+                                               ReceiveCommand.abort(getCommands());
+                                               return;
+                                       } else {
+                                               continue;
+                                       }
                                }
                        }
                        todo.add(new Command(rw, c));
index d7e930831e2dd709ddfb22dec4350efaa24043af..35cadd3c97b662b7fedf27831d705d7dbc8c88b0 100644 (file)
@@ -89,6 +89,9 @@ public class BatchRefUpdate {
        /** Push certificate associated with this update. */
        private PushCertificate pushCert;
 
+       /** Whether updates should be atomic. */
+       private boolean atomic;
+
        /**
         * Initialize a new batch update.
         *
@@ -98,6 +101,7 @@ public class BatchRefUpdate {
        protected BatchRefUpdate(RefDatabase refdb) {
                this.refdb = refdb;
                this.commands = new ArrayList<ReceiveCommand>();
+               this.atomic = refdb.performsAtomicTransactions();
        }
 
        /**
@@ -199,6 +203,36 @@ public class BatchRefUpdate {
                return refLogMessage == null;
        }
 
+       /**
+        * Request that all updates in this batch be performed atomically.
+        * <p>
+        * When atomic updates are used, either all commands apply successfully, or
+        * none do. Commands that might have otherwise succeeded are rejected with
+        * {@code REJECTED_OTHER_REASON}.
+        * <p>
+        * This method only works if the underlying ref database supports atomic
+        * transactions, i.e. {@link RefDatabase#performsAtomicTransactions()} returns
+        * true. Calling this method with true if the underlying ref database does not
+        * support atomic transactions will cause all commands to fail with {@code
+        * REJECTED_OTHER_REASON}.
+        *
+        * @param atomic whether updates should be atomic.
+        * @return {@code this}
+        * @since 4.4
+        */
+       public BatchRefUpdate setAtomic(boolean atomic) {
+               this.atomic = atomic;
+               return this;
+       }
+
+       /**
+        * @return atomic whether updates should be atomic.
+        * @since 4.4
+        */
+       public boolean isAtomic() {
+               return atomic;
+       }
+
        /**
         * Set a push certificate associated with this update.
         * <p>
@@ -271,6 +305,10 @@ public class BatchRefUpdate {
         * <p>
         * The default implementation of this method performs a sequential reference
         * update over each reference.
+        * <p>
+        * Implementations must respect the atomicity requirements of the underlying
+        * database as described in {@link #setAtomic(boolean)} and {@link
+        * RefDatabase#performsAtomicTransactions()}.
         *
         * @param walk
         *            a RevWalk to parse tags in case the storage system wants to
@@ -284,6 +322,17 @@ public class BatchRefUpdate {
         */
        public void execute(RevWalk walk, ProgressMonitor monitor)
                        throws IOException {
+
+               if (atomic && !refdb.performsAtomicTransactions()) {
+                       for (ReceiveCommand c : commands) {
+                               if (c.getResult() == NOT_ATTEMPTED) {
+                                       c.setResult(REJECTED_OTHER_REASON,
+                                                       JGitText.get().atomicRefUpdatesNotSupported);
+                               }
+                       }
+                       return;
+               }
+
                monitor.beginTask(JGitText.get().updatingReferences, commands.size());
                List<ReceiveCommand> commands2 = new ArrayList<ReceiveCommand>(
                                commands.size());
index c0c3862c8b97de109d7cd99bfc5fe638294d9571..517c8aa538e2b178227fc5f0aa653d80036445a6 100644 (file)
@@ -207,8 +207,25 @@ public abstract class RefDatabase {
        }
 
        /**
-        * @return if the database performs {@code newBatchUpdate()} as an atomic
-        *         transaction.
+        * Whether the database is capable of performing batch updates as atomic
+        * transactions.
+        * <p>
+        * If true, by default {@link BatchRefUpdate} instances will perform updates
+        * atomically, meaning either all updates will succeed, or all updates will
+        * fail. It is still possible to turn off this behavior on a per-batch basis
+        * by calling {@code update.setAtomic(false)}.
+        * <p>
+        * If false, {@link BatchRefUpdate} instances will never perform updates
+        * atomically, and calling {@code update.setAtomic(true)} will cause the
+        * entire batch to fail with {@code REJECTED_OTHER_REASON}.
+        * <p>
+        * This definition of atomicity is stronger than what is provided by
+        * {@link org.eclipse.jgit.transport.ReceivePack}. {@code ReceivePack} will
+        * attempt to reject all commands if it knows in advance some commands may
+        * fail, even if the storage layer does not support atomic transactions. Here,
+        * atomicity applies even in the case of unforeseeable errors.
+        *
+        * @return whether transactions are atomic by default.
         * @since 3.6
         */
        public boolean performsAtomicTransactions() {