package com.atlassian.bitbucket.throttle;

import javax.annotation.Nonnull;

/**
 * Manages buckets of {@link Ticket}s which may be used to throttle access to named resources. The exact semantics by
 * which tickets are managed is not guaranteed by this interface, but it does have certain contracts:
 * <ul>
 *     <li>Attempting to acquire a ticket <i>shall not</i> block indefinitely, and acquire timeouts may be adjusted
 *     with settings in {@code bitbucket.properties}</li>
 *     <li>If acquiring a ticket for a named resource times out, a {@link ResourceBusyException} <i>shall</i> be thrown
 *     indicating the resource is busy</li>
 *     <li>If resources have been timing out, a banner <i>shall</i> be displayed in the user interface indicating the
 *     server is under heavy load</li>
 *     <li>When a server request ends, all tickets which were held during that request <i>shall</i> be released, whether
 *     they were explicitly released before the request ended or not</li>
 * </ul>
 * For tickets that are acquired on non-request threads, such as {@code ExecutorService} threads, it is the acquirer's
 * obligation to ensure the ticket is released. The system will attempt to detect such "lost" tickets and release them,
 * but such detection is "best effort" and may not be foolproof. In general, calling code should follow an approach
 * similar to:
 * <pre><code>
 * Ticket ticket = throttleService.acquireTicket("resourceName");
 * try {
 *     //Do protected, resource-intensive operation
 * } finally {
 *     ticket.release();
 * }
 * </code></pre>
 * This ensures the ticket is released immediately after the resource-intensive operation being protected is completed,
 * which, in turn, ensures the resource remains available and isn't starved out by "lost" tickets.
 */
public interface ThrottleService {

    /**
     * Acquires a ticket for accessing the specified resource.
     * <p>
     * Successful acquisition of a ticket indicates that the named resource should have sufficient capacity to service
     * a <i>single, full request</i>. This means a ticket may be acquired once and held while multiple operations are
     * performed on the gated resource. Callers should use {@code finally} blocks to ensure the ticket is released when
     * they are finished using the gated resource. The system makes a <i>best effort</i> attempt to detect and release
     * "lost" tickets when a server request ends, but it is best for callers to explicitly manage their own tickets as
     * such detection may not catch all cases.
     * <p>
     * The returned {@link Ticket} will never be {@code null}. If no tickets are available for the requested resource,
     * a {@link ResourceBusyException} will be thrown.
     *
     * @param resourceName the resource for which the ticket should be acquired
     * @return the acquired ticket
     * @throws ResourceBusyException if a ticket cannot be acquired from the named bucket
     */
    @Nonnull
    Ticket acquireTicket(@Nonnull String resourceName) throws ResourceBusyException;
}
