Repository: vespa-engine/vespa
File: configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java (Java)

Code before:
        transaction.add(updateMetaDataWithDeployTimestamp(tenant, clock.instant()));
        return transaction;
    }

    private List<Transaction.Operation> updateMetaDataWithDeployTimestamp(Tenant tenant, Instant deployTimestamp) {
        TenantMetaData tenantMetaData = getTenantMetaData(tenant).withLastDeployTimestamp(deployTimestamp);
        return tenantRepository.createWriteTenantMetaDataTransaction(tenantMetaData).operations();
    }

    TenantMetaData getTenantMetaData(Tenant tenant) {
        return tenantRepository.getTenantMetaData(tenant);
    }

    static void checkIfActiveHasChanged(Session session, Session activeSession, boolean ignoreStaleSessionFailure) {
        long activeSessionAtCreate = session.getActiveSessionAtCreate();
        log.log(Level.FINE, () -> activeSession.logPre() + "active session id at create time=" + activeSessionAtCreate);
        if (activeSessionAtCreate == 0) return; // No active session at create time, or session created for indeterminate app.

        long sessionId = session.getSessionId();
        long activeSessionSessionId = activeSession.getSessionId();
        log.log(Level.FINE, () -> activeSession.logPre() + "sessionId=" + sessionId +
                            ", current active session=" + activeSessionSessionId);
        if (activeSession.isNewerThan(activeSessionAtCreate) &&
            activeSessionSessionId != sessionId) {
            String errMsg = activeSession.logPre() + "Cannot activate session " + sessionId +
                            " because another deployment (session "  + activeSessionSessionId + ")" +
                            " has been activated after the start of processing this one," +
                            " please try deploying again from start";
            if (ignoreStaleSessionFailure) {
                log.warning(errMsg + " (Continuing because of force.)");
            } else {
                throw new ActivationConflictException(errMsg);
            }
        }
    }

    // Config generation is equal to session id, and config generation must be a monotonically increasing number
    static void checkIfActiveIsNewerThanSessionToBeActivated(long sessionId, long currentActiveSessionId) {
        if (sessionId < currentActiveSessionId) {
            throw new ActivationConflictException("Cannot activate session " + sessionId +
                                                  ", because it is older than current active session (" +
                                                  currentActiveSessionId + "), please try deploying again from start");
        }
    }

    private static SessionRepository sessionRepository(Tenant tenant) { return tenant.getSessionRepository(); }

    // ---------------- Application operations ----------------------------------------------------------------

    /**
     * Deletes an application and associated resources

Review comment (bug):
I was confused about what "(was 1234 at creation time)" was referring to.  So a deployment's prepare & activate can assume the active session stays constant.  This is a nice invariant.  It would be nice to get this into the message somehow.

Consider this if we throw:
> This session X was prepared when session Y was active, but session Z has since become active: refusing to activate this session, please redeploy

And this with `ignoreStaleSessionFailure`:
> This session X was prepared when session Y was active, but session Z has since become active: will activate anyway (by force)

Example alternative if we throw:
> The active session has changed from X (at the time this session Y was created) to Z (before this session was activated), therefore this session Y is invalid and cannot be activated: please redeploy

Code after:
        transaction.add(updateMetaDataWithDeployTimestamp(tenant, clock.instant()));
        return transaction;
    }

    private List<Transaction.Operation> updateMetaDataWithDeployTimestamp(Tenant tenant, Instant deployTimestamp) {
        TenantMetaData tenantMetaData = getTenantMetaData(tenant).withLastDeployTimestamp(deployTimestamp);
        return tenantRepository.createWriteTenantMetaDataTransaction(tenantMetaData).operations();
    }

    TenantMetaData getTenantMetaData(Tenant tenant) {
        return tenantRepository.getTenantMetaData(tenant);
    }

    static void checkIfActiveHasChanged(Session session, Session activeSession, boolean ignoreStaleSessionFailure) {
        long activeSessionAtCreate = session.getActiveSessionAtCreate();
        log.log(Level.FINE, () -> activeSession.logPre() + "active session id at create time=" + activeSessionAtCreate);
        if (activeSessionAtCreate == 0) return; // No active session at create time, or session created for indeterminate app.

        long sessionId = session.getSessionId();
        long activeSessionSessionId = activeSession.getSessionId();
        log.log(Level.FINE, () -> activeSession.logPre() + "sessionId=" + sessionId +
                            ", current active session=" + activeSessionSessionId);
        if (activeSession.isNewerThan(activeSessionAtCreate) &&
            activeSessionSessionId != sessionId) {
            String errMsg = activeSession.logPre() + "This session " + sessionId +
                            " was prepared when session "+ activeSessionAtCreate + " was active," +
                            " but session " + activeSessionSessionId + " has since become active:";
            if (ignoreStaleSessionFailure) {
                log.warning(errMsg + " will activate anyway (by force)");
            } else {
                throw new ActivationConflictException(errMsg + " refusing to activate this session, please redeploy");
            }
        }
    }

    // Config generation is equal to session id, and config generation must be a monotonically increasing number
    static void checkIfActiveIsNewerThanSessionToBeActivated(long sessionId, long currentActiveSessionId) {
        if (sessionId < currentActiveSessionId) {
            throw new ActivationConflictException("Cannot activate session " + sessionId +
                                                  ", because it is older than current active session (" +
                                                  currentActiveSessionId + "), please try deploying again from start");
        }
    }

    private static SessionRepository sessionRepository(Tenant tenant) { return tenant.getSessionRepository(); }

    // ---------------- Application operations ----------------------------------------------------------------

    /**
     * Deletes an application and associated resources
     *