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