"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateGCConfigs = void 0;
const internal_1 = require("@fluidframework/telemetry-utils/internal");
const gcDefinitions_js_1 = require("./gcDefinitions.js");
const gcHelpers_js_1 = require("./gcHelpers.js");
/**
 * Generates configurations for the Garbage Collector that it uses to determine what to run and how.
 * @param mc - The monitoring context for reading configs from the config provider.
 * @param createParams - The creation params:
 * gcOptions - The garbage collector runtime options.
 * metadata - The container runtime's createParams.metadata.
 * existing - Whether the container is new or an existing one.
 * @returns The garbage collector configurations.
 */
function generateGCConfigs(mc, createParams) {
    let gcEnabled = true;
    let sessionExpiryTimeoutMs;
    let tombstoneTimeoutMs;
    let persistedGcFeatureMatrix;
    let gcVersionInBaseSnapshot;
    /**
     * The following GC state is enabled during container creation and cannot be changed throughout its lifetime:
     * 1. Whether running GC mark phase is allowed or not.
     * 2. Whether running GC sweep phase is allowed or not.
     * 3. Whether GC session expiry is enabled or not.
     * For existing containers, we get this information from the metadata blob of its summary.
     */
    if (createParams.existing) {
        const metadata = createParams.metadata;
        gcVersionInBaseSnapshot = (0, gcHelpers_js_1.getGCVersion)(metadata);
        // Existing documents which did not have metadata blob or had GC disabled have GC version as 0. GC will be
        // disabled for these documents.
        gcEnabled = gcVersionInBaseSnapshot !== 0;
        sessionExpiryTimeoutMs = metadata?.sessionExpiryTimeoutMs;
        const legacyPersistedSweepTimeoutMs = metadata?.sweepTimeoutMs;
        tombstoneTimeoutMs =
            metadata?.tombstoneTimeoutMs ??
                legacyPersistedSweepTimeoutMs ?? // Backfill old documents that have sweepTimeoutMs instead of tombstoneTimeoutMs
                computeTombstoneTimeout(sessionExpiryTimeoutMs); // Backfill old documents that didn't persist either value
        persistedGcFeatureMatrix = metadata?.gcFeatureMatrix;
    }
    else {
        // This Test Override only applies for new containers
        const testOverrideTombstoneTimeoutMs = mc.config.getNumber("Fluid.GarbageCollection.TestOverride.TombstoneTimeoutMs");
        // Set the Session Expiry if session expiry flag isn't explicitly set to false.
        if (mc.config.getBoolean(gcDefinitions_js_1.runSessionExpiryKey) !== false) {
            sessionExpiryTimeoutMs =
                createParams.gcOptions.sessionExpiryTimeoutMs ?? gcDefinitions_js_1.defaultSessionExpiryDurationMs;
        }
        tombstoneTimeoutMs =
            testOverrideTombstoneTimeoutMs ?? computeTombstoneTimeout(sessionExpiryTimeoutMs);
        const gcGeneration = createParams.gcOptions[gcDefinitions_js_1.gcGenerationOptionName];
        if (gcGeneration !== undefined) {
            persistedGcFeatureMatrix = { gcGeneration };
        }
    }
    // The persisted GC generation must indicate Sweep is allowed for this document,
    // according to the GC Generation option provided this session.
    // Note that if no generation option is provided, Sweep is allowed for any document.
    const sweepAllowed = (0, gcHelpers_js_1.shouldAllowGcSweep)(persistedGcFeatureMatrix ?? {} /* featureMatrix */, createParams.gcOptions[gcDefinitions_js_1.gcGenerationOptionName] /* currentGeneration */);
    /**
     * Whether sweep should run or not. This refers to whether Tombstones should fail on load and whether
     * sweep-ready nodes should be deleted.
     *
     * Assuming overall GC is enabled and tombstoneTimeout is provided, the following conditions have to be met to run sweep:
     *
     * 1. Sweep should be allowed in this container.
     * 2. Sweep should be enabled for this session, optionally restricted to attachment blobs only.
     *
     * These conditions can be overridden via the RunSweep feature flag.
     */
    const sweepEnabled = !gcEnabled || tombstoneTimeoutMs === undefined
        ? false
        : mc.config.getBoolean(gcDefinitions_js_1.runSweepKey) ??
            (sweepAllowed && createParams.gcOptions.enableGCSweep === true);
    const disableDatastoreSweep = mc.config.getBoolean(gcDefinitions_js_1.disableDatastoreSweepKey) === true ||
        createParams.gcOptions[gcDefinitions_js_1.gcDisableDataStoreSweepOptionName] === true;
    const shouldRunSweep = sweepEnabled
        ? disableDatastoreSweep
            ? "ONLY_BLOBS"
            : "YES"
        : "NO";
    // Override inactive timeout if test config or gc options to override it is set.
    const inactiveTimeoutMs = mc.config.getNumber("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs") ??
        createParams.gcOptions.inactiveTimeoutMs ??
        gcDefinitions_js_1.defaultInactiveTimeoutMs;
    // Inactive timeout must be greater than tombstone timeout since a node goes from active -> inactive -> sweep ready.
    if (tombstoneTimeoutMs !== undefined && inactiveTimeoutMs > tombstoneTimeoutMs) {
        throw new internal_1.UsageError("inactive timeout should not be greater than the tombstone timeout");
    }
    // Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
    const testMode = mc.config.getBoolean(gcDefinitions_js_1.gcTestModeKey) ?? createParams.gcOptions.runGCInTestMode === true;
    // Whether we are running in tombstone mode. If disabled, tombstone data will not be written to or read from snapshots,
    // and objects will not be marked as tombstoned even if they pass to the "TombstoneReady" state during the session.
    const tombstoneMode = mc.config.getBoolean(gcDefinitions_js_1.disableTombstoneKey) !== true;
    const runFullGC = createParams.gcOptions.runFullGC;
    const sweepGracePeriodMs = createParams.gcOptions.sweepGracePeriodMs ?? gcDefinitions_js_1.defaultSweepGracePeriodMs;
    (0, internal_1.validatePrecondition)(sweepGracePeriodMs >= 0, "sweepGracePeriodMs must be non-negative", {
        sweepGracePeriodMs,
    });
    const throwOnInactiveLoad = createParams.gcOptions.throwOnInactiveLoad;
    const throwOnTombstoneLoadConfig = mc.config.getBoolean(gcDefinitions_js_1.throwOnTombstoneLoadOverrideKey) ??
        createParams.gcOptions[gcDefinitions_js_1.gcDisableThrowOnTombstoneLoadOptionName] !== true;
    const throwOnTombstoneLoad = throwOnTombstoneLoadConfig && sweepAllowed && !createParams.isSummarizerClient;
    const throwOnTombstoneUsage = mc.config.getBoolean(gcDefinitions_js_1.throwOnTombstoneUsageKey) === true &&
        sweepAllowed &&
        !createParams.isSummarizerClient;
    return {
        gcEnabled, // For this document
        sweepEnabled: sweepAllowed, // For this document (based on current GC Generation option)
        shouldRunSweep, // For this session
        runFullGC,
        testMode,
        tombstoneMode,
        sessionExpiryTimeoutMs,
        tombstoneTimeoutMs,
        sweepGracePeriodMs,
        inactiveTimeoutMs,
        persistedGcFeatureMatrix,
        gcVersionInBaseSnapshot,
        gcVersionInEffect: (0, gcHelpers_js_1.getGCVersionInEffect)(mc.config),
        throwOnInactiveLoad,
        throwOnTombstoneLoad,
        throwOnTombstoneUsage,
    };
}
exports.generateGCConfigs = generateGCConfigs;
/**
 * Tombstone timeout is the time after which unreferenced content is guaranteed not to be revived (re-referenced).
 * Tombstone timeout = session expiry timeout + snapshot cache expiry timeout + one day buffer.
 *
 * The snapshot cache expiry timeout cannot be known precisely but the upper bound is 5 days.
 * The buffer is added to account for any clock skew or other edge cases.
 * We use server timestamps throughout so the skew should be minimal but make it 1 day to be safe.
 *
 * If there is no Session Expiry timeout, GC can never guarantee an object won't be revived, so return undefined.
 */
function computeTombstoneTimeout(sessionExpiryTimeoutMs) {
    const bufferMs = gcDefinitions_js_1.oneDayMs;
    return (sessionExpiryTimeoutMs && sessionExpiryTimeoutMs + gcDefinitions_js_1.maxSnapshotCacheExpiryMs + bufferMs);
}
//# sourceMappingURL=gcConfigs.js.map