software.amazon.kinesis.leases.Lease Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of amazon-kinesis-client Show documentation
Show all versions of amazon-kinesis-client Show documentation
The Amazon Kinesis Client Library for Java enables Java developers to easily consume and process data
from Amazon Kinesis.
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates.
* Licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package software.amazon.kinesis.leases;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import software.amazon.kinesis.common.HashKeyRangeForLease;
import software.amazon.kinesis.retrieval.kpl.ExtendedSequenceNumber;
/**
* This class contains data pertaining to a Lease. Distributed systems may use leases to partition work across a
* fleet of workers. Each unit of work (identified by a leaseKey) has a corresponding Lease. Every worker will contend
* for all leases - only one worker will successfully take each one. The worker should hold the lease until it is ready to stop
* processing the corresponding unit of work, or until it fails. When the worker stops holding the lease, another worker will
* take and hold the lease.
*/
@NoArgsConstructor
@Getter
@Accessors(fluent = true)
@EqualsAndHashCode(exclude = {"concurrencyToken", "lastCounterIncrementNanos", "childShardIds", "pendingCheckpointState", "isMarkedForLeaseSteal"})
@ToString
public class Lease {
/**
* See javadoc for System.nanoTime - summary:
*
* Sometimes System.nanoTime's return values will wrap due to overflow. When they do, the difference between two
* values will be very large. We will consider leases to be expired if they are more than a year old.
*/
private static final long MAX_ABS_AGE_NANOS = TimeUnit.DAYS.toNanos(365);
/**
* Identifies the unit of work associated with this lease.
*/
private String leaseKey;
/**
* Current owner of the lease, may be null.
*/
private String leaseOwner;
/**
* LeaseCounter is incremented periodically by the holder of the lease. Used for optimistic locking.
*/
private Long leaseCounter = 0L;
/**
* This field is used to prevent updates to leases that we have lost and re-acquired. It is deliberately not
* persisted in DynamoDB and excluded from hashCode and equals.
*/
private UUID concurrencyToken;
/**
* This field is used by LeaseRenewer and LeaseTaker to track the last time a lease counter was incremented. It is
* deliberately not persisted in DynamoDB and excluded from hashCode and equals.
*/
private Long lastCounterIncrementNanos;
/**
* Most recently application-supplied checkpoint value. During fail over, the new worker will pick up after
* the old worker's last checkpoint.
*/
private ExtendedSequenceNumber checkpoint;
/**
* Pending checkpoint, possibly null.
*/
private ExtendedSequenceNumber pendingCheckpoint;
/**
* Last pending checkpoint state, possibly null. Deliberately excluded from hashCode and equals.
*/
private byte[] pendingCheckpointState;
/**
* Denotes whether the lease is marked for stealing. Deliberately excluded from hashCode and equals and
* not persisted in DynamoDB.
*/
@Setter
private boolean isMarkedForLeaseSteal;
/**
* Count of distinct lease holders between checkpoints.
*/
private Long ownerSwitchesSinceCheckpoint = 0L;
private final Set parentShardIds = new HashSet<>();
private final Set childShardIds = new HashSet<>();
private HashKeyRangeForLease hashKeyRangeForLease;
/**
* Copy constructor, used by clone().
*
* @param lease lease to copy
*/
protected Lease(Lease lease) {
this(lease.leaseKey(), lease.leaseOwner(), lease.leaseCounter(), lease.concurrencyToken(),
lease.lastCounterIncrementNanos(), lease.checkpoint(), lease.pendingCheckpoint(),
lease.ownerSwitchesSinceCheckpoint(), lease.parentShardIds(), lease.childShardIds(),
lease.pendingCheckpointState(), lease.hashKeyRangeForLease());
}
@Deprecated
public Lease(final String leaseKey, final String leaseOwner, final Long leaseCounter,
final UUID concurrencyToken, final Long lastCounterIncrementNanos,
final ExtendedSequenceNumber checkpoint, final ExtendedSequenceNumber pendingCheckpoint,
final Long ownerSwitchesSinceCheckpoint, final Set parentShardIds) {
this(leaseKey, leaseOwner, leaseCounter, concurrencyToken, lastCounterIncrementNanos, checkpoint, pendingCheckpoint,
ownerSwitchesSinceCheckpoint, parentShardIds, new HashSet<>(), null, null);
}
public Lease(final String leaseKey, final String leaseOwner, final Long leaseCounter,
final UUID concurrencyToken, final Long lastCounterIncrementNanos,
final ExtendedSequenceNumber checkpoint, final ExtendedSequenceNumber pendingCheckpoint,
final Long ownerSwitchesSinceCheckpoint, final Set parentShardIds, final Set childShardIds,
final byte[] pendingCheckpointState, final HashKeyRangeForLease hashKeyRangeForLease) {
this.leaseKey = leaseKey;
this.leaseOwner = leaseOwner;
this.leaseCounter = leaseCounter;
this.concurrencyToken = concurrencyToken;
this.lastCounterIncrementNanos = lastCounterIncrementNanos;
this.checkpoint = checkpoint;
this.pendingCheckpoint = pendingCheckpoint;
this.ownerSwitchesSinceCheckpoint = ownerSwitchesSinceCheckpoint;
if (parentShardIds != null) {
this.parentShardIds.addAll(parentShardIds);
}
if (childShardIds != null) {
this.childShardIds.addAll(childShardIds);
}
this.hashKeyRangeForLease = hashKeyRangeForLease;
this.pendingCheckpointState = pendingCheckpointState;
this.isMarkedForLeaseSteal = false;
}
/**
* @return shardIds that parent this lease. Used for resharding.
*/
public Set parentShardIds() {
return new HashSet<>(parentShardIds);
}
/**
* Updates this Lease's mutable, application-specific fields based on the passed-in lease object. Does not update
* fields that are internal to the leasing library (leaseKey, leaseOwner, leaseCounter).
*
* @param lease
*/
public void update(final Lease lease) {
ownerSwitchesSinceCheckpoint(lease.ownerSwitchesSinceCheckpoint());
checkpoint(lease.checkpoint);
pendingCheckpoint(lease.pendingCheckpoint);
pendingCheckpointState(lease.pendingCheckpointState);
parentShardIds(lease.parentShardIds);
childShardIds(lease.childShardIds);
}
/**
* @param leaseDurationNanos duration of lease in nanoseconds
* @param asOfNanos time in nanoseconds to check expiration as-of
* @return true if lease is expired as-of given time, false otherwise
*/
public boolean isExpired(long leaseDurationNanos, long asOfNanos) {
if (lastCounterIncrementNanos == null) {
return true;
}
long age = asOfNanos - lastCounterIncrementNanos;
// see comment on MAX_ABS_AGE_NANOS
if (Math.abs(age) > MAX_ABS_AGE_NANOS) {
return true;
} else {
return age > leaseDurationNanos;
}
}
/**
* Sets lastCounterIncrementNanos
*
* @param lastCounterIncrementNanos last renewal in nanoseconds since the epoch
*/
public void lastCounterIncrementNanos(Long lastCounterIncrementNanos) {
this.lastCounterIncrementNanos = lastCounterIncrementNanos;
}
/**
* Sets concurrencyToken.
*
* @param concurrencyToken may not be null
*/
public void concurrencyToken(@NonNull final UUID concurrencyToken) {
this.concurrencyToken = concurrencyToken;
}
/**
* Sets leaseKey. LeaseKey is immutable once set.
*
* @param leaseKey may not be null.
*/
public void leaseKey(@NonNull final String leaseKey) {
if (this.leaseKey != null) {
throw new IllegalArgumentException("LeaseKey is immutable once set");
}
this.leaseKey = leaseKey;
}
/**
* Sets leaseCounter.
*
* @param leaseCounter may not be null
*/
public void leaseCounter(@NonNull final Long leaseCounter) {
this.leaseCounter = leaseCounter;
}
/**
* Sets checkpoint.
*
* @param checkpoint may not be null
*/
public void checkpoint(@NonNull final ExtendedSequenceNumber checkpoint) {
this.checkpoint = checkpoint;
}
/**
* Sets pending checkpoint.
*
* @param pendingCheckpoint can be null
*/
public void pendingCheckpoint(ExtendedSequenceNumber pendingCheckpoint) {
this.pendingCheckpoint = pendingCheckpoint;
}
/**
* Sets pending checkpoint state.
*
* @param pendingCheckpointState can be null
*/
public void pendingCheckpointState(byte[] pendingCheckpointState) {
this.pendingCheckpointState = pendingCheckpointState;
}
/**
* Sets ownerSwitchesSinceCheckpoint.
*
* @param ownerSwitchesSinceCheckpoint may not be null
*/
public void ownerSwitchesSinceCheckpoint(@NonNull final Long ownerSwitchesSinceCheckpoint) {
this.ownerSwitchesSinceCheckpoint = ownerSwitchesSinceCheckpoint;
}
/**
* Sets parentShardIds.
*
* @param parentShardIds may not be null
*/
public void parentShardIds(@NonNull final Collection parentShardIds) {
this.parentShardIds.clear();
this.parentShardIds.addAll(parentShardIds);
}
/**
* Sets childShardIds.
*
* @param childShardIds may not be null
*/
public void childShardIds(@NonNull final Collection childShardIds) {
this.childShardIds.addAll(childShardIds);
}
/**
* Set the hash range key for this shard.
* @param hashKeyRangeForLease
*/
public void hashKeyRange(HashKeyRangeForLease hashKeyRangeForLease) {
if (this.hashKeyRangeForLease == null) {
this.hashKeyRangeForLease = hashKeyRangeForLease;
} else if (!this.hashKeyRangeForLease.equals(hashKeyRangeForLease)) {
throw new IllegalArgumentException("hashKeyRange is immutable");
}
}
/**
* Sets leaseOwner.
*
* @param leaseOwner may be null.
*/
public void leaseOwner(String leaseOwner) {
this.leaseOwner = leaseOwner;
}
/**
* Returns a deep copy of this object. Type-unsafe - there aren't good mechanisms for copy-constructing generics.
*
* @return A deep copy of this object.
*/
public Lease copy() {
return new Lease(this);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy