You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DfsReftableDatabase.java 7.1KB

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