2022-02-08 09:25:59 -08:00
|
|
|
// https://github.com/postgres/postgres/blob/27b77ecf9f4d5be211900eda54d8155ada50d696/src/include/catalog/partition.h#L20
|
|
|
|
|
const PARTITION_HASH_SEED: u64 = 0x7A5B22367996DCFD;
|
|
|
|
|
|
2022-02-05 19:43:48 -08:00
|
|
|
pub struct Sharder {
|
|
|
|
|
shards: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Sharder {
|
|
|
|
|
pub fn new(shards: usize) -> Sharder {
|
|
|
|
|
Sharder { shards: shards }
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 09:25:59 -08:00
|
|
|
/// Hash function used by Postgres to determine which partition
|
|
|
|
|
/// to put the row in when using HASH(column) partitioning.
|
|
|
|
|
/// Source: https://github.com/postgres/postgres/blob/27b77ecf9f4d5be211900eda54d8155ada50d696/src/common/hashfn.c#L631
|
2022-02-08 11:10:28 -08:00
|
|
|
/// Supports only 1 bigint at the moment, but we can add more later.
|
2022-02-08 11:14:20 -08:00
|
|
|
pub fn pg_bigint_hash(&self, key: i64) -> usize {
|
2022-02-08 09:25:59 -08:00
|
|
|
let mut lohalf = key as u32;
|
|
|
|
|
let hihalf = (key >> 32) as u32;
|
|
|
|
|
lohalf ^= if key >= 0 { hihalf } else { !hihalf };
|
2022-02-15 22:45:45 -08:00
|
|
|
Self::combine(0, Self::pg_u32_hash(lohalf)) as usize % self.shards
|
2022-02-08 09:25:59 -08:00
|
|
|
}
|
|
|
|
|
|
2022-02-08 11:10:28 -08:00
|
|
|
#[inline]
|
2022-02-08 09:25:59 -08:00
|
|
|
fn rot(x: u32, k: u32) -> u32 {
|
2022-02-08 11:10:28 -08:00
|
|
|
(x << k) | (x >> (32 - k))
|
2022-02-08 09:25:59 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
fn mix(mut a: u32, mut b: u32, mut c: u32) -> (u32, u32, u32) {
|
|
|
|
|
a = a.wrapping_sub(c);
|
|
|
|
|
a ^= Self::rot(c, 4);
|
|
|
|
|
c = c.wrapping_add(b);
|
2022-02-08 10:05:10 -08:00
|
|
|
|
|
|
|
|
b = b.wrapping_sub(a);
|
2022-02-08 09:25:59 -08:00
|
|
|
b ^= Self::rot(a, 6);
|
|
|
|
|
a = a.wrapping_add(c);
|
2022-02-08 10:05:10 -08:00
|
|
|
|
|
|
|
|
c = c.wrapping_sub(b);
|
2022-02-08 09:25:59 -08:00
|
|
|
c ^= Self::rot(b, 8);
|
|
|
|
|
b = b.wrapping_add(a);
|
2022-02-08 10:05:10 -08:00
|
|
|
|
|
|
|
|
a = a.wrapping_sub(c);
|
2022-02-08 09:25:59 -08:00
|
|
|
a ^= Self::rot(c, 16);
|
|
|
|
|
c = c.wrapping_add(b);
|
2022-02-08 10:05:10 -08:00
|
|
|
|
|
|
|
|
b = b.wrapping_sub(a);
|
2022-02-08 09:25:59 -08:00
|
|
|
b ^= Self::rot(a, 19);
|
|
|
|
|
a = a.wrapping_add(c);
|
2022-02-08 10:05:10 -08:00
|
|
|
|
|
|
|
|
c = c.wrapping_sub(b);
|
2022-02-08 09:25:59 -08:00
|
|
|
c ^= Self::rot(b, 4);
|
|
|
|
|
b = b.wrapping_add(a);
|
2022-02-08 10:05:10 -08:00
|
|
|
|
2022-02-08 09:25:59 -08:00
|
|
|
(a, b, c)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
fn _final(mut a: u32, mut b: u32, mut c: u32) -> (u32, u32, u32) {
|
|
|
|
|
c ^= b;
|
2022-02-08 10:05:10 -08:00
|
|
|
c = c.wrapping_sub(Self::rot(b, 14));
|
2022-02-08 09:25:59 -08:00
|
|
|
a ^= c;
|
2022-02-08 10:05:10 -08:00
|
|
|
a = a.wrapping_sub(Self::rot(c, 11));
|
2022-02-08 09:25:59 -08:00
|
|
|
b ^= a;
|
2022-02-08 10:05:10 -08:00
|
|
|
b = b.wrapping_sub(Self::rot(a, 25));
|
2022-02-08 09:25:59 -08:00
|
|
|
c ^= b;
|
2022-02-08 10:05:10 -08:00
|
|
|
c = c.wrapping_sub(Self::rot(b, 16));
|
2022-02-08 09:25:59 -08:00
|
|
|
a ^= c;
|
2022-02-08 10:05:10 -08:00
|
|
|
a = a.wrapping_sub(Self::rot(c, 4));
|
2022-02-08 09:25:59 -08:00
|
|
|
b ^= a;
|
2022-02-08 10:05:10 -08:00
|
|
|
b = b.wrapping_sub(Self::rot(a, 14));
|
2022-02-08 09:25:59 -08:00
|
|
|
c ^= b;
|
2022-02-08 10:05:10 -08:00
|
|
|
c = c.wrapping_sub(Self::rot(b, 24));
|
2022-02-08 09:25:59 -08:00
|
|
|
(a, b, c)
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 11:10:28 -08:00
|
|
|
#[inline]
|
|
|
|
|
fn combine(mut a: u64, b: u64) -> u64 {
|
|
|
|
|
a ^= b
|
|
|
|
|
.wrapping_add(0x49a0f4dd15e5a8e3 as u64)
|
|
|
|
|
.wrapping_add(a << 54)
|
|
|
|
|
.wrapping_add(a >> 7);
|
|
|
|
|
a
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 10:05:10 -08:00
|
|
|
fn pg_u32_hash(k: u32) -> u64 {
|
|
|
|
|
let mut a: u32 = 0x9e3779b9 as u32 + std::mem::size_of::<u32>() as u32 + 3923095 as u32;
|
2022-02-08 09:25:59 -08:00
|
|
|
let mut b = a;
|
|
|
|
|
let c = a;
|
|
|
|
|
|
2022-02-08 11:10:28 -08:00
|
|
|
a = a.wrapping_add((PARTITION_HASH_SEED >> 32) as u32);
|
|
|
|
|
b = b.wrapping_add(PARTITION_HASH_SEED as u32);
|
2022-02-08 09:25:59 -08:00
|
|
|
let (mut a, b, c) = Self::mix(a, b, c);
|
|
|
|
|
|
2022-02-08 10:05:10 -08:00
|
|
|
a = a.wrapping_add(k);
|
2022-02-08 09:25:59 -08:00
|
|
|
|
2022-02-08 11:10:28 -08:00
|
|
|
let (_a, b, c) = Self::_final(a, b, c);
|
2022-02-08 09:25:59 -08:00
|
|
|
|
2022-02-08 10:05:10 -08:00
|
|
|
((b as u64) << 32) | (c as u64)
|
2022-02-08 09:25:59 -08:00
|
|
|
}
|
2022-02-05 19:43:48 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod test {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
2022-02-08 17:15:35 -08:00
|
|
|
// See tests/sharding/partition_hash_test_setup.sql
|
2022-02-08 11:14:20 -08:00
|
|
|
// The output of those SELECT statements will match this test,
|
|
|
|
|
// confirming that we implemented Postgres BIGINT hashing correctly.
|
2022-02-08 09:25:59 -08:00
|
|
|
#[test]
|
|
|
|
|
fn test_pg_bigint_hash() {
|
2022-02-08 11:10:28 -08:00
|
|
|
let sharder = Sharder::new(5);
|
2022-02-08 10:05:10 -08:00
|
|
|
|
2022-02-08 11:10:28 -08:00
|
|
|
let shard_0 = vec![1, 4, 5, 14, 19, 39, 40, 46, 47, 53];
|
2022-02-08 10:05:10 -08:00
|
|
|
|
2022-02-08 11:10:28 -08:00
|
|
|
for v in shard_0 {
|
|
|
|
|
assert_eq!(sharder.pg_bigint_hash(v), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let shard_1 = vec![2, 3, 11, 17, 21, 23, 30, 49, 51, 54];
|
|
|
|
|
|
|
|
|
|
for v in shard_1 {
|
|
|
|
|
assert_eq!(sharder.pg_bigint_hash(v), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let shard_2 = vec![6, 7, 15, 16, 18, 20, 25, 28, 34, 35];
|
|
|
|
|
|
|
|
|
|
for v in shard_2 {
|
|
|
|
|
assert_eq!(sharder.pg_bigint_hash(v), 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let shard_3 = vec![8, 12, 13, 22, 29, 31, 33, 36, 41, 43];
|
|
|
|
|
|
|
|
|
|
for v in shard_3 {
|
|
|
|
|
assert_eq!(sharder.pg_bigint_hash(v), 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let shard_4 = vec![9, 10, 24, 26, 27, 32, 37, 38, 42, 45];
|
|
|
|
|
|
|
|
|
|
for v in shard_4 {
|
|
|
|
|
assert_eq!(sharder.pg_bigint_hash(v), 4);
|
|
|
|
|
}
|
2022-02-08 09:25:59 -08:00
|
|
|
}
|
2022-02-05 19:43:48 -08:00
|
|
|
}
|