TypeID - Stripe ID'lerinden ilham alan tip güvenli, K-sıralanabilir, küresel olarak benzersiz tanımlayıcı
(github.com/jetpack-io)- TypeID, UUIDv7’yi genişleten tip güvenli bir tanımlayıcıdır; Stripe API’nin prefix kullanım biçiminden ilham alan, küresel olarak benzersiz bir tanımlayıcı formatı
- Resmî dize küçük harfli bir string’dir ve en fazla 63 karakterlik küçük harfli snake_case ASCII type prefix,
_ayırıcı ve değiştirilmiş base32 ile kodlanmış 26 karakterlik UUIDv7 suffix olmak üzere üç bölümden oluşur - Type prefix,
userID’sininpostID’si gereken yerde yanlışlıkla kullanılmasını önler ve hata ayıklama sırasında tanımlayıcının hangi varlık tipini işaret ettiğini hemen anlamayı sağlar - TypeID, UUID’nin bir üst kümesidir; tip bilgisi kaldırılıp decode edildiğinde geçerli bir UUIDv7 elde edilecek şekilde tasarlanmıştır
- K-sıralanabilir olduğu için veritabanı birincil anahtarı olarak kullanılabilir; tamamen rastgele küresel ID olan UUIDv4 ile karşılaştırıldığında veritabanı locality’sindeki bozulma sorununu azaltmayı amaçlar
- base32 kodlaması URL güvenliği, büyük/küçük harfe duyarsızlık, belirsiz karakterlerden kaçınma, çift tıklamayla seçilebilme ve UUID’nin geleneksel hex kodlamasına göre daha kısa uzunluk özelliklerine sahiptir
- Resmî implementasyonlar Go, SQL, TypeScript için sunulur; en güncel spec sürümü v0.3.0’dır
- Komut satırı aracıyla TypeID oluşturma, mevcut TypeID’nin UUID’sini decode etme ve UUID’yi TypeID’ye encode etme işlemleri yapılabilir
typeid new prefixile yeni TypeID oluşturmatypeid decode prefix_01h2xcejqtf2nbrexx3vqjhp41ile type ve uuid çıktılamatypeid encode prefix 0188bac7-4afa-78aa-bc3b-bd1eef28d881ile TypeID oluşturma
- Jetify’nin TypeID Converter aracında TypeID dizeleri UUID’ye dönüştürülebilir veya
prefix:UUIDformatında TypeID dönüşümü yapılabilir
1 yorum
Hacker News yorumları
Birkaç öneri var: prefix dizgesi çok geç olmadan sabitlenip belgelenirse iyi olur
Go uygulamasına bakınca küçük harf ASCII olduğu için sorun yok gibi görünüyor ama
article-commentgibi bileşik tiplerde belirsizlik varKarmaşık projeler veya ORM'ler için ayırıcıdan kaçınmak zor olacağından, tek bir ayırıcıya izin vermeyi değerlendirmeye değer
Go uygulamasında test yok ama birim testi yazması kolay bir kod olduğu için mutlaka eklenmeli
Go'da, Google'ın UUID uygulamasında olduğu gibi düzgün bir ayrıştırma fonksiyonu ve dahili byte dizisi kullanmak daha doğru; dizgeyi ise yalnızca render etme ve prefix için kullanmak daha iyi
Şu anki ayrıştırma fazla esnek ve suffix boşsa oluşturma moduna giriyor gibi görünüyor; ayrıca
SplitNsonrası indeksleme, alt çizgi yoksa panic'e yol açacak gibiTasarımın kendisinde kusur bulmaya çalıştım ama sonuçta trade-off'un sweet spot'unu iyi yakalamış gibi görünüyor
Yine de eleştiri yerinde; dil uygulamaları arttıkça birbirleriyle uyumlu olduklarını doğrulamak gerekeceği için daha sıkı bir test paketi eklemeyi planlıyoruz
Prefix konusunda ise, izin verilen karakter kümesini spesifikasyonda tanımlamamış olmamızın mı endişe yarattığını merak ediyorum
Ayrıca spesifikasyonda prefix'i de netleştirdim
Prefix, kullanım alanına göre belirlenir
Bu yaklaşımı birkaç yıldır kullanıyoruz ama iki fark var
Birincisi, insanların gerçekten bu değerleri elle girdiğini varsaymadığımız için
lile1karışıklığını çok önemsemiyoruzBunun yerine, kelimelerin, özellikle küfürlerin tesadüfen oluşma olasılığını azaltmak için
eiousesli harflerini çıkaran bir base32 kullanıyoruzİkincisi, salt eklenmiş iki base32 karakterini checksum olarak ekliyoruz
Böylece değer hatalı ya da kötü niyetli olduğunda veri deposuna sorgu atmadan bunu anlayabiliyoruz; diğer uygulamaların bunu neden yapmadığını bilmiyorum
analritaçıkmış ve bu yüzden şikayet almıştıkHariç tutulan karakterlere
aharfini eklemeyi de düşünebilirsinizİnsanların bunu elle girmediğini varsayıyorsak, yanlış bir değerin kazara girilmesine yol açan kanalın ne olduğu soru işareti
Kopyala/yapıştır hataları ya da sayfanın bunu büyük harfle render etmesi olabilir ama base32 kullanılıyorsa büyük/küçük harf normalize edilebilir
Ayrıca 2 byte'lık, kriptografik olarak güvenli olmayan bir checksum'ın kötü niyetli durumlarda nasıl yardımcı olacağını da merak ediyorum
Bunu TypeID spesifikasyonuna dahil etmenin doğru olup olmayacağını değerlendiriyorum
Veritabanı sorgularını ne kadar azalttığını bilmiyorum ama decode sırasında daha muğlak hatalara düşmemesi hoşuma gidiyor
Metinden ayrı olarak “Crockford's alphabet” bağlantısı da var: https://www.crockford.com/base32.html
Bu base32 sistemi, alfanümerik karakterler arasında
1ile karıştırılanI,L,0ile karıştırılanOve ayrıcaUharfini hariç tutuyor.Sayfada
Uharfinin çıkarılma nedeni olarak “kazara müstehcenlik” deniyor ama bunun tam olarak ne anlama geldiğini bilmiyorum.Karıştırılmayacak alfanümerik karakterlerle düzgün bir base32 alfabesi yapmak için aslında sadece
O,I,Lharflerini çıkarmak yeterli.Yine de 33 karakter kalıyor, yani bir tanesini daha çıkarmak gerekiyor; hangisini çıkarırsanız çıkarın fark etmediği için, son çıkarılan karaktere rastgele bir gerekçe uydurmuş denebilir.
Kullanıcının göreceği bir ID söz konusuysa tamamen saçma bir gerekçe de sayılmaz ama elbette kusursuz bir önleme sağladığı da söylenemez.
IveOzaten çıkarılmışkenUda çıkarılırsa, kaba görünen üç harfli ya da dört harfli kombinasyonların epey çoğu ortadan kalkabiliyor.Tabii
Ahâlâ olduğu için mümkün kalan kombinasyonlar var ama olasılık oldukça düşük.U, Cicero döneminde henüz var olmadığı için müstehcenliğe yakın derecede bayağı bir harftir.Şirkette QR kodlar için birkaç yeni “taban” tanımlamıştık; bence bu, bilgisayar bilimlerinde nispeten az değerlendirilen bir alan.
URL slug sorununu çözmeye çalışıyorduk ve uzunluklar oldukça fazla olduğu için bayt başına 5 bitin elle yazma zahmetini azaltacağını düşünmüştük.
Sonuçta birkaç harfi daha çıkarma payımız vardı ve herkes işten çıktıktan sonra oturup küfürlerin ne kadar saldırgan olduğuna göre sıralama yaparak hangi harfleri eleyeceğimize karar vermiştik.
Daha sonra aşağılayıcı ifadelerin daha büyük sorun olduğuna ikna edip
uyerinenharfini çıkarmaya odaklanmıştım.tggrsadece sevimli duruyor aman**r, çeşitli HR ekipleriyle rahatsız edici görüşmeler yapma sorununa yol açıyordu.Son karakter kümesi artık biraz bulanık hatırlanıyor ama genelde
[a-z][0-9]ile uğraşıyorduk ve URL’de kullanılamayan ya da sözlü olarak aktarması zor pek çok sembol vardı.Hatırladığım kadarıyla
0,l,1karakterlerini tamamen çıkarmıştık ve elle yazmanın ya tamamen büyük harfle ya da tamamen küçük harfle yapılacağını varsaymıştık.0osorun değildi,1Lde öyle.Crockford kodlamasını pek sevmiyorum.
Gerçekten de bu şekilde kodlanmış değerlerle teknik destek ya da analiz yaparken bunun bariz bir hata olduğunu hissettim.
Bu alfabe, telefonda tanımlayıcı okuma gibi pratikte nadir olan bir hedef için tasarlanmış ve belirsizlik ekliyor.
Kodlanmış dizenin kendisiyle loglarda
grepyapmak ya da çapraz referans vermek gerektiğinde tam bir felaket; ayrıca tirelere de izin verdiği için kopyala-yapıştır ve satır kayması hatalarının başlıca sebeplerinden biri oluyor.Nesne tanımlayıcılarını doğrudan elle yazmak nadirdir ama uygulamalar, sohbetler ve forumlar arasında kopyala-yapıştır yapmak, e-postayla iletmek ve log dosyalarında aramak çok yaygındır.
Bu koşullarda telaffuz edilebilirlik anlamsızdır, büyük/küçük harf duyarsızlığı ise engel çıkarır; gerekli olan şey tutarlılık ile yapıştırma ve satır kaymasına dayanıklılıktır.
base58, bu gereksinimlere daha iyi uyan birebir bir kodlamadır ve hatta daha kısadır.
Stripe’tan ilham alarak birkaç yıldır
user_1BzGURpnHGn6oNru84B3Rigibi tip prefix’i eklenmiş base58 kodlu UUID’leri nesne tanımlayıcısı olarak kullanıyorum.Yine de Douglas Crockford’un base32 kodlamasını 20 yıl önce, kullanım ortamının oldukça farklı olduğu bir dönemde tasarladığını hesaba katmak gerekir.
Ama büyük/küçük harf ayrımını varsaymak istemediğim için sonunda base32 tarafına yöneldim.
Örneğin bir ID’yi dosya adı olarak kullanmak isteyebilirsiniz ve büyük/küçük harf duyarsız bir dosya sistemiyle sınırlı bir ortamda olabilirsiniz.
TypeID, Crockford alfabesini her zaman küçük harfle kullanıyor; Crockford kodlama kurallarının tamamını kullanmıyor.
TypeID’de tireye izin verilmiyor ve belirsiz karakterleri farklı yazarak aynı ID’nin birden fazla biçimde kodlanmasına da izin verilmiyor.
Ama bazen ekran görüntüsünde ya da ekran paylaşımında görünen bir tanımlayıcıyı, ya da tanımlayıcıyı kolayca kopyalayamadığınız başka bir cihazdaki bir tanımlayıcıyı doğrudan girmek gerekebiliyor.
Crockford base32 de çalışıyor ama beni pek memnun etmiyor.
Kişisel olarak birinci tercihim, tip prefix’i eklenmiş KSUID kullanmak olurdu.
Böylece base62 kodlamalı, 160 bitlik, K-sıralanabilir bir ID elde edersiniz; uyumluluk nedeniyle mutlaka 128 bitlik ID gerekmedikçe oldukça uygun.
En sevdiğim base32 kodlaması z-base-32; göze daha rahat geliyor: https://philzimmermann.com/docs/human-oriented-base-32-encod...
base58’in en büyük sorunu, tam sayılar için çok uygun olsa da kripto anahtarları gibi rastgele ikili veriler için daha az uygun olması ve büyük/küçük harf duyarlı dizelerin hoş görünmemesidir.
Temiz görünüyor
“Type-safe” prefix hoşuma gitti
Bizim ORM’de benzer şekilde DB içindeki tamsayı ID’lere varlık bazında etiketleri otomatik ekleyen yönteme tagged ids diyorduk: https://joist-orm.io/docs/advanced/tagged-ids
Ayırıcı olarak
:kullanıyorduk ama çift tıklamayla kopyala/yapıştır açısından_kullanmamış olmaktan biraz pişmanımTeoride Joist’in “DB’deki UUID sütununu” “alan modelindeki TypeID”ye dönüştürmesini sağlamak da oldukça kolay olurdu, ama şu an yalnızca kullanıcı tarafı yapılandırmayla olacak gibi görünmüyor
Yine de iyi fikir
Öğe ID’leri
t3_15bfi0gibi oluyor vet3_tip prefix’it3gönderi,t1yorum,t5subreddit; geri kalanı ise otomatik artan birincil anahtarın base36 kodlaması.) geçtikİki nokta üst üste URL encoding’de
%ile encode edildiği için ID uzunluğu artıyor, URL’ler çirkin ve uzun oluyor, bu da çok sinir bozucuyduUUIDv7 birkaç yıldır HN’de popüler; ne zaman düzgün bir standart olacağını ve kütüphaneler, veritabanları ve ekosistemin geri kalanının bunu ne zaman native destekleyeceğini merak ediyorum
Çoğu yazılım UUID içindeki belirli bitlerle ilgilenmediği için bugün de kullanılabilir
Belirli bitleri önemseyen bir yazılım varsa UUIDv4 gibi taklit edilebilir ve o bitler de rastgele üretilebilir
Bir üretim prosedürü gerekiyorsa kendiniz yazabilirsiniz, zor değil
Örneğin Dart UUID kütüphanesini ben yönetiyorum; en son beta major sürümde v6, v7 ve özel v8 var
Bir yerde implementasyonların listesi de var ve ben de o sayfada kütüphane yöneticisi olarak yer aldığım için her yeni taslak çıktığında yazarlardan bildirim alıyorum
“Çift tıklamayla kopyala/yapıştır yapılabiliyor” kısmı hoşuma gitti
Detaylar önemlidir
UUID’den biraz şikayetçiyim
UUID versioning gibi ritüellerle uğraşmak yerine neden sadece zaman + rastgelelik birleştirilmiyor anlamıyorum
Yerellik önemli değilse doğrudan 128 bit rastgele sayı kullanılabilir
Deneyimime göre çoğu insan UUID’lerin insan tarafından okunabilir hex gösterimiyle, hatta tireleriyle birlikte saklanması gerektiğini düşünüyor
Veritabanında, ağda ve bellekte çok fazla yer israf ediyor
Örneğin ULID https://github.com/ulid/spec daha kısa ve zamanı sakladığı için sözlüksel olarak sıralanabiliyor
Gerçekte bu da diğer mühendislik problemleri gibi; gereksinimleri yazarsınız ve neyin çözdüğüne bakarsınız
Farklı biçimlerin avantajlarını okumak faydalı, çünkü başkalarının zor yoldan öğrendiklerinden yararlanmanızı sağlıyor
Sıralanabilirlik ve zaman tabanlı yerellik akla doğal olarak gelmeyebilir ama gerekiyorsa, bunu proje başladıktan 4 yıl sonra verileri çöpe attığınızı fark etmektense önceden bilmek çok daha iyi
Bazı UUID biçimleri kendi küçük güvenlik sorunlarını da beraberinde getirdi; örneğin UUID v1’deki MAC adresi sızıntısından kaçınmak iyi olur
Mevcut çözüm kullanım durumunuza uyuyorsa doğrudan kullanın
Uymuyorsa da çok dert etmeye gerek yok
Ben şahsen UUID’yi en çok kullandığım yerde “bana sadece benzersiz bir string gönderin, içiniz rahat edecekse istediğiniz UUID kütüphanesini kullanın” diye belirttim
Bu sistemde her veri kaynağının kendine özgü bir biçimi var gibi görünüyor ama sorun değil
Ölçek de günde on binler seviyesinde olduğu için bir problem yok ve UUID’ye göre sıralama gerekmiyor
Bu, gerçek bir tanımlayıcıdan çok göndericinin her mesaj için ürettiği benzersiz bir token; birden fazla kuyruğu olan heterojen bir sistemde aşağı akışta yinelenen teslimatları tespit etmek için kullanılıyor
Küresel benzersizlik de gerekmiyor; yalnızca küçük bir shard içinde benzersiz olması yeterli, hatta prensipte zaman sonra tekrar etmesi de sorun değil
Ama sadelik için sistemin tüm süre boyunca benzersiz kalmasını sağlıyoruz
Kendim üretmem gerektiğinde
/dev/urandomdan veri okuyup base64 olarak encode ediyorum ve==ile bitmeyen bir boyut seçtimBu da gerçek bir problem yüzünden değil, sadece görünüş açısından
Yine de tirelerin neden gerekli olduğunu hâlâ anlamıyorum ve diğer sürümlerin de pek anlamlı olmadığını düşünüyorum
UUIDv7 ya da UUID şart değilse, https://github.com/segmentio/ksuid çok daha geniş bir anahtar alanı sunuyor
Namespace gerekiyorsa başına bir string prefix ekleyebilirsiniz ve KSUID çakışma olasılığı herhangi bir UUID sürümüne göre çok daha düşüktür
Sıralanabilir timestamp’e sahip genel amaçlı ID üreticileri arasında ksuid en iyisiydi ve çoğu dil için kütüphanesi var
UUID v1~v7 israfa kaçıyor
Ek bitleri kaldırabiliyorsanız, ki çoğu durumda kaldırabilirsiniz, KSUID harika bir formattır
[1] https://github.com/sophiabits/resource-id
Ama ksuid’nin büyük sorunu 160 bit olması; bu da PostgreSQL gibi veritabanlarının yerel UUID tipiyle uyumlu olmadığı için bir performans maliyeti doğuruyor
1: https://github.com/svix/rust-ksuid
2: https://github.com/svix/python-ksuid
K-sıralanabilirlik harika bir kavram ve gevşek sıralı anahtarlar birçok kullanım senaryosunu çözüyor
Tip içeren sıkıştırılmış string gösterimini de beğeniyorum
Ancak UUID v7’nin istenmeyen bir yan etkisi olarak çok sayıda güvenlik sorunu doğurmasından endişeliyim
İnsanlar UUID’yi token olarak kullanmamalı ve veritabanının birincil anahtarı olarak da böyle kullanmamalı, ama pratikte bunu yapıyorlar
UUID v4 fiilen kriptografik rastgele sayı olduğundan, şimdiye kadarki birçok güvenlik açığının tesadüfen atlatıldığını düşünüyorum
UUID v7’de gerçek rastgele veri miktarının 32 bite düştüğünü sanmıştım ve geliştiricilere UUID’lerin tahmin edilebilir olabileceğini iyi anlatmak gerektiğini düşünmüştüm
Düzeltme: v7 UUID’lerinin tahmin edilebilirliği konusunda yanılmış gibi görünüyorum
Taslak, rastgele bitler için CSPRNG öneriyor, entropinin en az 74 bit olduğunu ve “tahmin edilemez” olacak şekilde tasarlandığını söylüyor
Güvenlikle ilgili kullanım alanları için “UUID v4” öneriliyor, ama bu muhtemelen timestamp’le ilgili anlamından dolayı olabilir
İsimler UUIDv4 ve UUIDv7 olunca, hangisinin veritabanı için daha iyi, hangisinin tek kullanımlık token için daha iyi olduğunu sürekli hatırlamayı gerektiren bitmeyen bir kafa karışıklığı olmaz mı diye düşünüyorum
Geriye dönük uyumlu bir çözüm de pek göremiyorum
Elixir’de v4 UUID oluştururken
UUID.uuid4()fonksiyonu kullanılıyorTeorik olarak koddaki kullanım yerleri taranabilir, ama bunların hepsi hata olasılığını artırıyor
v3 gibi hash tabanlı olanlarda da aynı durum vardı
v4 basit ve yanlış kullanım ihtimali daha düşük