import type { DesperateAny, MaybeCallable, PickPartial, MaybeAsyncCallable } 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";
import type { ShardNamer } from "./ShardNamer";
/**
 * Options for Cluster constructor.
 */
export interface ClusterOptions<TClient extends Client, TNode> {
    /** Islands configuration of the Cluster. */
    islands: MaybeAsyncCallable<ClusterIslands<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 auxiliary purposes when
     * discovering Shards/Clients. */
    localCache?: LocalCache | null;
    /** How often to recheck for changes in `options.islands`. If it is SYNC, then
     * by default - often, like every 500 ms (since it's assumed that
     * `options.islands` calculation is cheap). If it is ASYNC, then by default -
     * not so often, every `shardsDiscoverIntervalMs` (we assume that getting the
     * list of Island nodes may be expensive, e.g. fetching from AWS API or so).
     * If the Islands list here changes, then we trigger Shards rediscovery and
     * Clients recreation ASAP. */
    reloadIslandsIntervalMs?: MaybeCallable<number>;
    /** Info on how to build/parse Shard names. */
    shardNamer?: ShardNamer | null;
    /** How often to run Shards rediscovery in normal circumstances. */
    shardsDiscoverIntervalMs?: MaybeCallable<number>;
    /** Jitter for shardsDiscoverIntervalMs and reloadIslandsIntervalMs. */
    shardsDiscoverIntervalJitter?: MaybeCallable<number>;
    /** Used in the following situations:
     * 1. If we think that we know the Island of a particular Shard, but an
     *    attempt to access it fails. This means that maybe the Shard is migrating
     *    to another Island. So, 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. */
    runOnShardErrorRetryCount?: MaybeCallable<number>;
    /** How much time to wait before we retry rediscovering the entire Cluster
     * after a Shard-to-Island resolution error. The time here should be just
     * enough to wait for switching the Shard from one Island to another
     * (typically quick). */
    runOnShardErrorRediscoverClusterDelayMs?: MaybeCallable<number>;
    /** How much time to wait before sending discover requests to all Clients of
     * the Island trying to find the new master (or to reconnect). The time here
     * may reach several seconds, since some DBs shut down the old master and
     * promote some replica to it not simultaneously. */
    runOnShardErrorRediscoverIslandDelayMs?: MaybeCallable<number>;
    /** Delay the Client ending when this Client got removed from the Cluster.
     * This allows to lower the number of client_is_ended errors in case a Client
     * instance gets captured somewhere in the application code. */
    clientEndDelayMs?: MaybeCallable<number>;
}
/**
 * A type of `ClusterOptions#islands` property. Represents the full list of
 * Islands and their corresponding Nodes (masters and replicas).
 */
export type ClusterIslands<TNode> = ReadonlyArray<{
    no: number;
    nodes: readonly TNode[];
}>;
/**
 * 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 call to
     * `options.islands()`. */
    private islandsCache;
    /** Represents the result of the recent successful Shards discovery. */
    private shardsDiscoverCache;
    /** 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(what?: "islands" | "shards"): Promise<void>;
    private runOnShard;
    private rediscoverCluster;
    private rediscoverIsland;
    private shardsDiscoverExpensive;
}
//# sourceMappingURL=Cluster.d.ts.map