View Javadoc
1   /*
2    * Copyright (C) 2017, Google Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.internal.storage.dfs;
12  
13  import java.io.IOException;
14  import java.util.Arrays;
15  import java.util.HashSet;
16  import java.util.List;
17  import java.util.Map;
18  import java.util.Set;
19  import java.util.TreeSet;
20  import java.util.concurrent.locks.ReentrantLock;
21  
22  import org.eclipse.jgit.annotations.Nullable;
23  import org.eclipse.jgit.internal.storage.reftable.MergedReftable;
24  import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
25  import org.eclipse.jgit.internal.storage.reftable.ReftableDatabase;
26  import org.eclipse.jgit.lib.BatchRefUpdate;
27  import org.eclipse.jgit.lib.NullProgressMonitor;
28  import org.eclipse.jgit.lib.ObjectId;
29  import org.eclipse.jgit.lib.Ref;
30  import org.eclipse.jgit.revwalk.RevWalk;
31  import org.eclipse.jgit.transport.ReceiveCommand;
32  import org.eclipse.jgit.util.RefList;
33  import org.eclipse.jgit.util.RefMap;
34  
35  /**
36   * A {@link org.eclipse.jgit.internal.storage.dfs.DfsRefDatabase} that uses
37   * reftable for storage.
38   * <p>
39   * A {@code DfsRefDatabase} instance is thread-safe.
40   * <p>
41   * Implementors may wish to use
42   * {@link org.eclipse.jgit.internal.storage.dfs.DfsPackDescription#getMaxUpdateIndex()}
43   * as the primary key identifier for a
44   * {@link org.eclipse.jgit.internal.storage.pack.PackExt#REFTABLE} only pack
45   * description, ensuring that when there are competing transactions one wins,
46   * and one will fail.
47   */
48  public class DfsReftableDatabase extends DfsRefDatabase {
49  	final ReftableDatabase reftableDatabase;
50  
51  	private DfsReader ctx;
52  	private DfsReftableStack stack;
53  
54  	/**
55  	 * Initialize the reference database for a repository.
56  	 *
57  	 * @param repo
58  	 *            the repository this database instance manages references for.
59  	 */
60  	protected DfsReftableDatabase(DfsRepository repo) {
61  		super(repo);
62  		reftableDatabase = new ReftableDatabase() {
63  			@Override
64  			public MergedReftable openMergedReftable() throws IOException {
65  				DfsReftableDatabase.this.getLock().lock();
66  				try {
67  					return new MergedReftable(stack().readers());
68  				} finally {
69  					DfsReftableDatabase.this.getLock().unlock();
70  				}
71  			}
72  		};
73  		stack = null;
74  	}
75  
76  	/** {@inheritDoc} */
77  	@Override
78  	public boolean hasVersioning() {
79  		return true;
80  	}
81  
82  	/** {@inheritDoc} */
83  	@Override
84  	public boolean performsAtomicTransactions() {
85  		return true;
86  	}
87  
88  	/** {@inheritDoc} */
89  	@Override
90  	public BatchRefUpdate newBatchUpdate() {
91  		DfsObjDatabase odb = getRepository().getObjectDatabase();
92  		return new DfsReftableBatchRefUpdate(this, odb);
93  	}
94  
95  	/**
96  	 * Get configuration to write new reftables with.
97  	 *
98  	 * @return configuration to write new reftables with.
99  	 */
100 	public ReftableConfig getReftableConfig() {
101 		return new ReftableConfig(getRepository());
102 	}
103 
104 	/**
105 	 * Get the lock protecting this instance's state.
106 	 *
107 	 * @return the lock protecting this instance's state.
108 	 */
109 	protected ReentrantLock getLock() {
110 		return reftableDatabase.getLock();
111 	}
112 
113 	/**
114 	 * Whether to compact reftable instead of extending the stack depth.
115 	 *
116 	 * @return {@code true} if commit of a new small reftable should try to
117 	 *         replace a prior small reftable by performing a compaction,
118 	 *         instead of extending the stack depth.
119 	 */
120 	protected boolean compactDuringCommit() {
121 		return true;
122 	}
123 
124 
125 	/**
126 	 * Obtain a handle to the stack of reftables. Must hold lock.
127 	 *
128 	 * @return (possibly cached) handle to the stack.
129 	 * @throws java.io.IOException
130 	 *             if tables cannot be opened.
131 	 */
132 	protected DfsReftableStack stack() throws IOException {
133 		if (!getLock().isLocked()) {
134 			throw new IllegalStateException("most hold lock to access stack"); //$NON-NLS-1$
135 		}
136 		DfsObjDatabase odb = getRepository().getObjectDatabase();
137 
138 		if (ctx == null) {
139 			ctx = odb.newReader();
140 		}
141 		if (stack == null) {
142 			stack = DfsReftableStack.open(ctx, Arrays.asList(odb.getReftables()));
143 		}
144 		return stack;
145 	}
146 
147 	@Override
148 	public boolean isNameConflicting(String refName) throws IOException {
149 		return reftableDatabase.isNameConflicting(refName, new TreeSet<>(), new HashSet<>());
150 	}
151 
152 	/** {@inheritDoc} */
153 	@Override
154 	public Ref exactRef(String name) throws IOException {
155 		return reftableDatabase.exactRef(name);
156 	}
157 
158 	/** {@inheritDoc} */
159 	@Override
160 	public Map<String, Ref> getRefs(String prefix) throws IOException {
161 		List<Ref> refs = reftableDatabase.getRefsByPrefix(prefix);
162 		RefList.Builder<Ref> builder = new RefList.Builder<>(refs.size());
163 		for (Ref r : refs) {
164 			builder.add(r);
165 		}
166 		return new RefMap(prefix, builder.toRefList(), RefList.emptyList(),
167 			RefList.emptyList());
168 	}
169 
170 	/** {@inheritDoc} */
171 	@Override
172 	public List<Ref> getRefsByPrefix(String prefix) throws IOException {
173 
174 		return reftableDatabase.getRefsByPrefix(prefix);
175 	}
176 
177 	/** {@inheritDoc} */
178 	@Override
179 	public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
180 		if (!getReftableConfig().isIndexObjects()) {
181 			return super.getTipsWithSha1(id);
182 		}
183 		return reftableDatabase.getTipsWithSha1(id);
184 	}
185 
186 	/** {@inheritDoc} */
187 	@Override
188 	public boolean hasFastTipsWithSha1() throws IOException {
189 		return reftableDatabase.hasFastTipsWithSha1();
190 	}
191 
192 	/** {@inheritDoc} */
193 	@Override
194 	public Ref" href="../../../../../../org/eclipse/jgit/lib/Ref.html#Ref">Ref peel(Ref ref) throws IOException {
195 		Ref oldLeaf = ref.getLeaf();
196 		if (oldLeaf.isPeeled() || oldLeaf.getObjectId() == null) {
197 			return ref;
198 		}
199 		return recreate(ref, doPeel(oldLeaf), hasVersioning());
200 	}
201 
202 	@Override
203 	boolean exists() throws IOException {
204 		DfsObjDatabase odb = getRepository().getObjectDatabase();
205 		return odb.getReftables().length > 0;
206 	}
207 
208 	@Override
209 	void clearCache() {
210 		getLock().lock();
211 		try {
212 			if (ctx != null) {
213 				ctx.close();
214 				ctx = null;
215 			}
216 			reftableDatabase.clearCache();
217 			if (stack != null) {
218 				stack.close();
219 				stack = null;
220 			}
221 		} finally {
222 			getLock().unlock();
223 		}
224 	}
225 
226 	/** {@inheritDoc} */
227 	@Override
228 	protected boolean compareAndPut(Ref/../../../../org/eclipse/jgit/lib/Ref.html#Ref">Ref oldRef, @Nullable Ref newRef)
229 			throws IOException {
230 		ReceiveCommand cmd = ReftableDatabase.toCommand(oldRef, newRef);
231 		try (RevWalkvwalk/RevWalk.html#RevWalk">RevWalk rw = new RevWalk(getRepository())) {
232 			rw.setRetainBody(false);
233 			newBatchUpdate().setAllowNonFastForwards(true).addCommand(cmd)
234 					.execute(rw, NullProgressMonitor.INSTANCE);
235 		}
236 		switch (cmd.getResult()) {
237 		case OK:
238 			return true;
239 		case REJECTED_OTHER_REASON:
240 			throw new IOException(cmd.getMessage());
241 		case LOCK_FAILURE:
242 		default:
243 			return false;
244 		}
245 	}
246 
247 	/** {@inheritDoc} */
248 	@Override
249 	protected boolean compareAndRemove(Ref oldRef) throws IOException {
250 		return compareAndPut(oldRef, null);
251 	}
252 
253 	/** {@inheritDoc} */
254 	@Override
255 	protected RefCache scanAllRefs() throws IOException {
256 		throw new UnsupportedOperationException();
257 	}
258 
259 	@Override
260 	void stored(Ref ref) {
261 		// Unnecessary; DfsReftableBatchRefUpdate calls clearCache().
262 	}
263 
264 	@Override
265 	void removed(String refName) {
266 		// Unnecessary; DfsReftableBatchRefUpdate calls clearCache().
267 	}
268 
269 	/** {@inheritDoc} */
270 	@Override
271 	protected void cachePeeledState(Refef="../../../../../../org/eclipse/jgit/lib/Ref.html#Ref">Ref oldLeaf, Ref newLeaf) {
272 		// Do not cache peeled state in reftable.
273 	}
274 
275 }