sizeof(ReplicationSlotOnDisk) - ReplicationSlotOnDiskConstantSize
 
 #define SLOT_MAGIC     0x1051CA1   /* format identifier */
-#define SLOT_VERSION   2       /* version for new files */
+#define SLOT_VERSION   3       /* version for new files */
 
 /* Control array for replication slot management */
 ReplicationSlotCtlData *ReplicationSlotCtl = NULL;
        SpinLockAcquire(&s->mutex);
        effective_xmin = s->effective_xmin;
        effective_catalog_xmin = s->effective_catalog_xmin;
-       invalidated = (!XLogRecPtrIsInvalid(s->data.invalidated_at) &&
-                      XLogRecPtrIsInvalid(s->data.restart_lsn));
+       invalidated = s->data.invalidated != RS_INVAL_NONE;
        SpinLockRelease(&s->mutex);
 
        /* invalidated slots need not apply */
    {
        ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i];
        XLogRecPtr  restart_lsn;
+       bool        invalidated;
 
        if (!s->in_use)
            continue;
 
        SpinLockAcquire(&s->mutex);
        restart_lsn = s->data.restart_lsn;
+       invalidated = s->data.invalidated != RS_INVAL_NONE;
        SpinLockRelease(&s->mutex);
 
+       /* invalidated slots need not apply */
+       if (invalidated)
+           continue;
+
        if (restart_lsn != InvalidXLogRecPtr &&
            (min_required == InvalidXLogRecPtr ||
             restart_lsn < min_required))
    {
        ReplicationSlot *s;
        XLogRecPtr  restart_lsn;
+       bool        invalidated;
 
        s = &ReplicationSlotCtl->replication_slots[i];
 
        /* read once, it's ok if it increases while we're checking */
        SpinLockAcquire(&s->mutex);
        restart_lsn = s->data.restart_lsn;
+       invalidated = s->data.invalidated != RS_INVAL_NONE;
        SpinLockRelease(&s->mutex);
 
+       /* invalidated slots need not apply */
+       if (invalidated)
+           continue;
+
        if (restart_lsn == InvalidXLogRecPtr)
            continue;
 
        if (s->data.database != dboid)
            continue;
 
+       /* NB: intentionally counting invalidated slots */
+
        /* count slots with spinlock held */
        SpinLockAcquire(&s->mutex);
        (*nslots)++;
        if (s->data.database != dboid)
            continue;
 
+       /* NB: intentionally including invalidated slots */
+
        /* acquire slot, so ReplicationSlotDropAcquired can be reused  */
        SpinLockAcquire(&s->mutex);
        /* can't change while ReplicationSlotControlLock is held */
        {
            MyReplicationSlot = s;
            s->active_pid = MyProcPid;
-           s->data.invalidated_at = restart_lsn;
+           s->data.invalidated = RS_INVAL_WAL_REMOVED;
+
+           /*
+            * XXX: We should consider not overwriting restart_lsn and instead
+            * just rely on .invalidated.
+            */
            s->data.restart_lsn = InvalidXLogRecPtr;
 
            /* Let caller know */
 
            nulls[i++] = true;
 
        /*
-        * If invalidated_at is valid and restart_lsn is invalid, we know for
-        * certain that the slot has been invalidated.  Otherwise, test
-        * availability from restart_lsn.
+        * If the slot has not been invalidated, test availability from
+        * restart_lsn.
         */
-       if (XLogRecPtrIsInvalid(slot_contents.data.restart_lsn) &&
-           !XLogRecPtrIsInvalid(slot_contents.data.invalidated_at))
+       if (slot_contents.data.invalidated != RS_INVAL_NONE)
            walstate = WALAVAIL_REMOVED;
        else
            walstate = GetWALAvailability(slot_contents.data.restart_lsn);
 
    RS_TEMPORARY
 } ReplicationSlotPersistency;
 
+/*
+ * Slots can be invalidated, e.g. due to max_slot_wal_keep_size. If so, the
+ * 'invalidated' field is set to a value other than _NONE.
+ */
+typedef enum ReplicationSlotInvalidationCause
+{
+   RS_INVAL_NONE,
+   /* required WAL has been removed */
+   RS_INVAL_WAL_REMOVED,
+} ReplicationSlotInvalidationCause;
+
 /*
  * On-Disk data of a replication slot, preserved across restarts.
  */
    /* oldest LSN that might be required by this replication slot */
    XLogRecPtr  restart_lsn;
 
-   /* restart_lsn is copied here when the slot is invalidated */
-   XLogRecPtr  invalidated_at;
+   /* RS_INVAL_NONE if valid, or the reason for having been invalidated */
+   ReplicationSlotInvalidationCause invalidated;
 
    /*
     * Oldest LSN that the client has acked receipt for.  This is used as the