package org.dcache.srm.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.TimeUnit; import org.dcache.srm.SRM; import org.dcache.srm.SRMException; import org.dcache.srm.SRMInvalidRequestException; import org.dcache.util.TimeUtils; import org.dcache.util.TimeUtils.TimeUnitFormat; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static org.dcache.util.ByteUnit.KiB; /** * Utility methods for handling lifetime of requests. */ public class Lifetimes { private static final Logger LOGGER = LoggerFactory.getLogger(Lifetimes.class); /** * Calculate the lifetime of this request. * @param requestedLifetime the requested lifetime in seconds, or null if absent from request * @param maximumLifetime the maximum allowed lifetime in milliseconds. * @return the lifetime of this request in milliseconds * @throws SRMInvalidRequestException */ public static long calculateLifetime(Integer requestedLifetime, long maximumLifetime) throws SRMInvalidRequestException { return Lifetimes.calculateLifetime(requestedLifetime, 0, 0, maximumLifetime); } /** * Calculate the lifetime of this request. If the client supplied lifetime * requires an average bandwidth greater than {@literal bandwidth} then * an extended lifetime is returned. * @param requestedLifetime the requested lifetime in seconds, or null if absent from request * @param size the size of the file or zero if no value is supplied * @param bandwidth the maximum bandwidth the client may assume for this transfer in KiB/s, or zero if there is no limit * @param maximumLifetime the maximum allowed lifetime in milliseconds. * @return the lifetime of this request in milliseconds * @throws SRMInvalidRequestException */ public static long calculateLifetime(Integer requestedLifetime, long size, long bandwidth, long maximumLifetime) throws SRMInvalidRequestException { long lifetimeInSeconds = (requestedLifetime != null) ? requestedLifetime : 0; if (lifetimeInSeconds < 0) { /* [ SRM 2.2, 5.2.1 ] * m) If input parameter desiredTotalRequestTime is 0 (zero), each file request * must be tried at least once. Negative value must be invalid. */ throw new SRMInvalidRequestException("Negative desiredTotalRequestTime is invalid."); } else if (lifetimeInSeconds == 0) { // Revisit: Behaviour doesn't match the SRM spec return maximumLifetime; } else { long lifetime = TimeUnit.SECONDS.toMillis(lifetimeInSeconds); lifetime = calculateRequestLifetimeWithWorkaround(lifetime, size, bandwidth, maximumLifetime); return Math.min(lifetime, maximumLifetime); } } /** * Calculate an updated request lifetime that tries to ensure sufficient * time to transfer the supplied number of bytes, assuming a minimum * average bandwidth. The supplied lifetime is returned unless this is * (possibly) insufficient, in which case the estimated duration is returned. * @param lifetime client-supplied request lifetime, in milliseconds * @param size the number of bytes to transfer * @param bandwidth the maximum bandwidth the client may assume in KiB/s * @param maximumLifetime the configured maximum lifetime in milliseconds * @return a reasonable request lifetime, in milliseconds */ public static long calculateRequestLifetimeWithWorkaround(long lifetime, long size, long bandwidth, long maximumLifetime) { if (size > 0 && bandwidth > 0) { long estimatedDuration = SECONDS.toMillis(size/KiB.toBytes(bandwidth)); long cappedDuration = Math.min(estimatedDuration, maximumLifetime); if (lifetime < cappedDuration) { LOGGER.info("Requested lifetime of {} too short to transfer {} bytes; adjusting to {}", TimeUtils.duration(lifetime, MILLISECONDS, TimeUnitFormat.SHORT), size, TimeUtils.duration(cappedDuration, MILLISECONDS, TimeUnitFormat.SHORT)); lifetime = cappedDuration; } } return lifetime; } }