import type { DesperateAny, MaybeCallable, PickPartial } from "../internal/misc";
import type { Client } from "./Client";
import { Island } from "./Island";
import type { LocalCache } from "./LocalCache";
import type { Loggers } from "./Loggers";
import { Shard } from "./Shard";
/**
 * Options for Cluster constructor.
 */
export interface ClusterOptions<TClient extends Client, TNode> {
    /** Islands configuration of the Cluster. */
    islands: MaybeCallable<ReadonlyArray<{
        no: number;
        nodes: readonly TNode[];
    }>>;
    /** Given a node of some Island, instantiates a Client for this node. Called
     * when a new node appears in the Cluster statically or dynamically. */
    createClient: (node: TNode) => TClient;
    /** Loggers to be injected into all Clients returned by createClient(). */
    loggers: Loggers;
    /** An instance of LocalCache which may be used for auxillary purposes when
     * discovering Shards/Clients. */
    localCache?: LocalCache | null;
    /** How often to run Shards rediscovery in normal circumstances. */
    shardsDiscoverIntervalMs?: MaybeCallable<number>;
    /** Jitter for shardsDiscoverIntervalMs. */
    shardsDiscoverIntervalJitter?: MaybeCallable<number>;
    /** How often to recheck for changes in options.islands (typically, often,
     * since it's assumed that options.islands calculation is cheap). If the
     * Cluster configuration is changed, then we trigger rediscovery ASAP. */
    shardsDiscoverRecheckIslandsIntervalMs?: MaybeCallable<number>;
    /** Used in the following situations:
     * 1. If we think that we know Island of a particular Shard, but an attempt to
     *    access it fails, this means that maybe the Shard is migrating to another
     *    Island. In this case, we wait a bit and retry that many times. We should
     *    not do it too many times though, because all DB requests will be blocked
     *    waiting for the resolution.
     * 2. If we sent a write request to a Client, but it appeared that this Client
     *    is a replica, and the master moved to some other Client. In this case,
     *    we wait a bit and ping all Clients of the Island to refresh, who is
     *    master and who is replica. */
    locateIslandErrorRetryCount?: MaybeCallable<number>;
    /** How much time to wait before we retry rediscovering the entire Cluster.
     * The time here should be just enough to wait for switching the Shard from
     * one Island to another (typically quick). */
    locateIslandErrorRediscoverClusterDelayMs?: MaybeCallable<number>;
    /** How much time to wait before sending discover requests to all Clients of
     * the Island trying to find the new master. The time here may reach several
     * seconds, since some DBs shut down the old master and promote some replica
     * to it not simultaneously. */
    locateIslandErrorRediscoverIslandDelayMs?: MaybeCallable<number>;
}
/**
 * Cluster is a collection of Islands and an orchestration of shardNo -> Island
 * resolution.
 *
 * It's unknown beforehand, which Island some particular Shard belongs to; the
 * resolution is done asynchronously and lazily.
 *
 * Shard 0 is a special "global" Shard.
 */
export declare class Cluster<TClient extends Client, TNode = DesperateAny> {
    /** Default values for the constructor options. */
    static readonly DEFAULT_OPTIONS: Required<PickPartial<ClusterOptions<Client, never>>>;
    /** The complete registry of all initialized Clients. Cluster nodes may change
     * at runtime, so once a new node appears, its Client is added to the
     * registry. Also, the Clients of disappeared nodes are eventually removed
     * from the registry on the next Shards discovery. */
    private clientRegistry;
    /** The complete registry of all Islands ever created. If some Island changes
     * configuration, its old version is eventually removed from the registry
     * during the next Shards discovery. */
    private islandRegistry;
    /** Represents the result of the recent successful Shards discovery. */
    private shardsDiscoverCache;
    /** A handler which extracts Shard number from an ID (derived from some node's
     * Client assuming they all have the same logic). */
    private shardNoByID;
    /** Once set to true, Clients for newly appearing nodes will be pre-warmed. */
    private prewarmEnabled;
    /** Cluster configuration options. */
    readonly options: Required<ClusterOptions<TClient, TNode>>;
    /**
     * Initializes the Cluster, but doesn't send any queries yet, even discovery
     * queries (also, no implicit prewarming).
     */
    constructor(options: ClusterOptions<TClient, TNode>);
    /**
     * Signals the Cluster to keep the Clients pre-warmed, e.g. open. (It's up to
     * the particular Client's implementation, what does a "pre-warmed Client"
     * mean; typically, it's keeping some minimal number of pooled connections.)
     *
     * Except when `randomizedDelayMs` is passed as 0, the actual prewarm (and
     * Islands discovery) queries will run with a randomized delay between N/2 and
     * N ms. It is better to operate in such mode: if multiple Node processes
     * start simultaneously in the cluster, then the randomization helps to avoid
     * new connections burst (new connections establishment is expensive for e.g.
     * pgbouncer or when DB is accessed over SSL).
     */
    prewarm(randomizedDelayMs?: number, onInitialPrewarm?: (delayMs: number) => void): void;
    /**
     * Returns a global Shard of the Cluster. This method is made synchronous
     * intentionally, to defer the I/O and possible errors to the moment of the
     * actual query.
     */
    globalShard(): Shard<TClient>;
    /**
     * Returns all currently known (discovered) non-global Shards in the Cluster.
     */
    nonGlobalShards(): Promise<ReadonlyArray<Shard<TClient>>>;
    /**
     * Returns Shard of a particular id. This method is made synchronous
     * intentionally, to defer the I/O and possible errors to the moment of the
     * actual query.
     *
     * Why is it important? Because Shards may go up and down temporarily at
     * random moments of time. Imagine we made this method async and asserted that
     * the Shard is actually available at the moment when the method is called.
     * What would happen if the Shard object was stored somewhere as "successful"
     * by the caller, then the Island went down, and then a query is sent to the
     * Shard in, say, 20 seconds? We'd get an absolutely different exception, at
     * the moment of the query. We don't want this to happen: we want all of the
     * exceptions to be thrown with a consistent call stack (e.g. at the moment of
     * the query), no matter whether it was an immediate call or a deferred one.
     */
    shard(id: string): Shard<TClient>;
    /**
     * Returns a Shard if we know its number. The idea: for each Shard number
     * (even for non-discovered yet Shards), we keep the corresponding Shard
     * object in a Memoize cache, so Shards with the same number always resolve
     * into the same Shard object. Then, an actual Island locating process happens
     * when the caller wants to get a Client of that Shard (and it throws if such
     * Shard hasn't been discovered actually).
     */
    shardByNo(shardNo: number): Shard<TClient>;
    /**
     * Returns a random Shard among the ones which are currently known
     * (discovered) in the Cluster.
     */
    randomShard(seed?: object): Promise<Shard<TClient>>;
    /**
     * Returns an Island by its number.
     */
    island(islandNo: number): Promise<Island<TClient>>;
    /**
     * Returns all Islands in the Cluster.
     */
    islands(): Promise<Array<Island<TClient>>>;
    /**
     * Triggers shards rediscovery and finishes as soon as it's done. To be used
     * in unit tests mostly, because in real life, it's enough to just modify the
     * cluster configuration.
     */
    rediscover(): Promise<void>;
    /**
     * Runs the body function with retries. The Island injected into the body
     * function is located automatically by the Shard number.
     */
    private runWithLocatedIsland;
    /**
     * Runs the whole-cluster rediscover after a delay.
     *
     * Multiple concurrent calls to this method will be coalesced into one
     * (including the delay period):
     * 1. This protects against the burst of rediscover requests caused by
     *    multiple failing concurrent queries.
     * 2. It also allows to keep the queries batched when they are retried (i.e.
     *    the whole batch will be retried, not individual queries).
     */
    private rediscoverCluster;
    /**
     * Runs Island#rediscover() after a delay.
     *
     * Multiple concurrent calls to this method will be coalesced into one
     * (including the delay period):
     * 1. This protects against the burst of rediscover requests caused by
     *    multiple failing concurrent queries.
     * 2. It also allows to keep the queries batched when they are retried (i.e.
     *    the whole batch will be retried, not individual queries).
     */
    private rediscoverIsland;
    /**
     * Runs the actual Shards discovery queries over all Islands and updates the
     * mapping from each Shard number to an Island where it lives. These queries
     * may be expensive, so it's expected that the return Promise is heavily
     * cached by the caller code.
     */
    private shardsDiscoverExpensive;
}
//# sourceMappingURL=Cluster.d.ts.map