diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 469e7ab..853d8e0 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -168,7 +168,17 @@ btbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 	reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
 								   btbuildCallback, (void *) &buildstate);
 
-	/* okay, all heap tuples are indexed */
+	/*
+	 * By this point, all heap tuples are indexed so we don't need the
+	 * snapshot to complete the index build.
+	 *
+	 * Ideally, we'd like to drop our snapshot as soon as possible, to
+	 * avoid holding xmin back and causing bloat. That is only possible
+	 * if we are running a concurrent index build because that command
+	 * is sufficiently restricted to allow this to happen safely.
+	 */
+	if (indexInfo->ii_Concurrent)
+		PopActiveSnapshot();
 	if (buildstate.spool2 && !buildstate.haveDead)
 	{
 		/* spool2 turns out to be unnecessary */
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index ed6136c..469d455 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -770,11 +770,27 @@ DefineIndex(Oid relationId,
 	/* Now build the index */
 	index_build(rel, indexRelation, indexInfo, stmt->primary, false);
 
+	/*
+	 * At this point it is possible that the indexAM has popped its
+	 * snapshot and will be unable to run other commands, but we
+	 * accept that restriction because it means the snapshot is held
+	 * open for much less time.
+	 */
+	PopActiveSnapshotIfAny();
+
 	/* Close both the relations, but keep the locks */
 	heap_close(rel, NoLock);
 	index_close(indexRelation, NoLock);
 
 	/*
+	 * Now we need a snapshot so we can update our indexes. The snapshot
+	 * here is different from that used to build the index, but the only
+	 * thing we will do with it is update the pg_index row for the index
+	 * we have locked, so we're not in danger of mismatch.
+	 */
+	PushActiveSnapshot(GetTransactionSnapshot());
+
+	/*
 	 * Update the pg_index row to mark the index as ready for inserts. Once we
 	 * commit this transaction, any new transactions that open the table must
 	 * insert new entries into the index for insertions and non-HOT updates.
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index 92afc32..4d949f6 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -806,11 +806,19 @@ UpdateActiveSnapshotCommandId(void)
 void
 PopActiveSnapshot(void)
 {
+	Assert(ActiveSnapshot->as_snap->active_count > 0);
+	PopActiveSnapshotIfAny();
+}
+
+void
+PopActiveSnapshotIfAny(void)
+{
 	ActiveSnapshotElt *newstack;
 
-	newstack = ActiveSnapshot->as_next;
+	if (ActiveSnapshot == NULL)
+		return;
 
-	Assert(ActiveSnapshot->as_snap->active_count > 0);
+	newstack = ActiveSnapshot->as_next;
 
 	ActiveSnapshot->as_snap->active_count--;
 
diff --git a/src/include/utils/snapmgr.h b/src/include/utils/snapmgr.h
index 2618cc4..66368d2 100644
--- a/src/include/utils/snapmgr.h
+++ b/src/include/utils/snapmgr.h
@@ -75,6 +75,7 @@ extern void PushActiveSnapshot(Snapshot snapshot);
 extern void PushCopiedSnapshot(Snapshot snapshot);
 extern void UpdateActiveSnapshotCommandId(void);
 extern void PopActiveSnapshot(void);
+extern void PopActiveSnapshotIfAny(void);
 extern Snapshot GetActiveSnapshot(void);
 extern bool ActiveSnapshotSet(void);
 
