github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/datastore/crdb/pool/node_id.go (about)

     1  package pool
     2  
     3  import (
     4  	"github.com/jackc/pgx/v5"
     5  )
     6  
     7  // The Postgres wire protocol sends a `BackendKey` when it establishes a new
     8  // connection with a client. The `BackendKey` includes two fields, `ProcessID`
     9  // and `SecretKey`.
    10  //
    11  // In postgres, the ProcessID is the PID of the process that is
    12  // handling the current session, and `SecretKey`, which is unique per session
    13  // and is for cancelling a running query (out of band, on a new connection).
    14  //
    15  // In the wire protocol, the BackendKey is a single 64 bit field.
    16  //
    17  // In CockroachDB, a "SqlInstanceID" and random data get encoded into the
    18  // BackendKey in one of two ways:
    19  //
    20  //   - A 12 bit node ID and 52 bits of random data. This is used when talking to
    21  //     standalone (non-multitenant) CockroachDB nodes.
    22  //   - A 32 bit instance ID and 32 bits of random data. This is used when talking
    23  //     to multi-tenant CockroachDB nodes - it is stable for a single sql instance
    24  //     but may not correspond to the physical node ID.
    25  //
    26  // The first bit is a sentinel that indicates which type of encoding is used.
    27  //
    28  // In both cases, random data fills the SecretKey portion of the BackendKey and
    29  // for compatibility with postgres, can also be used to cancel running queries
    30  // (CockroachDB also has a separate first-class CANCEL command).
    31  //
    32  // This diagram shows how Cockroach and Postgres encode/decode the bits of the
    33  // BackendKey field:
    34  //
    35  //	0                   1                   2                   3                   4                   5                   6
    36  //	0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
    37  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    38  //	|                    PG BackendKey.ProcessID                    |                    PG BackendKey.SecretKey                    |
    39  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    40  //	|0|  Short ID (Node ID) |                                              Random Data                                              |
    41  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    42  //	|1|                           Long ID                           |                           Random Data                         |
    43  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    44  //
    45  // For tracking connections to CRDB nodes this means that the SqlInstanceID can be used to track which nodes have
    46  // connections.
    47  //
    48  // Refs:
    49  // - get_backend_pid(): https://www.postgresql.org/docs/15/functions-info.html
    50  // - BackendKey and query cancellation: https://www.postgresql.org/docs/current/protocol-flow.html
    51  // - BackendKey wire definition: https://github.com/jackc/pgproto3/blob/master/backend_key_data.go
    52  // - CRDB implementation: https://github.com/cockroachdb/cockroach/blob/fd33b0a3f5daeb6045e4c9ec925db8ef8ca38ca8/pkg/sql/pgwire/pgwirecancel/backend_key_data.go#L21
    53  
    54  // nodeID returns the sqlInstanceID for a pgxpool.Conn
    55  func nodeID(conn *pgx.Conn) uint32 {
    56  	return sqlInstanceID(conn.PgConn().PID())
    57  }
    58  
    59  // sqlInstanceID returns the instance ID encoded into the PID field
    60  func sqlInstanceID(pid uint32) uint32 {
    61  	// If leading bit is 0, we have a "short" sqlInstanceID
    62  	// which should be the nodeID for standalone Cockroach nodes.
    63  	// The first 12 bits are the id, the rest is random
    64  	if pid&(1<<31) == 0 {
    65  		return pid >> 20
    66  	}
    67  
    68  	// If the leading bit is 1, we have a "long" sqlInstanceID
    69  	// These are stable for all connections to the same SQL node but
    70  	// may not correspond to the physical node ID.
    71  	// This should only happen when talking to a tenanted CRDB cluster.
    72  
    73  	// clear out the sentinel bit - the rest is the instance ID
    74  	return pid &^ (1 << 31)
    75  }