diff --git a/lib/storage/index_db.go b/lib/storage/index_db.go
index bde3e0cd35..ddccd83f2a 100644
--- a/lib/storage/index_db.go
+++ b/lib/storage/index_db.go
@@ -1775,7 +1775,7 @@ func (is *indexSearch) updateMetricIDsForTagFilters(metricIDs *uint64set.Set, tf
 		}
 		minMetricIDs = mIDs
 	}
-	metricIDs.Union(minMetricIDs)
+	metricIDs.UnionMayOwn(minMetricIDs)
 	return nil
 }
 
@@ -2094,7 +2094,7 @@ func (is *indexSearch) getMetricIDsForTimeRange(tr TimeRange, maxMetrics int, ac
 			err := isLocal.getMetricIDsForDate(date, &result, maxMetrics, accountID, projectID)
 			mu.Lock()
 			if metricIDs.Len() < maxMetrics {
-				metricIDs.Union(&result)
+				metricIDs.UnionMayOwn(&result)
 			}
 			if err != nil {
 				errGlobal = err
@@ -2140,7 +2140,7 @@ func (is *indexSearch) tryUpdatingMetricIDsForDateRange(metricIDs *uint64set.Set
 			ok, err := isLocal.tryUpdatingMetricIDsForDate(date, &result, tfs, maxMetrics)
 			mu.Lock()
 			if metricIDs.Len() < maxMetrics {
-				metricIDs.Union(&result)
+				metricIDs.UnionMayOwn(&result)
 			}
 			if !ok {
 				okGlobal = ok
@@ -2229,7 +2229,7 @@ func (is *indexSearch) tryUpdatingMetricIDsForDate(date uint64, metricIDs *uint6
 			return true, nil
 		}
 	}
-	metricIDs.Union(result)
+	metricIDs.UnionMayOwn(result)
 	return true, nil
 }
 
diff --git a/lib/uint64set/uint64set.go b/lib/uint64set/uint64set.go
index 45ae9c88a1..ef8f898492 100644
--- a/lib/uint64set/uint64set.go
+++ b/lib/uint64set/uint64set.go
@@ -165,16 +165,33 @@ func (s *Set) sort() {
 
 // Union adds all the items from a to s.
 func (s *Set) Union(a *Set) {
+	s.union(a, false)
+}
+
+// UnionMayOwn adds all the items from a to s.
+//
+// It may own a if s is empty. This means that `a` cannot be used
+// after the call to UnionMayOwn.
+func (s *Set) UnionMayOwn(a *Set) {
+	s.union(a, true)
+}
+
+func (s *Set) union(a *Set, mayOwn bool) {
+	if mayOwn && s.Len() < a.Len() {
+		// Swap `a` with `s` in order to reduce the number of iterations in ForEach loop below.
+		// This operation is safe only if `a` is no longer used after the call to union.
+		*a, *s = *s, *a
+	}
+	if a.Len() == 0 {
+		// Fast path - nothing to union.
+		return
+	}
 	if s.Len() == 0 {
 		// Fast path - just copy a.
 		aCopy := a.Clone()
 		*s = *aCopy
 		return
 	}
-	if a.Len() == 0 {
-		// Fast path - nothing to union.
-		return
-	}
 	a.ForEach(func(part []uint64) bool {
 		for _, x := range part {
 			s.Add(x)