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  }