/debezium-connector-mongodb/src/main/java/io/debezium/connector/mongodb/ReplicaSets.java

https://github.com/debezium/debezium · Java · 231 lines · 123 code · 22 blank · 86 comment · 21 complexity · da7cabd3a2291b95684320af2daff436 MD5 · raw file

  1. /*
  2. * Copyright Debezium Authors.
  3. *
  4. * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
  5. */
  6. package io.debezium.connector.mongodb;
  7. import java.util.ArrayList;
  8. import java.util.Collection;
  9. import java.util.Collections;
  10. import java.util.HashMap;
  11. import java.util.HashSet;
  12. import java.util.List;
  13. import java.util.Map;
  14. import java.util.Objects;
  15. import java.util.Set;
  16. import java.util.function.Consumer;
  17. import java.util.regex.Pattern;
  18. import org.apache.kafka.connect.util.ConnectorUtils;
  19. import io.debezium.annotation.Immutable;
  20. import io.debezium.util.Strings;
  21. /**
  22. * A set of replica set specifications.
  23. *
  24. * @author Randall Hauch
  25. */
  26. @Immutable
  27. public class ReplicaSets {
  28. private static final Pattern REPLICA_DELIMITER_PATTERN = Pattern.compile(";");
  29. /**
  30. * Parse the supplied string for the information about the replica set hosts. The string is a semicolon-delimited list of
  31. * shard hosts (e.g., "{@code shard01=replicaSet1/host1:27017,host2:27017}"), replica set hosts (e.g.,
  32. * "{@code replicaSet1/host1:27017,host2:27017}"), and standalone hosts (e.g., "{@code host1:27017}" or
  33. * "{@code 1.2.3.4:27017}").
  34. *
  35. * @param hosts the hosts string; may be null
  36. * @return the replica sets; never null but possibly empty
  37. * @see ReplicaSets#hosts()
  38. */
  39. public static ReplicaSets parse(String hosts) {
  40. Set<ReplicaSet> replicaSets = new HashSet<>();
  41. if (hosts != null) {
  42. for (String replicaSetStr : REPLICA_DELIMITER_PATTERN.split(hosts.trim())) {
  43. if (!replicaSetStr.isEmpty()) {
  44. ReplicaSet replicaSet = ReplicaSet.parse(replicaSetStr);
  45. if (replicaSetStr != null) {
  46. replicaSets.add(replicaSet);
  47. }
  48. }
  49. }
  50. }
  51. return new ReplicaSets(replicaSets);
  52. }
  53. /**
  54. * Get an instance that contains no replica sets.
  55. *
  56. * @return the empty instance; never null
  57. */
  58. public static ReplicaSets empty() {
  59. return new ReplicaSets(null);
  60. }
  61. private final Map<String, ReplicaSet> replicaSetsByName = new HashMap<>();
  62. private final List<ReplicaSet> nonReplicaSets = new ArrayList<>();
  63. /**
  64. * Create a set of replica set specifications.
  65. *
  66. * @param rsSpecs the replica set specifications; may be null or empty
  67. */
  68. public ReplicaSets(Collection<ReplicaSet> rsSpecs) {
  69. if (rsSpecs != null) {
  70. rsSpecs.forEach(replicaSet -> {
  71. if (replicaSet.hasReplicaSetName()) {
  72. replicaSetsByName.put(replicaSet.replicaSetName(), replicaSet);
  73. }
  74. else {
  75. nonReplicaSets.add(replicaSet);
  76. }
  77. });
  78. }
  79. Collections.sort(nonReplicaSets);
  80. }
  81. /**
  82. * Get the number of replica sets.
  83. *
  84. * @return the replica set count
  85. */
  86. public int replicaSetCount() {
  87. return replicaSetsByName.size() + nonReplicaSets.size();
  88. }
  89. /**
  90. * Get the number of replica sets with names.
  91. *
  92. * @return the valid replica set count
  93. */
  94. public int validReplicaSetCount() {
  95. return replicaSetsByName.size();
  96. }
  97. /**
  98. * Perform the supplied function on each of the replica sets
  99. *
  100. * @param function the consumer function; may not be null
  101. */
  102. public void onEachReplicaSet(Consumer<ReplicaSet> function) {
  103. this.replicaSetsByName.values().forEach(function);
  104. this.nonReplicaSets.forEach(function);
  105. }
  106. /**
  107. * Subdivide this collection of replica sets into the maximum number of groups.
  108. *
  109. * @param maxSubdivisionCount the maximum number of subdivisions
  110. * @param subdivisionConsumer the function to be called with each subdivision; may not be null
  111. */
  112. public void subdivide(int maxSubdivisionCount, Consumer<ReplicaSets> subdivisionConsumer) {
  113. int numGroups = Math.min(replicaSetCount(), maxSubdivisionCount);
  114. if (numGroups <= 1) {
  115. // Just one replica set or subdivision ...
  116. subdivisionConsumer.accept(this);
  117. return;
  118. }
  119. ConnectorUtils.groupPartitions(all(), numGroups).forEach(rsList -> {
  120. subdivisionConsumer.accept(new ReplicaSets(rsList));
  121. });
  122. }
  123. /**
  124. * Get a copy of all of the {@link ReplicaSet} objects.
  125. *
  126. * @return the replica set objects; never null but possibly empty
  127. */
  128. public List<ReplicaSet> all() {
  129. List<ReplicaSet> replicaSets = new ArrayList<>();
  130. replicaSets.addAll(replicaSetsByName.values());
  131. replicaSets.addAll(nonReplicaSets);
  132. return replicaSets;
  133. }
  134. /**
  135. * Get a copy of all of the valid {@link ReplicaSet} objects that have names.
  136. *
  137. * @return the valid replica set objects; never null but possibly empty
  138. */
  139. public List<ReplicaSet> validReplicaSets() {
  140. List<ReplicaSet> replicaSets = new ArrayList<>();
  141. replicaSets.addAll(replicaSetsByName.values());
  142. return replicaSets;
  143. }
  144. /**
  145. * Get a copy of all of the {@link ReplicaSet} objects that have no names.
  146. *
  147. * @return the unnamed replica set objects; never null but possibly empty
  148. */
  149. public List<ReplicaSet> unnamedReplicaSets() {
  150. List<ReplicaSet> replicaSets = new ArrayList<>();
  151. replicaSets.addAll(nonReplicaSets);
  152. return replicaSets;
  153. }
  154. /**
  155. * Determine if one or more replica sets has been added or removed since the prior state.
  156. *
  157. * @param priorState the prior state of the replica sets; may be null
  158. * @return {@code true} if the replica sets have changed since the prior state, or {@code false} otherwise
  159. */
  160. public boolean haveChangedSince(ReplicaSets priorState) {
  161. if (priorState.replicaSetCount() != this.replicaSetCount()) {
  162. // At least one replica set has been added or removed ...
  163. return true;
  164. }
  165. if (this.replicaSetsByName.size() != priorState.replicaSetsByName.size()) {
  166. // The total number of replica sets hasn't changed, but the number of named replica sets has changed ...
  167. return true;
  168. }
  169. // We have the same number of named replica sets ...
  170. if (!this.replicaSetsByName.isEmpty()) {
  171. if (!this.replicaSetsByName.keySet().equals(priorState.replicaSetsByName.keySet())) {
  172. // The replica sets have different names ...
  173. return true;
  174. }
  175. // Otherwise, they have the same names and we don't care about the members ...
  176. }
  177. // None of the named replica sets has changed, so we have no choice to be compare the non-replica set members ...
  178. return this.nonReplicaSets.equals(priorState.nonReplicaSets) ? false : true;
  179. }
  180. /**
  181. * Get the string containing the host names for the replica sets. The result is a string with each replica set hosts
  182. * separated by a semicolon.
  183. *
  184. * @return the host names; never null
  185. * @see #parse(String)
  186. */
  187. public String hosts() {
  188. return Strings.join(";", all());
  189. }
  190. @Override
  191. public int hashCode() {
  192. return Objects.hash(replicaSetsByName, nonReplicaSets);
  193. }
  194. @Override
  195. public boolean equals(Object obj) {
  196. if (obj == this) {
  197. return true;
  198. }
  199. if (obj instanceof ReplicaSets) {
  200. ReplicaSets that = (ReplicaSets) obj;
  201. return this.replicaSetsByName.equals(that.replicaSetsByName) && this.nonReplicaSets.equals(that.nonReplicaSets);
  202. }
  203. return false;
  204. }
  205. @Override
  206. public String toString() {
  207. return hosts();
  208. }
  209. }