@Override @SuppressWarnings("unchecked") public Listener onEvent(String event, Consumer listener) { return eventListeners .computeIfAbsent(Assert.notNull(event, "event"), e -> new Listeners<>()) .add(Assert.notNull(listener, "listener")); }
/** * @throws IllegalStateException if candidate is not positive or if term, logIndex or logTerm * are negative */ @Override public VoteRequest build() { super.build(); Assert.stateNot(request.term < 0, "term must not be negative"); Assert.stateNot(request.logIndex < 0, "log index must not be negative"); Assert.stateNot(request.logTerm < 0, "log term must not be negative"); return request; }
@Override public <T extends Operation<U>, U> StateMachineExecutor register( Class<T> type, Function<Commit<T>, U> callback) { Assert.notNull(type, "type"); Assert.notNull(callback, "callback"); operations.put(type, callback); return this; }
/** * Sets the maximum number of allows entries per segment, returning the builder for method * chaining. * * <p>The maximum entry count dictates when logs should roll over to new segments. As entries * are written to a segment of the log, if the entry count in that segment meets the configured * maximum entry count, the log will create a new segment and append new entries to that * segment. * * <p>By default, the maximum entries per segment is {@code 1024 * 1024}. * * @param maxEntriesPerSegment The maximum number of entries allowed per segment. * @return The storage builder. * @throws IllegalArgumentException If the {@code maxEntriesPerSegment} not greater than the * default max entries per segment */ public Builder withMaxEntriesPerSegment(int maxEntriesPerSegment) { Assert.arg(maxEntriesPerSegment > 0, "max entries per segment must be positive"); Assert.argNot( maxEntriesPerSegment > DEFAULT_MAX_ENTRIES_PER_SEGMENT, "max entries per segment cannot be greater than " + DEFAULT_MAX_ENTRIES_PER_SEGMENT); storage.maxEntriesPerSegment = maxEntriesPerSegment; return this; }
private Builder(Address clientAddress, Address serverAddress, Collection<Address> members) { this.members = Assert.notNull(members, "members"); Serializer serializer = new Serializer(); this.clientAddress = Assert.notNull(clientAddress, "clientAddress"); this.clientBuilder = CopycatClient.builder(members).withSerializer(serializer.clone()); this.serverBuilder = CopycatServer.builder(clientAddress, serverAddress, members) .withSerializer(serializer.clone()); }
/** @throws IllegalStateException if status is OK and members is null */ @Override public RegisterResponse build() { super.build(); Assert.stateNot( response.status == Status.OK && response.members == null, "members cannot be null"); Assert.stateNot( response.status == Status.OK && response.timeout <= 0, "timeout must be positive"); return response; }
public ClientSession( UUID clientId, Transport transport, Collection<Address> members, Serializer serializer) { this.clientId = Assert.notNull(clientId, "clientId"); this.client = Assert.notNull(transport, "transport").client(); this.members = new HashSet<>(Assert.notNull(members, "members")); this.context = new SingleThreadContext( "copycat-client-" + clientId.toString(), Assert.notNull(serializer, "serializer").clone()); this.connectMembers = new ArrayList<>(members); }
private ClientSession( ClientConnection connection, ClientSessionState state, ThreadContext context, ConnectionStrategy connectionStrategy) { this.connection = Assert.notNull(connection, "connection"); this.state = Assert.notNull(state, "state"); this.manager = new ClientSessionManager(connection, state, context, connectionStrategy); ClientSequencer sequencer = new ClientSequencer(state); this.listener = new ClientSessionListener(connection, state, sequencer, context); this.submitter = new ClientSessionSubmitter(connection, state, sequencer, context); }
@Override public <T extends Operation<Void>> StateMachineExecutor register( Class<T> type, Consumer<Commit<T>> callback) { Assert.notNull(type, "type"); Assert.notNull(callback, "callback"); operations.put( type, (Function<Commit<T>, Void>) commit -> { callback.accept(commit); return null; }); return this; }
@Override public CompletableFuture<Void> listen(Address address, Consumer<Connection> listener) { Assert.notNull(address, "address"); Assert.notNull(listener, "listener"); if (listening) return CompletableFuture.completedFuture(null); ThreadContext context = ThreadContext.currentContextOrThrow(); synchronized (this) { if (listenFuture == null) { listenFuture = new CompletableFuture<>(); listen(address, listener, context); } } return listenFuture; }
/** * Sets the maximum segment size in bytes, returning the builder for method chaining. * * <p>The maximum segment size dictates when logs should roll over to new segments. As entries * are written to a segment of the log, once the size of the segment surpasses the configured * maximum segment size, the log will create a new segment and append new entries to that * segment. * * <p>By default, the maximum segment size is {@code 1024 * 1024 * 32}. * * @param maxSegmentSize The maximum segment size in bytes. * @return The storage builder. * @throws IllegalArgumentException If the {@code maxSegmentSize} is not positive */ public Builder withMaxSegmentSize(int maxSegmentSize) { Assert.arg( maxSegmentSize > SegmentDescriptor.BYTES, "maxSegmentSize must be greater than " + SegmentDescriptor.BYTES); storage.maxSegmentSize = maxSegmentSize; return this; }
/** * Returns a boolean value indicating whether the selector state would be changed by the given * members. */ private boolean changed(Address leader, Collection<Address> servers) { Assert.notNull(servers, "servers"); Assert.argNot(servers.isEmpty(), "servers list cannot be empty"); if (this.leader != null && leader == null) { return true; } else if (this.leader == null && leader != null) { Assert.arg(servers.contains(leader), "leader must be present in servers list"); return true; } else if (this.leader != null && !this.leader.equals(leader)) { Assert.arg(servers.contains(leader), "leader must be present in servers list"); return true; } else if (!matches(this.servers, servers)) { return true; } return false; }
/** @throws IllegalStateException if {@code term} is negative */ @Override public VoteResponse build() { super.build(); if (response.status == Response.Status.OK) { Assert.stateNot(response.term < 0, "term cannot be negative"); } return response; }
@Override public Snapshot complete() { Buffer buffer = FileBuffer.allocate(file.file(), SnapshotDescriptor.BYTES); try (SnapshotDescriptor descriptor = new SnapshotDescriptor(buffer)) { Assert.stateNot(descriptor.locked(), "cannot complete locked snapshot descriptor"); descriptor.lock(); } return super.complete(); }
@Override public synchronized SnapshotReader reader() { Assert.state(file.file().exists(), "missing snapshot file: %s", file.file()); Buffer buffer = FileBuffer.allocate(file.file(), SnapshotDescriptor.BYTES); SnapshotDescriptor descriptor = new SnapshotDescriptor(buffer); int length = buffer.position(SnapshotDescriptor.BYTES).readInt(); return openReader( new SnapshotReader( buffer.mark().limit(SnapshotDescriptor.BYTES + Integer.BYTES + length), this, store.serializer()), descriptor); }
@Override public Session publish(String event, Object message) { Assert.notNull(event, "event"); context .executor() .execute( () -> { Listeners<Object> listeners = eventListeners.get(event); if (listeners != null) { listeners.accept(message); } }); return this; }
SnapshotWriter(Buffer buffer, Snapshot snapshot, Serializer serializer) { this.buffer = Assert.notNull(buffer, "buffer"); this.snapshot = Assert.notNull(snapshot, "snapshot"); this.serializer = Assert.notNull(serializer, "serializer"); }
/** * Sets the log directory, returning the builder for method chaining. * * <p>The log will write segment files into the provided directory. If multiple {@link Storage} * objects are located on the same machine, they write logs to different directories. * * @param directory The log directory. * @return The storage builder. * @throws NullPointerException If the {@code directory} is {@code null} */ public Builder withDirectory(String directory) { return withDirectory(new File(Assert.notNull(directory, "directory"))); }
@Override public Listener<Session> onClose(Consumer<Session> listener) { return closeListeners.add(Assert.notNull(listener, "listener")); }
/** * Sets the log directory, returning the builder for method chaining. * * <p>The log will write segment files into the provided directory. If multiple {@link Storage} * objects are located on the same machine, they write logs to different directories. * * @param directory The log directory. * @return The storage builder. * @throws NullPointerException If the {@code directory} is {@code null} */ public Builder withDirectory(File directory) { storage.directory = Assert.notNull(directory, "directory"); return this; }
@Override public Listener<Session> onOpen(Consumer<Session> listener) { return openListeners.add(Assert.notNull(listener, "listener")); }
/** @throws NullPointerException if {@code directory} is null */ public Storage(String directory) { this(new File(Assert.notNull(directory, "directory"))); }
public Storage(StorageLevel storageLevel) { this.storageLevel = Assert.notNull(storageLevel, "storageLevel"); }
/** * Sets the percentage of entries in the segment that must be cleaned before a segment can be * compacted, returning the builder for method chaining. * * <p>The compaction threshold is used during {@link * io.atomix.copycat.server.storage.compaction.Compaction#MINOR minor compaction} to determine * the set of segments to compact. By default, the compaction threshold is {@code 0.5}. * Increasing the compaction threshold will increase the number of {@link * io.atomix.copycat.server.storage.entry.Entry entries} that must be cleaned from the segment * before compaction and thus decrease the likelihood that a segment will be compacted. * Conversely, decreasing the compaction threshold will increase the frequency of compaction at * the cost of unnecessary I/O. * * @see io.atomix.copycat.server.storage.compaction.MinorCompactionManager * @param threshold The segment compact threshold. * @return The storage builder. */ public Builder withCompactionThreshold(double threshold) { storage.compactionThreshold = Assert.argNot(threshold, threshold <= 0, "threshold must be positive"); return this; }
/** * Sets the major compaction interval, returning the builder for method chaining. * * <p>The major compaction interval dictates the interval at which the {@link * io.atomix.copycat.server.storage.compaction.MajorCompactionManager} should evaluate {@link * Segment}s in the log for major compaction. Because of the performance costs of major * compaction, it is recommended that the major compaction interval be at least an order of * magnitude greater than the minor compaction interval. * * @see io.atomix.copycat.server.storage.compaction.MajorCompactionManager * @see io.atomix.copycat.server.storage.compaction.MajorCompactionTask * @param interval The major compaction interval. * @return The storage builder. */ public Builder withMajorCompactionInterval(Duration interval) { storage.majorCompactionInterval = Assert.notNull(interval, "interval"); return this; }
/** * Sets the request member. * * @param member The request member. * @return The request builder. * @throws NullPointerException if {@code member} is null */ public Builder withMember(Address member) { request.member = Assert.notNull(member, "member"); return this; }
public ResourceManagerState(ResourceRegistry registry) { this.registry = Assert.notNull(registry, "registry"); }
/** * Sets the maximum size of snapshot files on disk, returning the builder for method chaining. * * <p>The maximum snapshot size dictates the size in bytes of a single snapshot file on disk. * Reducing the maximum snapshot file size can help ensure that the system is not bogged down * with storing and replicating snapshots. By default, snapshots are practically unlimited with * a 2GB limit, but in practice they should be fairly small. * * @param maxSnapshotSize The maximum snapshot size in bytes. * @return The storage builder. * @throws IllegalArgumentException if the {@code maxSnapshotSize} is not positive or is less * than {@code 64} */ public Builder withMaxSnapshotSize(int maxSnapshotSize) { storage.maxSnapshotSize = Assert.arg(maxSnapshotSize, maxSnapshotSize >= 64, "max snapshot size must be positive"); return this; }
/** @throws IllegalStateException if member is null */ @Override public LeaveRequest build() { super.build(); Assert.state(request.member != null, "member cannot be null"); return request; }
/** * Sets the number of log compaction threads, returning the builder for method chaining. * * <p>The compaction thread count dictates the parallelism with which the log {@link * io.atomix.copycat.server.storage.compaction.Compactor} can rewrite segments in the log. By * default, the log uses {@code Runtime.getRuntime().availableProcessors() / 2} compaction * threads. * * @param compactionThreads The number of log compaction threads. * @return The storage builder. * @throws IllegalArgumentException if {@code compactionThreads} is not positive */ public Builder withCompactionThreads(int compactionThreads) { storage.compactionThreads = Assert.arg( compactionThreads, compactionThreads > 0, "compactionThreads must be positive"); return this; }