github.com/thanos-io/thanos@v0.32.5/pkg/querysharding/analysis.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package querysharding 5 6 var excludedLabels = []string{"le"} 7 8 type QueryAnalysis struct { 9 // Labels to shard on 10 shardingLabels []string 11 12 // When set to true, sharding is `by` shardingLabels, 13 // otherwise it is `without` shardingLabels. 14 shardBy bool 15 } 16 17 func nonShardableQuery() QueryAnalysis { 18 return QueryAnalysis{ 19 shardingLabels: nil, 20 } 21 } 22 23 func (q *QueryAnalysis) scopeToLabels(labels []string, by bool) QueryAnalysis { 24 labels = without(labels, excludedLabels) 25 26 if q.shardingLabels == nil { 27 return QueryAnalysis{ 28 shardBy: by, 29 shardingLabels: labels, 30 } 31 } 32 33 if q.shardBy && by { 34 return QueryAnalysis{ 35 shardBy: true, 36 shardingLabels: intersect(q.shardingLabels, labels), 37 } 38 } 39 40 if !q.shardBy && !by { 41 return QueryAnalysis{ 42 shardBy: false, 43 shardingLabels: union(q.shardingLabels, labels), 44 } 45 } 46 47 // If we are sharding by and without the same time, 48 // keep the sharding by labels that are not in the without labels set. 49 labelsBy, labelsWithout := q.shardingLabels, labels 50 if !q.shardBy { 51 labelsBy, labelsWithout = labelsWithout, labelsBy 52 } 53 return QueryAnalysis{ 54 shardBy: true, 55 shardingLabels: without(labelsBy, labelsWithout), 56 } 57 } 58 59 func (q *QueryAnalysis) IsShardable() bool { 60 return len(q.shardingLabels) > 0 61 } 62 63 func (q *QueryAnalysis) ShardingLabels() []string { 64 if len(q.shardingLabels) == 0 { 65 return nil 66 } 67 68 return q.shardingLabels 69 } 70 71 func (q *QueryAnalysis) ShardBy() bool { 72 return q.shardBy 73 } 74 75 func intersect(sliceA, sliceB []string) []string { 76 if len(sliceA) == 0 || len(sliceB) == 0 { 77 return []string{} 78 } 79 80 mapA := make(map[string]struct{}, len(sliceA)) 81 for _, s := range sliceA { 82 mapA[s] = struct{}{} 83 } 84 85 mapB := make(map[string]struct{}, len(sliceB)) 86 for _, s := range sliceB { 87 mapB[s] = struct{}{} 88 } 89 90 result := make([]string, 0) 91 for k := range mapA { 92 if _, ok := mapB[k]; ok { 93 result = append(result, k) 94 } 95 } 96 97 return result 98 } 99 100 func without(sliceA, sliceB []string) []string { 101 if sliceA == nil { 102 return nil 103 } 104 if len(sliceA) == 0 { 105 return []string{} 106 } 107 if len(sliceB) == 0 { 108 return sliceA 109 } 110 111 keyMap := make(map[string]struct{}, len(sliceA)) 112 for _, s := range sliceA { 113 keyMap[s] = struct{}{} 114 } 115 for _, s := range sliceB { 116 delete(keyMap, s) 117 } 118 119 result := make([]string, 0, len(keyMap)) 120 for k := range keyMap { 121 result = append(result, k) 122 } 123 124 return result 125 } 126 127 func union(sliceA, sliceB []string) []string { 128 if len(sliceA) == 0 && len(sliceB) == 0 { 129 return []string{} 130 } 131 if len(sliceA) == 0 { 132 return sliceB 133 } 134 if len(sliceB) == 0 { 135 return sliceA 136 } 137 138 keyMap := make(map[string]struct{}, len(sliceA)) 139 for _, s := range sliceA { 140 keyMap[s] = struct{}{} 141 } 142 for _, s := range sliceB { 143 keyMap[s] = struct{}{} 144 } 145 146 result := make([]string, 0, len(keyMap)) 147 for k := range keyMap { 148 result = append(result, k) 149 } 150 151 return result 152 }