Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

KetchSystem.java 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. * Copyright (C) 2016, 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.ketch;
  11. import static org.eclipse.jgit.internal.ketch.KetchConstants.ACCEPTED;
  12. import static org.eclipse.jgit.internal.ketch.KetchConstants.COMMITTED;
  13. import static org.eclipse.jgit.internal.ketch.KetchConstants.CONFIG_KEY_TYPE;
  14. import static org.eclipse.jgit.internal.ketch.KetchConstants.CONFIG_SECTION_KETCH;
  15. import static org.eclipse.jgit.internal.ketch.KetchConstants.DEFAULT_TXN_NAMESPACE;
  16. import static org.eclipse.jgit.internal.ketch.KetchConstants.STAGE;
  17. import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_NAME;
  18. import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_REMOTE;
  19. import java.net.URISyntaxException;
  20. import java.time.Duration;
  21. import java.util.ArrayList;
  22. import java.util.List;
  23. import java.util.Random;
  24. import java.util.concurrent.Executors;
  25. import java.util.concurrent.ScheduledExecutorService;
  26. import java.util.concurrent.ThreadFactory;
  27. import java.util.concurrent.atomic.AtomicInteger;
  28. import org.eclipse.jgit.annotations.Nullable;
  29. import org.eclipse.jgit.lib.Config;
  30. import org.eclipse.jgit.lib.PersonIdent;
  31. import org.eclipse.jgit.lib.Repository;
  32. import org.eclipse.jgit.transport.RemoteConfig;
  33. import org.eclipse.jgit.transport.URIish;
  34. import org.eclipse.jgit.util.time.MonotonicClock;
  35. import org.eclipse.jgit.util.time.MonotonicSystemClock;
  36. import org.eclipse.jgit.util.time.ProposedTimestamp;
  37. import org.slf4j.Logger;
  38. import org.slf4j.LoggerFactory;
  39. /**
  40. * Ketch system-wide configuration.
  41. * <p>
  42. * This class provides useful defaults for testing and small proof of concepts.
  43. * Full scale installations are expected to subclass and override methods to
  44. * provide consistent configuration across all managed repositories.
  45. * <p>
  46. * Servers should configure their own
  47. * {@link java.util.concurrent.ScheduledExecutorService}.
  48. */
  49. public class KetchSystem {
  50. private static final Random RNG = new Random();
  51. /**
  52. * Get default executor, one thread per available processor.
  53. *
  54. * @return default executor, one thread per available processor.
  55. */
  56. public static ScheduledExecutorService defaultExecutor() {
  57. return DefaultExecutorHolder.I;
  58. }
  59. private final ScheduledExecutorService executor;
  60. private final MonotonicClock clock;
  61. private final String txnNamespace;
  62. private final String txnAccepted;
  63. private final String txnCommitted;
  64. private final String txnStage;
  65. /**
  66. * Create a default system with a thread pool of 1 thread per CPU.
  67. */
  68. public KetchSystem() {
  69. this(defaultExecutor(), new MonotonicSystemClock(), DEFAULT_TXN_NAMESPACE);
  70. }
  71. /**
  72. * Create a Ketch system with the provided executor service.
  73. *
  74. * @param executor
  75. * thread pool to run background operations.
  76. * @param clock
  77. * clock to create timestamps.
  78. * @param txnNamespace
  79. * reference namespace for the RefTree graph and associated
  80. * transaction state. Must begin with {@code "refs/"} and end
  81. * with {@code '/'}, for example {@code "refs/txn/"}.
  82. */
  83. public KetchSystem(ScheduledExecutorService executor, MonotonicClock clock,
  84. String txnNamespace) {
  85. this.executor = executor;
  86. this.clock = clock;
  87. this.txnNamespace = txnNamespace;
  88. this.txnAccepted = txnNamespace + ACCEPTED;
  89. this.txnCommitted = txnNamespace + COMMITTED;
  90. this.txnStage = txnNamespace + STAGE;
  91. }
  92. /**
  93. * Get executor to perform background operations.
  94. *
  95. * @return executor to perform background operations.
  96. */
  97. public ScheduledExecutorService getExecutor() {
  98. return executor;
  99. }
  100. /**
  101. * Get clock to obtain timestamps from.
  102. *
  103. * @return clock to obtain timestamps from.
  104. */
  105. public MonotonicClock getClock() {
  106. return clock;
  107. }
  108. /**
  109. * Get how long the leader will wait for the {@link #getClock()}'s
  110. * {@code ProposedTimestamp} used in commits proposed to the RefTree graph
  111. * ({@link #getTxnAccepted()})
  112. *
  113. * @return how long the leader will wait for the {@link #getClock()}'s
  114. * {@code ProposedTimestamp} used in commits proposed to the RefTree
  115. * graph ({@link #getTxnAccepted()}). Defaults to 5 seconds.
  116. */
  117. public Duration getMaxWaitForMonotonicClock() {
  118. return Duration.ofSeconds(5);
  119. }
  120. /**
  121. * Whether elections should require monotonically increasing commit
  122. * timestamps
  123. *
  124. * @return {@code true} if elections should require monotonically increasing
  125. * commit timestamps. This requires a very good
  126. * {@link org.eclipse.jgit.util.time.MonotonicClock}.
  127. */
  128. public boolean requireMonotonicLeaderElections() {
  129. return false;
  130. }
  131. /**
  132. * Get the namespace used for the RefTree graph and transaction management.
  133. *
  134. * @return reference namespace such as {@code "refs/txn/"}.
  135. */
  136. public String getTxnNamespace() {
  137. return txnNamespace;
  138. }
  139. /**
  140. * Get name of the accepted RefTree graph.
  141. *
  142. * @return name of the accepted RefTree graph.
  143. */
  144. public String getTxnAccepted() {
  145. return txnAccepted;
  146. }
  147. /**
  148. * Get name of the committed RefTree graph.
  149. *
  150. * @return name of the committed RefTree graph.
  151. */
  152. public String getTxnCommitted() {
  153. return txnCommitted;
  154. }
  155. /**
  156. * Get prefix for staged objects, e.g. {@code "refs/txn/stage/"}.
  157. *
  158. * @return prefix for staged objects, e.g. {@code "refs/txn/stage/"}.
  159. */
  160. public String getTxnStage() {
  161. return txnStage;
  162. }
  163. /**
  164. * Create new committer {@code PersonIdent} for ketch system
  165. *
  166. * @param time
  167. * timestamp for the committer.
  168. * @return identity line for the committer header of a RefTreeGraph.
  169. */
  170. public PersonIdent newCommitter(ProposedTimestamp time) {
  171. String name = "ketch"; //$NON-NLS-1$
  172. String email = "ketch@system"; //$NON-NLS-1$
  173. return new PersonIdent(name, email, time);
  174. }
  175. /**
  176. * Construct a random tag to identify a candidate during leader election.
  177. * <p>
  178. * Multiple processes trying to elect themselves leaders at exactly the same
  179. * time (rounded to seconds) using the same
  180. * {@link #newCommitter(ProposedTimestamp)} identity strings, for the same
  181. * term, may generate the same ObjectId for the election commit and falsely
  182. * assume they have both won.
  183. * <p>
  184. * Candidates add this tag to their election ballot commit to disambiguate
  185. * the election. The tag only needs to be unique for a given triplet of
  186. * {@link #newCommitter(ProposedTimestamp)}, system time (rounded to
  187. * seconds), and term. If every replica in the system uses a unique
  188. * {@code newCommitter} (such as including the host name after the
  189. * {@code "@"} in the email address) the tag could be the empty string.
  190. * <p>
  191. * The default implementation generates a few bytes of random data.
  192. *
  193. * @return unique tag; null or empty string if {@code newCommitter()} is
  194. * sufficiently unique to identify the leader.
  195. */
  196. @Nullable
  197. public String newLeaderTag() {
  198. int n = RNG.nextInt(1 << (6 * 4));
  199. return String.format("%06x", Integer.valueOf(n)); //$NON-NLS-1$
  200. }
  201. /**
  202. * Construct the KetchLeader instance of a repository.
  203. *
  204. * @param repo
  205. * local repository stored by the leader.
  206. * @return leader instance.
  207. * @throws java.net.URISyntaxException
  208. * a follower configuration contains an unsupported URI.
  209. */
  210. public KetchLeader createLeader(Repository repo)
  211. throws URISyntaxException {
  212. KetchLeader leader = new KetchLeader(this) {
  213. @Override
  214. protected Repository openRepository() {
  215. repo.incrementOpen();
  216. return repo;
  217. }
  218. };
  219. leader.setReplicas(createReplicas(leader, repo));
  220. return leader;
  221. }
  222. /**
  223. * Get the collection of replicas for a repository.
  224. * <p>
  225. * The collection of replicas must include the local repository.
  226. *
  227. * @param leader
  228. * the leader driving these replicas.
  229. * @param repo
  230. * repository to get the replicas of.
  231. * @return collection of replicas for the specified repository.
  232. * @throws java.net.URISyntaxException
  233. * a configured URI is invalid.
  234. */
  235. protected List<KetchReplica> createReplicas(KetchLeader leader,
  236. Repository repo) throws URISyntaxException {
  237. List<KetchReplica> replicas = new ArrayList<>();
  238. Config cfg = repo.getConfig();
  239. String localName = getLocalName(cfg);
  240. for (String name : cfg.getSubsections(CONFIG_KEY_REMOTE)) {
  241. if (!hasParticipation(cfg, name)) {
  242. continue;
  243. }
  244. ReplicaConfig kc = ReplicaConfig.newFromConfig(cfg, name);
  245. if (name.equals(localName)) {
  246. replicas.add(new LocalReplica(leader, name, kc));
  247. continue;
  248. }
  249. RemoteConfig rc = new RemoteConfig(cfg, name);
  250. List<URIish> uris = rc.getPushURIs();
  251. if (uris.isEmpty()) {
  252. uris = rc.getURIs();
  253. }
  254. for (URIish uri : uris) {
  255. String n = uris.size() == 1 ? name : uri.getHost();
  256. replicas.add(new RemoteGitReplica(leader, n, uri, kc, rc));
  257. }
  258. }
  259. return replicas;
  260. }
  261. private static boolean hasParticipation(Config cfg, String name) {
  262. return cfg.getString(CONFIG_KEY_REMOTE, name, CONFIG_KEY_TYPE) != null;
  263. }
  264. private static String getLocalName(Config cfg) {
  265. return cfg.getString(CONFIG_SECTION_KETCH, null, CONFIG_KEY_NAME);
  266. }
  267. static class DefaultExecutorHolder {
  268. private static final Logger log = LoggerFactory.getLogger(KetchSystem.class);
  269. static final ScheduledExecutorService I = create();
  270. private static ScheduledExecutorService create() {
  271. int cores = Runtime.getRuntime().availableProcessors();
  272. int threads = Math.max(5, cores);
  273. log.info("Using {} threads", Integer.valueOf(threads)); //$NON-NLS-1$
  274. return Executors.newScheduledThreadPool(
  275. threads,
  276. new ThreadFactory() {
  277. private final AtomicInteger threadCnt = new AtomicInteger();
  278. @Override
  279. public Thread newThread(Runnable r) {
  280. int id = threadCnt.incrementAndGet();
  281. Thread thr = new Thread(r);
  282. thr.setName("KetchExecutor-" + id); //$NON-NLS-1$
  283. return thr;
  284. }
  285. });
  286. }
  287. private DefaultExecutorHolder() {
  288. }
  289. }
  290. }