/*
# Licensed Materials - Property of IBM
# Copyright IBM Corp. 2016
*/
package com.ibm.streamsx.topology.consistent;
import java.util.concurrent.TimeUnit;
import com.ibm.streamsx.topology.TStream;
/**
* Immutable consistent region configuration.
*
* The default values for a {@code ConsistentRegionConfig} are:
* <UL>
* <LI><b>{@code drainTimeout}</b> - 180 seconds - Indicates the maximum time in
* seconds that the drain and checkpoint of the region is allotted to finish
* processing. If the process takes longer than the specified time, a failure is
* reported and the region is reset to the point of the previously successfully
* established consistent state.</LI>
* <LI><b>{@code resetTimeout}</b> - 180 seconds - Indicates the maximum time in
* seconds that the reset of the region is allotted to finish processing. If the
* process takes longer than the specified time, a failure is reported and
* another reset of the region is attempted</LI>
* <LI>
* <b>{@code maxConsecutiveResetAttempts}</b> - 5 - Indicates the maximum
* number of consecutive attempts to reset a consistent region. After a failure,
* if the maximum number of attempts is reached, the region stops processing new
* tuples. After the maximum number of consecutive attempts is reached, a region
* can be reset only with manual intervention or with a program with a call to a
* method in the consistent region controller.
* </LI>
* </UL>
* <P>
* A configuration has {@link #getTimeUnit() time unit} that applies to
* {@link #getPeriod()}, {@link #getDrainTimeout()} and {@link #getResetTimeout()}.
* The time unit is hard-coded to {@code TimeUnit.SECONDS}, future versions may
* allow creating configurations with a different time unit.
* </P>
* <P>
* <b>Example Use:</b>
* <pre>
* <code>
* // set source to be a the start of a operator driven consistent region
* // with a drain timeout of five seconds and a reset timeout of twenty seconds.
* source.setConsistent(operatorDriven().drainTimeout(5).resetTimeout(20));
* <code>
* </pre>
* </P>
*
* @see TStream#setConsistent(ConsistentRegionConfig)
*/
public final class ConsistentRegionConfig {
/**
* Defines how the drain-checkpoint cycle of a consistent region is triggered.
*/
public enum Trigger {
/**
* Region is triggered by the start operator.
*/
OPERATOR_DRIVEN,
/**
* Region is triggered periodically.
*/
PERIODIC,
;
}
private final Trigger trigger;
private final TimeUnit unit = TimeUnit.SECONDS;
private final long period ;
private final long drain;
private final long reset;
private final int attempts;
private ConsistentRegionConfig(ConsistentRegionConfig old, Long drain, Long reset, Integer attempts) {
this.trigger = old.trigger;
this.period = old.period;
this.drain = drain == null ? old.drain : drain;
this.reset = reset == null ? old.reset : reset;
this.attempts = attempts == null ? old.attempts : attempts;
}
private ConsistentRegionConfig(Trigger trigger, long period) {
this.trigger = trigger;
this.period = period;
this.drain = 180;
this.reset = 180;
this.attempts = 5;
}
/**
* Create a {@link Trigger#OPERATOR_DRIVEN} consistent region configuration.
* The source operator drives when a region is drained and checkpointed,
* for example a messaging source might drain and checkpoint every ten thousand
* messages.
* <BR>
* Configuration values are set to the default values.
*/
public ConsistentRegionConfig() {
this(Trigger.OPERATOR_DRIVEN, -1);
}
/**
* Create a {@link Trigger#OPERATOR_DRIVEN} consistent region configuration.
* The source operator triggers drain and checkpoint cycles for the region.
* <BR>
* Configuration values are set to the default values.
* <P>
* This is equivalent to {@link #ConsistentRegionConfig()}
* but when used as an imported static method can produce clearer code:
* <BR>
* <pre>
* <code>
* import static com.ibm.streamsx.topology.consistent.ConsistentRegionConfig.operatorDriven;
* ...
* stream.setConsistent(operatorDriven());
* </code>
* </pre>
* </P>
*/
public static ConsistentRegionConfig operatorDriven() {
return new ConsistentRegionConfig();
}
/**
* Create a {@link Trigger#PERIODIC} consistent region configuration.
* The IBM Streams runtime will trigger a drain and checkpoint
* the region periodically approximately every {@code period} seconds.
* <BR>
* Configuration values are set to the default values.
* <P>
* This is equivalent to {@link #ConsistentRegionConfig(int)}
* but when used as an imported static method can produce clearer code:
* <BR>
* <pre>
* <code>
* import static com.ibm.streamsx.topology.consistent.ConsistentRegionConfig.periodic;
* ...
* stream.setConsistent(periodic(30));
* </code>
* </pre>
* </P>
* @param period Trigger period in seconds.
*/
public static ConsistentRegionConfig periodic(int period) {
return new ConsistentRegionConfig(period);
}
/**
* Create a {@link Trigger#PERIODIC} consistent region configuration.
* The IBM Streams runtime will trigger a drain and checkpoint
* the region periodically approximately every {@code period} seconds.
* <BR>
* Configuration values are set to the default values.
* @param period Trigger period in seconds.
*/
public ConsistentRegionConfig(int period) {
this(Trigger.PERIODIC, period);
if (period <= 0)
throw new IllegalArgumentException("period must be greater than zero:" + period);
}
/**
* Get the trigger type.
* @return trigger type.
*/
public Trigger getTrigger() {
return trigger;
}
/**
* Get the trigger period.
* @return period if {@link #getTrigger()} is {@link Trigger#PERIODIC} otherwise -1.
*/
public long getPeriod() {
return period;
}
/**
* Get the time unit for {@link #getPeriod()}, {@link #getDrainTimeout()}
* and {@link #getResetTimeout()()}.
* @return Time unit for this configuration.
*/
public TimeUnit getTimeUnit() {
return unit;
}
/**
* Get the drain timeout for this configuration.
* @return Drain timeout in units of {@link #getTimeUnit()}.
*/
public long getDrainTimeout() {
return drain;
}
/**
* Return a new configuration changing {@code drainTimeout}.
* A new configuration instance is returned that is a copy
* of this configuration with only {@code drainTimeout} changed.
* <P>
* {@code stream.setConsistent(periodic(30).drainTimeout(5))}
* </P>
* @param drainTimeout Drain timeout to use in seconds, must be greater than 0.
* @return New configuration with drain timeout set to {@code drainTimeout}
* and the remaining values copied from this configuration.
*/
public ConsistentRegionConfig drainTimeout(long drainTimeout) {
if (drainTimeout <= 0)
throw new IllegalArgumentException("drainTimeout must be greater than zero:" + drainTimeout);
return new ConsistentRegionConfig(this, drainTimeout, null, null);
}
/**
* Get the reset timeout for this configuration.
* @return Reset timeout in units of {@link #getTimeUnit()}.
*/
public long getResetTimeout() {
return reset;
}
/**
* Return a new configuration changing {@code resetTimeout}.
* A new configuration instance is returned that is a copy
* of this configuration with only {@code resetTimeout} changed.
* <P>
* {@code stream.setConsistent(periodic(30).resetTimeout(15))}
* </P>
* @param resetTimeout Reset timeout to use in seconds, must be greater than 0.
* @return New configuration with reset timeout set to {@code resetTimeout}
* and the remaining values copied from this configuration.
*/
public ConsistentRegionConfig resetTimeout(long resetTimeout) {
if (resetTimeout <= 0)
throw new IllegalArgumentException("resetTimeout must be greater than zero:" + resetTimeout);
return new ConsistentRegionConfig(this, null, resetTimeout, null);
}
/**
* Get the maximum number of consecutive reset attempts.
* @return Maximum number of consecutive reset attempts.
*/
public int getMaxConsecutiveResetAttempts() {
return attempts;
}
/**
* Return a new configuration changing {@code maxConsecutiveResetAttempts}.
* A new configuration instance is returned that is a copy
* of this configuration with only {@code maxConsecutiveResetAttempts} changed.
* <P>
* {@code stream.setConsistent(periodic(30).maxConsecutiveResetAttempts(7))}
* </P>
* @param attempts Maximum number of consecutive reset attempts, must be greater than 0.
* @return New configuration with maxConsecutiveResetAttempts set to {@code attempts}
* and the remaining values copied from this configuration.
*/
public ConsistentRegionConfig maxConsecutiveResetAttempts(int attempts) {
if (attempts <= 0)
throw new IllegalArgumentException("maxConsecutiveResetAttempts must be greater than zero:" + attempts);
return new ConsistentRegionConfig(this, null, null, attempts);
}
@Override
public int hashCode() {
return java.util.Objects.hash(trigger, period, drain, reset, attempts);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ConsistentRegionConfig other = (ConsistentRegionConfig) obj;
if (attempts != other.attempts)
return false;
if (drain != other.drain)
return false;
if (period != other.period)
return false;
if (reset != other.reset)
return false;
if (trigger != other.trigger)
return false;
if (unit != other.unit)
return false;
return true;
}
}