TypeID - Pengidentifikasi unik global yang type-safe, dapat diurutkan-K, terinspirasi dari ID Stripe
(github.com/jetpack-io)- TypeID adalah pengidentifikasi type-safe yang memperluas UUIDv7, format pengidentifikasi unik global yang terinspirasi dari cara Stripe API menggunakan prefix
- String kanonisnya berupa string huruf kecil, terdiri dari tiga bagian: type prefix ASCII snake_case huruf kecil maksimal 63 karakter, pemisah
_, dan suffix UUIDv7 26 karakter yang dienkode dengan base32 yang dimodifikasi - Type prefix mencegah kesalahan memakai ID
userdi tempat yang membutuhkan IDpost, dan saat debugging memungkinkan Anda langsung mengetahui tipe entitas yang dirujuk oleh pengidentifikasi tersebut - TypeID adalah superset dari UUID; jika informasi tipe dihapus lalu didekode, strukturnya menghasilkan UUIDv7 yang valid
- Dapat diurutkan-K sehingga bisa digunakan sebagai primary key database, dengan tujuan mengurangi masalah penurunan locality database dibandingkan UUIDv4 yang merupakan ID global sepenuhnya acak
- Encoding base32 memiliki karakteristik aman untuk URL, tidak peka huruf besar/kecil, menghindari karakter ambigu, dapat dipilih dengan double-click, dan lebih pendek daripada encoding hex tradisional UUID
- Implementasi resmi tersedia dalam Go, SQL, dan TypeScript, dengan versi spec terbaru v0.3.0
- Tool command-line dapat digunakan untuk membuat TypeID, mendecode UUID dari TypeID yang sudah ada, dan mengencode UUID menjadi TypeID
typeid new prefixuntuk membuat TypeID barutypeid decode prefix_01h2xcejqtf2nbrexx3vqjhp41untuk menampilkan type dan uuidtypeid encode prefix 0188bac7-4afa-78aa-bc3b-bd1eef28d881untuk membuat TypeID
- Di TypeID Converter dari Jetify, Anda dapat mengubah string TypeID menjadi UUID atau mengubahnya menjadi TypeID dalam format
prefix:UUID
1 komentar
Komentar Hacker News
Ada beberapa saran: sebaiknya string prefix ditetapkan dan didokumentasikan sebelum terlambat.
Melihat implementasi Go, karena memakai ASCII huruf kecil tampaknya tidak masalah, tetapi untuk tipe gabungan seperti
article-commentini menjadi ambigu.Pada proyek kompleks atau ORM, sulit menghindari pemisah, jadi layak dipertimbangkan untuk mengizinkan satu pemisah tunggal.
Di implementasi Go belum ada test, padahal kodenya mudah dibuat unit test, jadi ini harus ditambahkan.
Di Go, seperti implementasi UUID dari Google, pendekatan yang tepat adalah memakai fungsi parsing yang benar dan array byte internal; string sebaiknya hanya dipakai untuk rendering dan prefix.
Parsing saat ini terlalu permisif, dan jika suffix kosong tampaknya masuk ke mode pembuatan; pengindeksan setelah
SplitNsepertinya akan panic jika tidak ada garis bawah.Saya mencoba mencari cela pada desainnya sendiri, tetapi pada akhirnya tampaknya desain ini berhasil menemukan sweet spot trade-off yang baik.
Meski begitu, masukannya benar, dan seiring bertambahnya implementasi di berbagai bahasa, perlu dipastikan semuanya kompatibel, jadi kami berencana menambahkan kumpulan test yang lebih ketat.
Terkait prefix, saya ingin tahu apakah yang dikhawatirkan adalah bahwa himpunan karakter yang diizinkan belum didefinisikan dalam spesifikasi.
Dan prefix juga sudah diperjelas dalam spesifikasi.
Prefix ditentukan sesuai domain penggunaan.
Saya sudah memakai pendekatan seperti ini selama beberapa tahun, tetapi ada dua perbedaan.
Pertama, saya tidak menganggap orang benar-benar mengetik nilainya secara manual, jadi kebingungan antara
ldan1tidak terlalu saya pedulikan.Sebagai gantinya, saya memakai base32 tanpa vokal
eiouuntuk mengurangi kemungkinan kata, terutama kata-kata kasar, muncul secara kebetulan.Kedua, saya menambahkan dua karakter base32 yang diberi salt sebagai checksum.
Saat nilainya aneh karena kesalahan atau niat jahat, kita tidak perlu sampai melakukan query ke penyimpanan data; saya tidak tahu kenapa implementasi lain tidak melakukan ini.
analritadan kami menerima keluhan.Mungkin layak mempertimbangkan untuk memasukkan
ajuga ke karakter yang dikecualikan.Jika diasumsikan orang tidak mengetik manual, saya bertanya-tanya jalur apa yang bisa membuat nilai salah dimasukkan karena kesalahan.
Mungkin ada kesalahan salin/tempel atau halaman merendernya dalam huruf besar, tetapi kalau base32, huruf besar/kecil bisa dinormalisasi.
Saya juga penasaran bagaimana checksum 2 byte yang tidak aman secara kriptografis bisa membantu dalam kasus berniat jahat.
Saya sedang mempertimbangkan apakah sebaiknya dimasukkan ke spesifikasi TypeID.
Saya tidak tahu seberapa banyak ini mengurangi query database, tetapi bagus karena saat decoding tidak jatuh ke error yang lebih ambigu.
Terlepas dari isi artikel, ada tautan “Crockford's alphabet”: https://www.crockford.com/base32.html
Skema base32 ini mengecualikan
IdanLyang bisa tertukar dengan1,Oyang bisa tertukar dengan0, sertaUDi halaman itu, alasan menghapus
Udisebut “ketidaksenonohan yang tidak disengaja”, tapi saya tidak paham maksudnya apaUntuk membuat alfabet base32 yang bagus dari karakter alfanumerik yang tidak membingungkan, cukup hapus
O,I, danLMeski begitu masih tersisa 33 karakter, jadi harus menghapus satu lagi, dan karena karakter mana pun tidak masalah, ia seolah memberi alasan arbitrer untuk karakter terakhir yang dihapus
Kalau ID itu terlihat oleh pengguna, alasannya tidak sepenuhnya mengada-ada, tetapi tentu saja sama sekali bukan pencegahan yang sempurna
IdanOsudah dikecualikan laluUjuga dihapus, cukup banyak kombinasi tiga atau empat huruf yang tampak kasar bisa dihilangkanTentu saja karena masih ada
A, masih ada kombinasi yang mungkin, tetapi kemungkinannya sangat rendahUbelum ada pada masa Cicero, jadi itu huruf yang begitu vulgar sampai mendekati cabulDi perusahaan, kami mendefinisikan beberapa “basis” baru untuk kode QR, dan secara pribadi saya melihat ini sebagai area yang kurang dimanfaatkan dalam ilmu komputer
Kami sedang memecahkan masalah slug URL, dan karena panjangnya cukup besar, kami menilai 5 bit per byte bisa mengurangi kerepotan penyalinan manual
Pada akhirnya kami masih punya ruang untuk menghapus beberapa huruf lagi, dan setelah semua orang pulang, kami berdua duduk lalu memeringkat tingkat penghinaan kata-kata kasar untuk menentukan huruf mana yang akan dihapus
Setelah itu saya meyakinkan bahwa ungkapan peyoratif adalah masalah yang lebih besar, jadi kami fokus menghapus
nalih-alihutggrhanya terdengar lucu, tetapin**rakan menjadi masalah yang memaksa percakapan tidak nyaman dengan berbagai tim HRSet karakter akhirnya kini agak samar dalam ingatan saya, tetapi biasanya kami menangani
[a-z][0-9]dan ada banyak simbol yang tidak bisa dipakai di URL atau sulit dibacakanSeingat saya, kami menghapus
0,l, dan1semuanya, dan tampaknya kami menganggap penyalinan manual dilakukan seluruhnya dalam huruf besar atau seluruhnya dalam huruf kecil0obukan masalah, begitu juga1LSaya kurang menyukai encoding Crockford
Dalam praktiknya, ketika mendukung secara teknis atau menganalisis nilai yang dienkode dengan cara ini, saya merasa itu jelas sebuah kesalahan
Alfabet ini dirancang untuk sasaran yang sebenarnya jarang, seperti membacakan identifier lewat telepon, dan justru memasukkan ambiguitas
Dalam situasi ketika Anda melakukan
greppada log atau cross-reference berdasarkan string encoding itu sendiri, ini bencana, dan karena tanda hubung juga diizinkan, itu menjadi sumber utama kesalahan copy/paste dan pemenggalan barisJarang ada orang mengetik identifier objek secara manual, tetapi menyalin/menempel di antara aplikasi, chat, dan forum, meneruskannya lewat email, serta mencarinya di file log adalah hal yang umum
Dalam kondisi seperti ini, kemudahan pelafalan tidak berarti, case-insensitive justru mengganggu, dan yang dibutuhkan adalah konsistensi serta ketahanan terhadap paste dan pemenggalan baris
base58 adalah encoding bijektif yang lebih cocok untuk kebutuhan ini, bahkan lebih pendek
Terinspirasi oleh Stripe, selama beberapa tahun saya memakai UUID berenkode base58 dengan prefix tipe seperti
user_1BzGURpnHGn6oNru84B3Risebagai identifier objekNamun perlu diingat bahwa encoding base32 Douglas Crockford dirancang 20 tahun lalu, ketika lingkungan penggunaannya cukup berbeda
Namun saya tidak ingin bergantung pada case-sensitive, jadi akhirnya condong ke base32
Misalnya, Anda mungkin ingin memakai ID sebagai nama file, dan bisa saja berada di lingkungan yang terikat pada sistem file case-insensitive
TypeID selalu memakai alfabet Crockford dalam huruf kecil; bukan berarti memakai seluruh aturan encoding Crockford
TypeID tidak mengizinkan tanda hubung, dan juga tidak mengizinkan karakter ambigu ditulis berbeda sehingga ID yang sama bisa dienkode dalam beberapa cara
Namun kadang kita perlu mengetikkannya langsung dari screenshot atau screen sharing, atau dari perangkat lain yang tidak memudahkan pengambilan identifier
Crockford base32 juga berfungsi, tetapi saya tidak terlalu menyukainya
Secara pribadi, pilihan pertama saya adalah memakai KSUID dengan prefix tipe
Anda bisa mendapatkan ID 160-bit yang K-sortable dengan encoding base62, dan itu cocok jika tidak harus menggunakan ID 128-bit demi kompatibilitas
Encoding base32 favorit saya adalah z-base-32, karena terasa lebih nyaman dilihat: https://philzimmermann.com/docs/human-oriented-base-32-encod...
Masalah terbesar base58 adalah cocok untuk bilangan bulat, tetapi kurang cocok untuk data biner arbitrer seperti kunci kriptografi, dan string case-sensitive kurang enak dilihat
Rapi
Saya suka prefix “type-safe”
Di ORM kami, pendekatan serupa—menambahkan tag per entitas secara otomatis ke ID integer di dalam DB—kami sebut tagged ids: https://joist-orm.io/docs/advanced/tagged-ids
Kami memakai
:sebagai pemisah, tetapi untuk urusan salin/tempel dengan klik dua kali, saya sedikit menyesal tidak memakai_Secara teori, akan sangat mudah membuat Joist mengubah “kolom UUID di DB” menjadi “TypeID di model domain”, tetapi saat ini sepertinya belum bisa dilakukan hanya lewat konfigurasi di ruang pengguna
Tetap saja, ini ide yang bagus
ID elemen berbentuk seperti
t3_15bfi0, dant3_adalah prefix tipet3adalah postingan,t1adalah komentar,t5adalah subreddit, dan sisanya adalah encoding base36 dari primary key auto-increment.)Titik dua akan di-encode sebagai
%dalam URL encoding, membuat ID jadi lebih panjang, dan URL menjadi jelek serta panjang, yang sangat menjengkelkanUUIDv7 sudah populer di HN selama bertahun-tahun, dan saya penasaran kapan akan menjadi standar yang benar-benar matang serta kapan library, database, dan ekosistem lainnya akan mendukungnya secara native
Kebanyakan software tidak peduli pada bit tertentu di dalam UUID, jadi mestinya bisa dipakai hari ini juga
Jika ada software yang peduli pada bit tertentu, Anda bisa menirunya seperti UUIDv4, dan bit-bit seperti itu pun bisa saja dihasilkan secara acak
Jika membutuhkan prosedur pembuatan, Anda bisa menulisnya sendiri, dan itu tidak sulit
Misalnya, saya mengelola library UUID untuk Dart, dan rilis mayor beta terbaru sudah mencakup v6, v7, dan v8 kustom
Daftar implementasi juga ada di suatu tempat, dan karena saya tercantum di halaman itu sebagai maintainer library, para penulis memberi tahu saya setiap kali ada draf baru
Saya suka bagian “bisa disalin/ditempel dengan klik dua kali”
Detail itu penting
Saya punya beberapa keluhan tentang UUID
Saya tidak mengerti mengapa kita tidak cukup menggabungkan waktu + angka acak saja, tanpa ritual seperti versioning UUID
Jika locality tidak penting, gunakan saja angka acak 128-bit apa adanya
Dari pengalaman, kebanyakan orang tampaknya menganggap UUID harus disimpan dalam representasi heksadesimal yang ramah dibaca manusia, bahkan termasuk tanda hubung
Itu sangat memboroskan ruang di database, jaringan, dan memori
Misalnya ULID https://github.com/ulid/spec lebih pendek dan menyimpan waktu sehingga terurut secara leksikografis
Kenyataannya sama seperti masalah rekayasa lain: tuliskan requirement-nya dan lihat apa yang menyelesaikannya
Membaca kelebihan berbagai format sangat membantu karena memungkinkan kita memanfaatkan hal-hal yang sudah dipelajari orang lain dengan susah payah
Kemampuan diurutkan dan locality berbasis waktu mungkin tidak terpikir secara alami, tetapi jika Anda membutuhkannya, jauh lebih baik mengetahuinya sejak awal daripada menyadari 4 tahun kemudian bahwa data itu sudah terbuang
Beberapa format UUID juga memperkenalkan sendiri masalah keamanan kecil; misalnya kebocoran alamat MAC pada UUID v1, yang sebaiknya dihindari
Jika solusi yang sudah ada cocok untuk use case Anda, pakai saja
Jika tidak, Anda tidak perlu terlalu khawatir
Secara pribadi, di tempat saya paling banyak memakai UUID, saya menetapkan “kirim saja string unik; kalau merasa lebih tenang, pakai library UUID apa pun yang Anda mau”
Sistem ini tampaknya punya format unik untuk setiap sumber data, tetapi itu tidak masalah
Volumenya hanya puluhan ribu per hari, jadi tidak ada masalah skala, dan tidak perlu mengurutkan berdasarkan UUID
Ini lebih seperti token unik yang dibuat pengirim untuk setiap pesan daripada identifier sungguhan, dipakai untuk mendeteksi kedatangan duplikat di hilir dalam sistem heterogen dengan banyak queue
Keunikan global juga tidak diperlukan; cukup unik di dalam shard kecil, dan secara prinsip tidak masalah jika berulang setelah waktu berlalu
Namun demi kesederhanaan, kami menjaga sistem agar unik sepanjang seluruh periode
Jika membuatnya sendiri, kami membaca data dari
/dev/urandomlalu meng-encode-nya dengan base64, dan memilih ukuran yang tidak berakhir dengan==Ini pun bukan karena masalah nyata, melainkan alasan estetika
Meski begitu, saya tetap tidak tahu mengapa tanda hubung diperlukan, dan menurut saya versi-versi lain tidak terlalu bermakna
Jika tidak benar-benar membutuhkan UUIDv7 atau UUID, https://github.com/segmentio/ksuid menyediakan ruang kunci yang jauh lebih besar
Jika membutuhkan namespace, cukup tambahkan prefix string, dan kemungkinan tabrakan KSUID jauh lebih kecil daripada UUID versi mana pun
Di antara generator ID umum yang memiliki timestamp yang dapat diurutkan, ksuid adalah yang terbaik, dan ada library untuk sebagian besar bahasa
UUID v1~v7 sangat boros
Jika Anda sanggup menanggung bit tambahan—dan kebanyakan kasus mungkin bisa—KSUID adalah format yang bagus
[1] https://github.com/sophiabits/resource-id
Namun masalah besar ksuid adalah ukurannya 160 bit, sehingga tidak cocok dengan tipe UUID native di database, misalnya PostgreSQL, dan menimbulkan biaya performa
1: https://github.com/svix/rust-ksuid
2: https://github.com/svix/python-ksuid
K-sortable adalah konsep yang bagus, dan key yang terurut secara lemah menyelesaikan banyak use case
Saya juga suka representasi string terkompresi yang bertipe
Namun saya khawatir akan muncul banyak masalah keamanan sebagai efek samping yang tidak disengaja dari UUID v7
Orang seharusnya tidak memakai UUID sebagai token, dan juga tidak seharusnya memakai primary key DB seperti itu, tetapi dalam praktiknya mereka melakukannya
UUID v4 pada dasarnya adalah angka acak kriptografis, jadi menurut saya banyak kerentanan keamanan sejauh ini kebetulan lolos tanpa masalah
Saya sempat mengira pada UUID v7 data acak sebenarnya turun menjadi 32 bit, dan merasa developer perlu diedukasi dengan baik bahwa UUID bisa ditebak
Koreksi: sepertinya saya salah membaca soal kemungkinan UUID v7 dapat ditebak
Drafnya merekomendasikan CSPRNG untuk bit acak, menyatakan entropinya minimal 74 bit, dan dirancang agar “tidak dapat ditebak”
Untuk penggunaan terkait keamanan memang disarankan memakai “UUID v4”, tetapi mungkin itu terkait makna timestamp
Jika namanya UUIDv4 dan UUIDv7, bukankah akan terus muncul kebingungan tanpa akhir karena harus mengingat mana yang bagus untuk database dan mana yang bagus untuk token sekali pakai?
Saya juga tidak tahu solusi yang kompatibel ke belakang
Di Elixir, saat membuat UUID v4, fungsi yang dipakai adalah
UUID.uuid4()Secara teori, kita bisa memindai penggunaannya di kode, tetapi hal-hal seperti ini semuanya meningkatkan kemungkinan error
Yang berbasis hash seperti v3 juga sama
v4 sederhana dan kecil kemungkinan disalahgunakan