github.com/grafana/synthetic-monitoring-agent@v0.64.2/pkg/pb/synthetic_monitoring/ids.go (about) 1 // Copyright 2022 Grafana Labs 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package synthetic_monitoring 15 16 import ( 17 "fmt" 18 "math" 19 ) 20 21 // This file contains methods for converting IDs for Synthetic Monitoring 22 // objects (checks, tenants) from single-region (local) to region-aware IDs 23 // (global). This is needed for agents that run checks for multiple 24 // regions. 25 // 26 // As IDs are only unique within their region, we create a new space 27 // of IDs (global IDs) that avoids collisions when objects from 28 // different regions are handled. 29 // 30 // At the same time, it is necessary to undo the process and obtain 31 // the region and local ID from a global ID, so that results can be 32 // associated with their respective regions. 33 // 34 // How this works: 35 // 36 // Local IDs are positive, non-zero integers, assigned sequentially. 37 // 38 // Global IDs are negative, non-zero integers. This allows to tell them 39 // apart from Local IDs easily and for both to coexist with some safety. 40 // They are constructed by multiplying the original ID by 1000 (MaxRegions) 41 // and then adding a unique regionID (<1000). 42 // 43 // For example, check with ID 1234 in region 3 will have a global ID of 44 // -1234003. 45 // 46 // This reduces the space of IDs available by a factor of 1000, from 63 bits 47 // to 53, which is still more than enough. 48 const ( 49 // MaxRegions is the maximum number of regions supported. 50 MaxRegions = 1000 51 52 // MinRegionID is the minimum valid region ID. 53 MinRegionID = 1 54 55 // MaxRegionID is the maximum valid region ID. 56 MaxRegionID = MaxRegions - 1 57 58 // BadID is the ID value that is not valid in any case 59 // (as global, local or region ID). 60 BadID = 0 61 62 // MinLocalID is the smallest local ID, as 0 is not valid. 63 MinLocalID = 1 64 65 // MaxLocalID is the maximum value allowed for a local ID. 66 // This is the largest positive integer that can be multiplied 67 // by 1000 and 999 added to it and still fit in an int64. 68 // MaxLocalID = 9_223_372_036_854_774 69 MaxLocalID = (math.MaxInt64 / MaxRegions) - 1 70 71 // MaxGlobalID is the maximum value a global ID can hold. 72 // It is the equivalent to (MinLocalID, MinRegionID) 73 // MaxGlobalID = -1001 74 MaxGlobalID = -(MinLocalID*MaxRegions + MinRegionID) 75 76 // MinGlobalID is the minimum value a GlobalID can hold. 77 // MinGlobalID = -9_223_372_036_854_774_999 78 MinGlobalID = -(MaxLocalID*MaxRegions + MaxRegionID) 79 ) 80 81 // IsGlobalIDValid returns true if an ID is Global, false otherwise. 82 func IsGlobalIDValid(id int64) bool { 83 return MinGlobalID <= id && id <= MaxGlobalID 84 } 85 86 // IsRegionIDValid checks that a region ID is within bounds. 87 func IsRegionIDValid(id int) bool { 88 return MinRegionID <= id && id <= MaxRegionID 89 } 90 91 func IsLocalIDValid(id int64) bool { 92 return MinLocalID <= id && id <= MaxLocalID 93 } 94 95 // LocalIDToGlobalID converts the given localID to a global ID 96 // using the given region ID. 97 func LocalIDToGlobalID(localID int64, regionID int) (int64, error) { 98 if !IsLocalIDValid(localID) { 99 return BadID, BadLocalIDError(localID) 100 } 101 102 if !IsRegionIDValid(regionID) { 103 return BadID, BadRegionIDError(regionID) 104 } 105 106 return -(localID*MaxRegions + int64(regionID)), nil 107 } 108 109 // GlobalIDToLocalID converts a globalID back to a (local ID, region ID) pair. 110 func GlobalIDToLocalID(globalID int64) (localID int64, regionID int, err error) { 111 if !IsGlobalIDValid(globalID) { 112 return BadID, BadID, BadGlobalIDError(globalID) 113 } 114 115 localID = -globalID / MaxRegions 116 117 regionID = int(-globalID % MaxRegions) 118 if !IsRegionIDValid(regionID) { 119 return BadID, BadID, BadGlobalIDError(globalID) 120 } 121 122 return localID, regionID, nil 123 } 124 125 // BadRegionIDError type is returned when an invalid region ID is used. 126 type BadRegionIDError int 127 128 // ID returns the ID that caused the error. 129 func (n BadRegionIDError) ID() int { 130 return int(n) 131 } 132 133 // Error implements the error interface. 134 func (n BadRegionIDError) Error() string { 135 return fmt.Sprintf("bad region ID: %d", n.ID()) 136 } 137 138 // BadLocalIDError type is returned when an invalid local ID is used. 139 type BadLocalIDError int64 140 141 // ID returns the ID that caused the error. 142 func (n BadLocalIDError) ID() int64 { 143 return int64(n) 144 } 145 146 // Error implements the error interface. 147 func (n BadLocalIDError) Error() string { 148 return fmt.Sprintf("bad local ID: %d", n.ID()) 149 } 150 151 // BadGlobalIDError type is returned when an invalid global ID is used. 152 type BadGlobalIDError int64 153 154 // ID returns the ID that caused the error. 155 func (n BadGlobalIDError) ID() int64 { 156 return int64(n) 157 } 158 159 // Error implements the error interface. 160 func (n BadGlobalIDError) Error() string { 161 return fmt.Sprintf("bad global ID: %d", n.ID()) 162 }