GoSuda

Randflake ID: elosztott, 64 bites egyedi azonosító-generálás Sparx64 blokk-titkosítóval

By Lemon Mint views ...

Tekintsünk egy olyan helyzetet, amikor egyedi, 64 bites véletlen számokat kell generálnunk, és külső felek ne tudják megjósolni a következő vagy az előző számot.

Ebben az esetben generálhatunk egy 8 bájtos véletlen értéket, ellenőrizhetjük, hogy az már létezik-e az adatbázisban, majd tárolhatjuk, ha egyedi.

Ennek a módszernek azonban számos hátránya van. Minden generált számot adatbázisban kell tárolnunk az egyediség biztosításához. Az, hogy legalább egy adatbázis-kéréssel kell körbejárnunk, késleltetési problémákat okoz, különösen elosztott környezetben, ahol a skálázhatóság kulcsfontosságú.

Ezen problémák megoldására vezetjük be a Randflake ID-t: egy elosztott, egyenletes, kiszámíthatatlan és egyedi véletlen azonosító-generátort.

Hogyan működik a Randflake ID?

A Randflake ID a Snowflake ID-ból indul ki, amely az X (korábban Twitter) által kifejlesztett, széles körben használt, k-rendezett azonosító-generálási mechanizmus.

A Snowflake ID az aktuális időbélyeget, egy csomóponti azonosítót (node ID) és egy helyi számlálót használ az azonosító előállítására.

Ezt a megközelítést tovább bővítettük véletlen egyedi azonosítók generálására, és egy új titkos kulcs elemmel egészítettük ki.

A kulcsgondolat az, hogy a meglévő egyedi azonosító-generátorhoz blokk-titkosító réteget adunk, hogy a számok közötti összefüggés megjóslása megvalósíthatatlan legyen.

A blokk-titkosító egy alapvető kriptográfiai függvény, amely a rögzített hosszúságú nyílt szöveg blokkját azonos hosszúságú titkosszöveg-blokká alakítja. Ezt az átalakítást egy kriptográfiai kulcs szabályozza. A blokk-titkosító megkülönböztető jellemzője a visszafordíthatósága: egy-egyértelmű (bijektív) függvénynek kell lennie, amely biztosítja, hogy minden egyedi bemenet egyedi kimenetnek felel meg, és fordítva. Ez a tulajdonság alapvető fontosságú a visszafejtéshez, mivel a helyes kulcs alkalmazásakor az eredeti nyílt szöveg visszanyerhető a titkosszövegből.

Ha a blokk-titkosítót egy-egyértelmű függvényként alkalmazzuk, garantálhatjuk, hogy minden egyedi bemenet a definiált tartományon belül egy megfelelő egyedi kimenetet állít elő.

A szerkezet és a tervezési szempontok

Ezekre az alapfogalmakra építve vizsgáljuk meg, hogyan valósítja meg a Randflake ID ezeket az elveket a gyakorlatban.

A Randflake ID szerkezete egy 30 bites, másodperc pontosságú unix időbélyeget, egy 17 bites csomópont-azonosítót, egy 17 bites helyi számlálót és a sparx64 algoritmuson alapuló 64 bites blokk-titkosítót tartalmaz.

Néhány tervezési döntés:

  • A Google Cloud Platform egyes virtuális géppéldányai 0,2 ms pontossággal tudják szinkronizálni az órát, de ez a pontosság nem érhető el a nyilvános internetenvagy más felhőszolgáltatóknál.

  • Másodperc pontosságot választottunk, mert a csomópontok közötti óraszinkronizálást csak néhány milliszekundumos felbontáson tudjuk hatékonyan megvalósítani.

  • A 17 bites csomópont-azonosító egyidejűleg 131072 egyedi generátort tesz lehetővé, amelyek folyamatonként, magonként vagy szálanként rendelhetők hozzá.

  • Nagy átviteli sebességű rendszerekben a 17 bites helyi számláló elégtelen lehet. Az átviteli sebesség illesztéséhez több generátort rendelhetünk hozzá, mindegyiket egyedi csomópont-azonosítóval, hogy egyetlen folyamatban vagy szálon működjenek.

  • A 64 bites blokk-titkosítóként a sparx64 algoritmust alkalmaztuk, amely egy modern, könnyűsúlyú, ARX-alapú blokk-titkosító.

  • A Randflake azonosítók belső nyomonkövethetőséget biztosítanak: eredeti csomópont-azonosítójukat és időbélyegüket csak a titkos kulccsal rendelkezők láthatják.

  • Az elméleti maximális átviteli sebesség 17 179 869 184 azonosító/s, ami a legtöbb globális méretű alkalmazás számára elegendő.

A Randflake ID generálás álkódja

A Randflake ID generálási folyamatának további szemléltetésére a következő Python-álkód egyszerűsített megvalósítást nyújt:

 1import time
 2import struct
 3from .sparx64 import Sparx64
 4
 5# Konstansok
 6RANDFLAKE_EPOCH_OFFSET = 1730000000  # Vasárnap, 2024. október 27., 3:33:20 UTC
 7
 8# Bitkiosztás
 9RANDFLAKE_TIMESTAMP_BITS = 30  # 30 bit az időbélyegnek (34 éves élettartam)
10RANDFLAKE_NODE_BITS = 17  # 17 bit a csomópont-azonosítónak (legfeljebb 131072 csomópont)
11RANDFLAKE_SEQUENCE_BITS = 17  # 17 bit a sorszámnak (legfeljebb 131072 sorszám)
12
13# Származtatott konstansok
14RANDFLAKE_MAX_TIMESTAMP = RANDFLAKE_EPOCH_OFFSET + (1 << RANDFLAKE_TIMESTAMP_BITS) - 1
15RANDFLAKE_MAX_NODE = (1 << RANDFLAKE_NODE_BITS) - 1
16RANDFLAKE_MAX_SEQUENCE = (1 << RANDFLAKE_SEQUENCE_BITS) - 1
17
18class Randflake:
19    def __init__(self, node_id: int, secret: bytes):
20        self.node_id = int(node_id)
21        self.sequence = int(0)
22        self.rollover = int(time.time())
23        self.sbox = Sparx64(secret)
24
25    def _new_raw(self) -> int:
26        while True:
27            now = int(time.time())
28
29            self.sequence += 1
30            sequence = self.sequence
31
32            if sequence > RANDFLAKE_MAX_SEQUENCE:
33                if now > self.rollover:
34                    self.sequence = 0
35                    self.rollover = now
36                    sequence = 0
37                else:
38                    continue
39
40            timestamp = now - RANDFLAKE_EPOCH_OFFSET
41            return (timestamp << 34) | (self.node_id << 17) | sequence
42
43    def generate(self) -> int:
44        id_raw = self._new_raw()
45        src = struct.pack("<q", id_raw)
46        dst = bytearray(8)
47        self.sbox.encrypt(dst, src)
48        return struct.unpack("<q", dst)[0]

Egy éles használatra kész, csomópont-azonosító bérleti mechanizmust tartalmazó Randflake-implementáció a GitHubon érhető el.

Egyéb szempontok

Ebben a szakaszban a Randflake ID megvalósításának néhány további szempontját tárgyaljuk.

Csomópont-azonosítók koordinálása

Bérleten alapuló csomópont-azonosító koordinálást javasolunk.

Ebben a megközelítésben egy központi koordinációs szolgáltatás minden generátornak egyedi csomópont-azonosítót rendel.

Ezt a csomópont-azonosítót a bérleti időszak alatt nem osztják ki újra, hogy biztosítsák az egyediséget, ami csökkenti a koordinációs szolgáltatással való gyakori kommunikáció szükségességét.

A bérletet birtokoló generátor kérheti a bérlet megújítását a koordinációs szolgáltatástól, ha a megújítási feltétel teljesül.

A megújítási feltétel olyan kritériumok összességét jelenti, amelyeknek teljesülniük kell ahhoz, hogy a bérlet megújítható legyen, például hogy a generátor még aktív, és továbbra is szüksége van a csomópont-azonosítóra.

A bérletes (leaseholder) a csomópont-azonosító tartomány aktuális birtokosa.

A bérlet akkor tekinthető aktívnak és nem lejártnak, ha érvényes időszakán belül van.

Így a bérlet megújítási időszakonként legfeljebb egy körutazásra csökkenthető, ami minimalizálja a késleltetést és javítja a hatékonyságot az elosztott rendszerekben.

Védelem hibás óra ellen

A bérletszolgáltatásnak ellenőriznie kell az időbélyegek konzisztenciáját a bérlet kiosztásakor. A kiosztott bérlet kezdési idejének nagyobbnak vagy egyenlőnek kell lennie az utolsó bérlet kezdési idejével.

A generátornak el kell utasítania a kérést, ha az aktuális időbélyeg kisebb a bérlet kezdési idejénél, vagy nagyobb a bérlet befejezési idejénél.

Ez az eljárás fontos a generált azonosítók egyediségének védelmében, amikor az óra visszafelé ugrik. Például, ha az óra visszafelé ugrik, előfordulhat, hogy egy új bérletet a korábban kiosztott bérletnél korábbi kezdési idővel osztanak ki, ami duplikált azonosítók generálásához vezethet. A létező bérleti időszakon belüli időbélyegű kérések elutasításával megakadályozzuk ezt a helyzetet, és fenntartjuk az azonosítók egyediségét.

Azonosítóeloszlás egyenletessége

Az azonosítóeloszlás hisztogramja

A fenti hisztogram alapján láthatjuk, hogy a generált Randflake azonosítók eloszlása nagyon egyenletes. Ez arra utal, hogy az azonosítóeloszlás közvetlenül használható shardolási kulcsként.

Következtetés

Ebben a cikkben a Randflake-et mutattuk be, egy újszerű azonosító-generálási algoritmust, amely ötvözi a Snowflake és a Sparx64 előnyeit.

Reméljük, hogy ez a cikk átfogó képet adott a Randflake-ről és megvalósításáról.

A Randflake teljes forráskódja a GitHubon érhető el.

Ha kérdése vagy javaslata van, nyugodtan lépjen velünk kapcsolatba. Emellett olyan közreműködőket keresünk, akik segítenek a Randflake fejlesztésében és más programozási nyelvekben történő megvalósításában.

Egy éles használatra kész koordinációs szolgáltatást tervezünk kiadni a Randflake és a Snowflake számára, amely nyílt forráskódú lesz a GitHubon. Figyelje a frissítéseket!