All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.kv.table.TimeToLive Maven / Gradle / Ivy

/*-
 * Copyright (C) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle NoSQL
 * Database made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/nosqldb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle NoSQL Database for a copy of the license and
 * additional information.
 */

package oracle.kv.table;

import static oracle.kv.impl.util.SerialVersion.TTL_SERIAL_VERSION;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;

import oracle.kv.impl.util.SerialVersion;

import com.sleepycat.persist.model.Persistent;

/**
 * TimeToLive is a utility class that represents a period of time, similar to
 * Java 8's java.time.Duration, but specialized to the needs of Oracle NoSQL
 * Database.
 * 

* This class is restricted to durations of days and hours. It is only * used as input related to time to live (TTL) for {@link Row} instances, * set by using {@link Row#setTTL}. Construction allows only day and hour * durations for efficiency reasons. Durations of days are recommended as they * result in the least amount of storage consumed in a store. *

* Only positive durations are allowed on input, although negative durations * can be returned from {@link #fromExpirationTime} if the expirationTime is * in the past relative to the referenceTime. * * @since 4.0 * @hiddensee {@link #writeFastExternal FastExternalizable format} */ @Persistent public final class TimeToLive implements Serializable { private static final long serialVersionUID = 1L; private final long value; private final TimeUnit unit; /** * A convenience constant that can be used as an argument to * {@link Row#setTTL} to indicate that the row should not expire. */ public static final TimeToLive DO_NOT_EXPIRE = new TimeToLive(0, TimeUnit.DAYS); /** * Creates a duration using a period of hours. * * @param hours the number of hours in the duration, must be * a non-negative number * * @return the duration * * @throws IllegalArgumentException if a negative value is provided */ public static TimeToLive ofHours(long hours) { return createTimeToLive(hours, TimeUnit.HOURS); } /** * Creates a duration using a period of 24 hour days. * * @param days the number of days in the duration, must be * a non-negative number * * @return the duration * * @throws IllegalArgumentException if a negative value is provided */ public static TimeToLive ofDays(long days) { return createTimeToLive(days, TimeUnit.DAYS); } /** * Returns the number of days in this duration, which may be negative. * * @return the number of days */ public long toDays() { return TimeUnit.DAYS.convert(value, unit); } /** * Returns the number of hours in this duration, which may be negative. * * @return the number of hours */ public long toHours() { return TimeUnit.HOURS.convert(value, unit); } /** * Returns an absolute time representing the duration plus the absolute * time reference parameter. If an expiration time from the current time is * desired the parameter should be {@link System#currentTimeMillis}. If the * duration of this object is 0 ({@link #DO_NOT_EXPIRE}), indicating no * expiration time, this method will return 0, regardless of the reference * time. * * @param referenceTime an absolute time in milliseconds since January * 1, 1970. * * @return time in milliseconds, 0 if this object's duration is 0 */ public long toExpirationTime(long referenceTime) { if (value == 0) { return 0; } return referenceTime + toMillis(); } /** * Returns an instance of TimeToLive based on an absolute expiration * time and a reference time. If a duration relative to the current time * is desired the referenceTime should be {@link System#currentTimeMillis}. * If the expirationTime is 0, the referenceTime is ignored and a * TimeToLive of duration 0 is created, indicating no expiration. *

* Days will be use as the primary unit of duration if the expiration time * is evenly divisible into days, otherwise hours are used. * * @param expirationTime an absolute time in milliseconds since January * 1, 1970 * * @param referenceTime an absolute time in milliseconds since January * 1, 1970. * * @return a TimeToLive instance */ public static TimeToLive fromExpirationTime(long expirationTime, long referenceTime) { final long MILLIS_PER_HOUR = 1000L * 60 * 60; if (expirationTime == 0) { return DO_NOT_EXPIRE; } /* * Calculate whether the given time in millis, when converted to hours, * rounding up, is not an even multiple of 24. */ final long hours = (expirationTime + MILLIS_PER_HOUR - 1) / MILLIS_PER_HOUR; boolean timeInHours = hours % 24 != 0; /* * This may result in a negative duration. This is ok and is documented. * If somehow the duration is 0, set it to -1 hours because 0 means * no expiration. */ long duration = expirationTime - referenceTime; if (duration == 0) { /* very unlikely, but possible; set to -1 hours to avoid 0 */ duration = -MILLIS_PER_HOUR; timeInHours = true; } if (timeInHours) { if (duration > 0 && duration < MILLIS_PER_HOUR) { return new TimeToLive(1, TimeUnit.HOURS); } return new TimeToLive( TimeUnit.HOURS.convert(duration, TimeUnit.MILLISECONDS), TimeUnit.HOURS); } if (duration > 0 && duration < MILLIS_PER_HOUR * 24) { return new TimeToLive(1, TimeUnit.DAYS); } return new TimeToLive( TimeUnit.DAYS.convert(duration, TimeUnit.MILLISECONDS), TimeUnit.DAYS); } /** * Equality compares the duration only if the units used for construction * are the same. If the units (ofHours vs ofDays) are different two * instances will not be equal even if their absolute durations are the * same unless both values are 0, which means no expiration. */ @Override public boolean equals(Object other) { if (!(other instanceof TimeToLive)) { return false; } TimeToLive otherTTL = (TimeToLive) other; if (value == 0 && otherTTL.value == 0) { return true; } return (unit == otherTTL.unit && value == otherTTL.value); } @Override public int hashCode() { /* include the high order bits */ return (int) ((value >>> 32) ^ value) + unit.hashCode(); } @Override public String toString() { return value + " " + unit.toString(); } /** * Creates a TimeToLive instance. The value must be a non-negative number * Internal use only * @hidden */ public static TimeToLive createTimeToLive(long value, TimeUnit unit) { if (value < 0) { throw new IllegalArgumentException( "TimeToLive does not support negative time periods"); } return (value == 0) ? DO_NOT_EXPIRE : new TimeToLive(value, unit); } /** * Internal use only * @hidden * * Returns the numeric duration value */ public long getValue() { return value; } /** * Internal use only * @hidden * * Returns the TimeUnit used for the duration */ public TimeUnit getUnit() { return unit; } /** * Private constructor. All construction is done via this constructor, which * validates the arguments. * * @param value value of time * @param unit unit of time, cannot be null */ private TimeToLive(long value, TimeUnit unit) { if (unit != TimeUnit.DAYS && unit != TimeUnit.HOURS) { throw new IllegalArgumentException( "Invalid TimeUnit (" + unit + ") in TimeToLive construction." + "Must be DAYS or HOURS."); } this.value = value; this.unit = unit; } /** * For DPL. DPL/@Persistent is *only* required because TimeToLive was * (mistakenly) included in the 4.0 EvolveTable task, which is persistent. * As of 4.0 DPL is not used for plans/tasks but it's needed to upgrade * plans from 3.0-based stores. Eventually, when the pre-req version * increases beyond the DPL-based version, this can be removed. */ private TimeToLive() { this(0, TimeUnit.DAYS); } private long toMillis() { return TimeUnit.MILLISECONDS.convert(value, unit); } /** * Reads an instance from the input stream, returning null if the serial * version shows that TTL is not supported. * * @hidden For internal use only */ public static TimeToLive readFastExternal(DataInput in, short serialVersion) throws IOException { if (serialVersion < TTL_SERIAL_VERSION) { return null; } final int ttlVal = readTTLValue(in); final TimeUnit ttlUnit = readTTLUnit(in, ttlVal); return createTimeToLive(ttlVal, ttlUnit); } /** * Reads the TTL value from the input stream. * * @hidden For internal use only */ public static int readTTLValue(DataInput in) throws IOException { return in.readInt(); } /** * Reads the TimeUnit associated with the specified TTL value from the * input stream. * * @hidden For internal use only */ public static TimeUnit readTTLUnit(DataInput in, int ttlVal) throws IOException { try { return convertTimeToLiveUnit(ttlVal, (ttlVal == 0) ? 0 : in.readByte()); } catch (IllegalArgumentException e) { throw new IOException("Invalid value: " + e.getMessage(), e); } } /** * Converts an ordinal value for a TTL TimeUnit into the TimeUnit. */ public static TimeUnit convertTimeToLiveUnit(int ttlVal, byte unitOrdinal) { if (ttlVal == 0) { return TimeUnit.DAYS; } switch (unitOrdinal) { case 6: return TimeUnit.DAYS; case 5: return TimeUnit.HOURS; default: throw new IllegalArgumentException( "Unknown TimeUnit ordinal: " + unitOrdinal); } } /** * Returns the numeric duration value associated with a {@link TimeToLive}, * or 0 if the ttl is null. * * @param ttl the ttl or null * @return the numeric duration value */ public static int getTTLValue(TimeToLive ttl) { return (ttl != null) ? (int) ttl.getValue() : 0; } /** * Returns the TimeUnit associated with a {@link TimeToLive}, or * {TimeUnit#DAYS} if the ttl is null. * * @param ttl the ttl or null * @return the time unit */ public static TimeUnit getTTLUnit(TimeToLive ttl) { return (ttl != null) ? ttl.getUnit() : TimeUnit.DAYS; } /** * Writes a TTL value to the output stream. Format for {@code * serialVersion} {@link SerialVersion#TTL_SERIAL_VERSION} and greater: *

    *
  1. ({@link DataOutput#writeInt int}) {@code ttlVal} *
  2. [Optional] ({@code byte}) {@code ttlUnit} // {@link * TimeUnit#DAYS}=6, {@link TimeUnit#HOURS}=5, only present if ttlVal * is not 0 *
* * @param out the output stream * @param serialVersion the version of the serialization format * @param ttlVal the TTL value * @param ttlUnit the TTL time unit, may be null if ttVal is 0 */ public static void writeFastExternal(DataOutput out, short serialVersion, int ttlVal, TimeUnit ttlUnit) throws IOException { if (serialVersion < TTL_SERIAL_VERSION) { if (ttlVal == 0) { return; } throw new UnsupportedOperationException("TTL is not supported"); } out.writeInt(ttlVal); if (ttlVal == 0) { return; } if ((ttlUnit != TimeUnit.DAYS) && (ttlUnit != TimeUnit.HOURS)) { throw new IllegalArgumentException( "Invalid TTL time unit: " + ttlUnit); } out.writeByte((byte) ttlUnit.ordinal()); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy