Eşzamanlılığa dayanıklı fatura numaralandırma: tekrarları ve boşlukları önleme
Birden çok kullanıcının aynı anda fatura veya bilet oluşturduğu durumlarda tekrarları ve beklenmedik boşlukları önlemek için eşzamanlılığa dayanıklı pratik numaralandırma desenlerini öğrenin.

İki kişi aynı anda kayıt oluştururken neler ters gider
Yoğun bir ofiste, saat 16:55 civarı olduğunu hayal edin. İki kişi bir faturayı tamamlayıp birbirine bir saniyeden kısa sürede Kaydet’e basıyor. Her iki ekranda da kısa süreliğine “Fatura #1042” görünüyor. Bir kayıt başarılı oluyor, diğeri hata veriyor veya daha kötüsü, her ikisi de aynı numarayla kaydediliyor. Gerçekte en sık görülen semptom budur: yük altındayken ortaya çıkan tekrar eden numaralar.
Biletlerde durum benzer. İki temsilci aynı müşteri için aynı anda yeni bir bilet oluşturduğunda ve sisteminiz “sonraki numarayı al” adımını son kaydı okuyarak yapıyorsa, her iki istek de aynı “son” değeri okuyup aynı sonraki numarayı seçebilir.
İkinci semptom daha sinsi: atlanan numaralar. #1042’yi gördükten sonra #1044 gelir ve #1043 eksiktir. Bu genellikle bir hata veya yeniden deneme sonrası olur. Bir istek bir numarayı ayırır, sonra kaydetme doğrulama hatası, zaman aşımı veya kullanıcı sekmeyi kapattığı için başarısız olur. Ya da bir arka plan işi ağ kesintisinden sonra yeniden denediğinde yeni bir numara alır, ilk deneme zaten bir numara tüketmiş olsa bile.
Faturalar için bu önemlidir çünkü numaralandırma denetim izinizin bir parçasıdır. Muhasebeciler her faturanın benzersiz olarak tanımlanmasını bekler ve müşteriler ödemelerde veya destek e-postalarında fatura numaralarına referans verebilir. Biletlerde ise numara, konuşmalarda, raporlarda ve dışa aktarmalarda herkesin kullandığı etikettir. Çakışan numaralar karışıklık yaratır. Eksik numaralar, hiçbir usulsüzlük olmasa bile incelemeler sırasında soru doğurabilir.
Burada erken belirlemeniz gereken ana beklenti şudur: her numaralandırma yöntemi hem eşzamanlılığa dayanıklı hem de boşluksuz olamaz. Eşzamanlılığa dayanıklı fatura numaralandırma (çok kullanıcı olduğunda bile tekrar olmaması) ulaşılabilir ve taviz verilemez olmalıdır. Boşluksuz numaralandırma mümkün olsa da ekstra kurallar gerektirir ve genellikle taslaklar, hatalar ve iptallerin nasıl ele alınacağını değiştirir.
Sorunu çerçevelemenin iyi bir yolu, numaralarınızın hangi garantileri vermesi gerektiğini sormaktır:
- Asla tekrarlanmamalı (her zaman benzersiz)
- Çoğunlukla artan olmalı (iyi olur)
- Asla atlamamalı (sadece ona göre tasarlarsanız)
Kuralı seçtiğinizde teknik çözüm seçmek çok daha kolay olur.
Neden çakışmalar ve boşluklar olur
Çoğu uygulama basit bir desen izler: kullanıcı Kaydet’e basar, uygulama sonraki fatura veya bilet numarasını ister, sonra yeni kaydı o numarayla ekler. Tek kullanıcıyken bu gayet güvenli görünür çünkü sorunsuz çalışır.
Sorun, iki kaydetme neredeyse aynı anda gerçekleştiğinde başlar. Her iki istek de ekleme bitmeden önce “sonraki numarayı al” adımına ulaşabilir. Her iki okuma aynı “sonraki” değeri görürse, her ikisi de aynı numarayı yazmaya çalışır. Bu bir yarış durumudur: sonuç mantıktan çok zamanlamaya bağlıdır.
Tipik zaman çizelgesi şöyle görünür:
- İstek A sonraki numarayı okur: 1042
- İstek B sonraki numarayı okur: 1042
- İstek A fatura 1042'yi ekler
- İstek B fatura 1042'yi ekler (veya unique kuralı engellerse hata alır)
Çakışmalar, veritabanında ikinci eklemeyi durduracak bir şey olmadığında olur. Sadece uygulama kodunda “bu numara alınmış mı?” kontrolü yaparsanız, kontrol ile ekleme arasındaki yarışta yine kaybedebilirsiniz.
Boşluklar farklı bir problemdir. Sisteminiz bir numarayı “ayırdığında” ama kayıt gerçek, taahhüt edilmiş bir fatura veya bilet haline gelmediğinde boşluklar ortaya çıkar. Yaygın nedenler başarısız ödemeler, geç bulunan doğrulama hataları, zaman aşımı veya numara atandıktan sonra kullanıcının sekmeyi kapatmasıdır. Ekleme başarısız olsa ve hiçbir şey kaydedilmemiş olsa bile numara zaten tüketilmiş olabilir.
Gizli eşzamanlılık bunu daha da kötüleştirir çünkü nadiren sadece “iki insanın Kaydet’e basması” değildir. Ayrıca şunlar olabilir:
- Paralel olarak kayıt oluşturan API istemcileri
- Parti halinde çalışan içe aktarmalar
- Gece boyunca fatura oluşturan arka plan işleri
- Dalgalı bağlantıya sahip mobil uygulamalardan gelen yeniden denemeler
Dolayısıyla temel nedenler: (1) birden çok isteğin aynı sayaç değerini okuduğu zamanlama çatışmaları ve (2) numaraların işlemin başarılı olacağından emin olmadan önce ayrılması. Eşzamanlılığa dayanıklı fatura numaralandırma planı, hangi sonucu tolere edebileceğinize karar vermelidir: tekrar yok mu, boşluk yok mu, yoksa her ikisi mi — ve hangi olaylar altında (taslaklar, yeniden denemeler, iptaller).
Bir çözüm seçmeden önce numaralandırma kuralınızı belirleyin
Eşzamanlılığa dayanıklı fatura numaralandırmasına başlamadan önce numaranın işinizde ne anlama gelmesi gerektiğini yazın. En yaygın hata önce teknik bir yöntem seçmek, sonra muhasebe veya yasal kuralların farklı bir şey beklediğini fark etmektir.
İki sıklıkla karıştırılan hedefi ayırarak başlayın:
- Benzersiz: hiçbir iki fatura veya bilet aynı numarayı asla paylaşmaz.
- Boşluksuz: numaralar benzersizdir ve aynı zamanda sıralı ardışık (eksik numara yok).
Birçok gerçek sistem yalnızca benzersizliği hedefler ve boşlukları kabul eder. Boşluklar normal nedenlerle olabilir: kullanıcı bir taslağı açıp bırakır, bir ödeme numara ayrıldıktan sonra başarısız olur veya bir kayıt oluşturulup sonra iptal edilir. Yardım masası biletleri için boşluklar genellikle hiç önemli değildir. Hatta faturalar için bile birçok ekip boşlukları, denetim izinde açıklanabilecek şekilde kabul eder (iptal edildi, iptal edildi, test vb.). Boşluksuz numaralandırma mümkün ama ekstra kurallar gerektirir ve genellikle sürtünme ekler.
Sonra sayacın kapsamını belirleyin. Küçük ifade farkları tasarımı çok değiştirir:
- Her şey için tek bir global sıra mı, yoksa her şirket/kiracı için ayrı sıralar mı?
- Her yıl sıfırlansın mı (2026-000123) yoksa hiç sıfırlanmasın mı?
- Faturalar, kredi notları ve biletler için farklı seriler mi?
- İnsan dostu bir format (ön ekler, ayırıcılar) mı gerekli yoksa sadece dahili bir sayı mı?
Somut bir örnek: birden fazla müşteri şirketi olan bir SaaS ürünü, fatura numaralarının her şirket bazında benzersiz olmasını ve her takvim yılı için sıfırlanmasını isteyebilir; biletler ise global olarak benzersiz ve hiç sıfırlanmayan. Bunlar aynı kullanıcı arayüzüne benziyor olsa bile farklı kurallara sahip iki ayrı sayacın kullanımını gerektirir.
Gerçekten boşluksuz olmanız gerekiyorsa, bir numara atandıktan sonra hangi olaylara izin verileceğini açıkça belirtin. Örneğin, bir fatura silinebilir mi yoksa sadece iptal mi edilebilir? Kullanıcılar taslakları numarasız kaydedebilir mi ve numara sadece nihai onayda mı atanır? Bu seçimler genellikle veritabanı tekniğinden daha önemlidir.
Uygulamaya başlamadan önce kuralı kısa bir şartname halinde yazın:
- Hangi kayıt türleri sırayı kullanır?
- Bir numarayı “kullanılmış” yapan nedir (taslak, gönderildi, ödendi)?
- Kapsam nedir (global, şirket başına, yıl başına, seri başına)?
- İptaller ve düzeltmeler nasıl ele alınır?
AppMaster içinde bu tür kural verinizin ve iş süreci akışınızın yanında yer almalıdır, böylece ekip aynı davranışı API, web UI ve mobilde sürpriz yaşamadan uygular.
Yaygın yaklaşımlar ve her birinin sağladığı garantiler
İnsanlar “fatura numaralandırması” derken genellikle iki farklı hedefi karıştırırlar: (1) aynı numarayı iki kez üretmemek ve (2) boşluk olmaması. Çoğu sistem ilkini kolaylıkla sağlayabilir. İkincisi çok daha zordur çünkü boşluklar herhangi bir zamanda bir işlem başarısız olduğunda, bir taslak terk edildiğinde veya bir kayıt iptal edildiğinde ortaya çıkabilir.
Yaklaşım 1: Veritabanı sequence'i (hızlı benzersizlik)
PostgreSQL sequence, yük altında benzersiz ve artan numaralar almak için en basit yoldur. Veritabanı, aynı anda birçok kullanıcı olsa bile sequence değerlerini hızlıca vermek üzere tasarlanmıştır.
Ne elde edersiniz: benzersizlik ve (çoğunlukla) artan sıra. Ne elde etmezsiniz: boşluksuzluk. Bir ekleme sequence değeri atandıktan sonra başarısız olursa, o numara “yanmış” olur ve bir boşluk görülür.
Yaklaşım 2: Unique kısıtı + yeniden deneme (veritabanının karar vermesine izin verin)
Burada uygulamanız bir aday numara üretir, kaydeder ve UNIQUE kısıtının çakışmaları reddetmesine güvenirsiniz. Çakışma olursa yeni numarayla yeniden denersiniz.
Bu işe yarayabilir, ama yüksek eşzamanlılık altında gürültülü hale gelmeye eğilimlidir. Daha fazla yeniden deneme, daha fazla başarısız işlem ve hata ayıklaması zor zirveler yaşayabilirsiniz. Ayrıca boşluksuzluğu garanti etmez; bunun için sıkı rezervasyon kurallarıyla birleştirilmesi gerekir ve bu da karmaşıklığı artırır.
Yaklaşım 3: Kilitlenen sayaç satırı (boşluksuzluğu hedefleyin)
Gerçekten boşluksuz numaralara ihtiyacınız varsa, genellikle ayrılmış bir sayaç tablosu (her kapsam için bir satır) kullanılır. O satırı bir işlem içinde kilitlersiniz, arttırırsınız ve yeni değeri kullanırsınız.
Bu normal veritabanı tasarımında boşluksuza en yakın yaklaşımdır, ancak bir maliyeti vardır: tüm yazıcıların beklemesi gereken tek bir “sıcak nokta” oluşturur. Ayrıca uzun işlemler, zaman aşımı ve deadlock gibi operasyonel hatalar için risk artar.
Yaklaşım 4: Ayrı rezervasyon servisi (sadece özel durumlar için)
Bağımsız bir “numaralandırma servisi” birden fazla uygulama veya veritabanı arasında kuralları merkezileştirebilir. Birkaç sistem numara veriyorsa ve yazma işlemlerini konsolide edemiyorsanız bunun değeri olabilir.
Takas, operasyonel risktir: başka bir servisi doğru, yüksek erişilebilir ve tutarlı hale getirmiş olursunuz.
Kısaca garantiler şöyle düşünebilirsiniz:
- Sequence: benzersiz, hızlı, boşluklara izin verir
- Unique + yeniden deneme: benzersiz, düşük yükte basit, yüksek yükte çalkantılı olabilir
- Kilitli sayaç satırı: boşluksuz olabilir, yüksek eşzamanlılıkta daha yavaş
- Ayrı servis: sistemler arası esneklik, en yüksek karmaşıklık ve hata türleri
AppMaster gibi bir no-code araçta da aynı seçimler geçerlidir: doğruluk veritabanında yaşar. Uygulama mantığı yeniden denemeler ve net hata mesajlarıyla yardımcı olabilir, ama nihai garanti constraintler ve işlemlerden gelmelidir.
Adım adım: sequence ve unique kısıtlarla çakışmaları önleme
Asıl hedefiniz çakışmaları önlemekse (boşluksuzluk garanti etmeye değil), en basit güvenilir desen şu: veritabanının dahili bir kimlik üretmesine izin verin ve müşteri tarafı gösterilen numarada benzersizliği zorunlu kılın.
İki kavramı ayırarak başlayın. Birincil anahtar (joins, düzenlemeler, dışa aktarmalar için) için veritabanı tarafından üretilen bir değer (identity/sequence) kullanın. invoice_no veya ticket_no gibi görünen numarayı ayrı bir sütun olarak tutun.
PostgreSQL için pratik bir kurulum
Aşağıda “sonraki numara” mantığını veritabanı içinde tutan ve eşzamanlılığı doğru yöneten yaygın bir PostgreSQL yaklaşımı var.
-- Internal, never-shown primary key
create table invoices (
id bigint generated always as identity primary key,
invoice_no text not null,
created_at timestamptz not null default now()
);
-- Business-facing uniqueness guarantee
create unique index invoices_invoice_no_uniq on invoices (invoice_no);
-- Sequence for the visible number
create sequence invoice_no_seq;
Görünen numarayı INSERT sırasında oluşturun ("select max(invoice_no) + 1" yapmayın). Bir desen, INSERT içinde sequence değerini formatlamaktır:
insert into invoices (invoice_no)
values (
'INV-' || lpad(nextval('invoice_no_seq')::text, 8, '0')
)
returning id, invoice_no;
50 kullanıcı aynı anda “Fatura oluştur” dese bile, her insert farklı sequence değeri alır ve unique indeks kazara tekrarları engeller.
Çakışma olduğunda ne yapmalı
Düz sequence ile çakışmalar nadirdir. Genellikle sorun, “yıl başında sıfırla”, “kiracı başına” veya kullanıcı tarafından düzenlenebilir numaralar gibi ek kurallar eklediğinizde çıkar. Bu yüzden unique kısıt hala önemlidir.
Uygulama düzeyinde, invoice_no üzerinde unique ihlali aldığınızda küçük bir yeniden deneme döngüsü ile başa çıkın:
- Insert yapmayı deneyin
- invoice_no üzerinde unique hatası alırsanız yeniden deneyin
- Birkaç denemeden sonra durun ve net bir hata gösterin
Bu, yalnızca olağandışı durumlarda tetiklenen yeniden denemeler olduğu için genelde yeterlidir.
Yarış penceresini küçük tutun
Numarayı UI'da hesaplamayın ve numarayı ayırmak için önce okumaya sonra eklemeye dayalı bir yol kullanmayın. Numara üretimini veritabanı yazmasına mümkün olduğunca yakın yapın.
AppMaster ile PostgreSQL kullanıyorsanız, id'yi Data Designer'da identity primary key olarak modelleyin, invoice_no için unique kısıt ekleyin ve invoice_no'yu create akışı sırasında oluşturun ki bu ekleme ile birlikte olsun. Böylece veritabanı gerçek tek kaynak olur ve eşzamanlılık PostgreSQL'in en güçlü olduğu yerde kalır.
Adım adım: satır kilitleme ile boşluksuz sayaç oluşturma
Gerçekten boşluksuz numaralara ihtiyacınız varsa, işlemsel sayaç tablosu ve satır kilitleme kullanabilirsiniz. Fikir basittir: belirli bir kapsam için aynı anda yalnızca bir işlem sonraki numarayı alabilir, bu yüzden numaralar sırayla verilir.
İlk önce kapsamınızı kararlaştırın. Birçok ekip şirket başına, yıl başına veya seri başına ayrı sıralar ister. Sayaç tablosu her kapsam için son kullanılan numarayı saklar.
PostgreSQL satır kilitleme kullanarak pratik bir desen:
number_countersgibi bir tablo oluşturun;company_id,year,series,last_numbersütunları ve(company_id, year, series)üzerinde unique anahtar olsun.- Bir veritabanı işlemi başlatın.
- Kapsam için sayaç satırını
SELECT last_number FROM number_counters WHERE ... FOR UPDATEile kilitleyin. next_number = last_number + 1hesaplayın, sayaç satırınılast_number = next_numberile güncelleyin.- Invoice veya ticket satırını
next_numberile ekleyin ve commit edin.
Anahtar FOR UPDATE. Yük altında çakışma olmaz. Ayrıca iki kullanıcı aynı numarayı almaz çünkü ikinci işlem ilk işlem commit (veya rollback) edene kadar sayacı okuyup artıramaz. İkinci kullanıcının isteği kısa bir süre bekler. Bu bekleme boşluksuz olmanın maliyetidir.
Yeni bir kapsam başlatma
Yeni bir kapsam (yeni şirket, yeni yıl, yeni seri) için bir planınız olmalı. İki yaygın seçenek:
- Sayaç satırlarını önceden oluşturun (örneğin, gelecek yılın satırlarını Aralık’ta oluşturun).
- Talep üzerine oluşturun: sayaç satırını
last_number = 0ile eklemeyi deneyin; zaten varsa normal kilitleme ve arttırma akışına dönün.
No-code bir araçta bu “kilitle, arttır, ekle” dizisini tek bir işlem içinde tutun, böylece ya hepsi olur ya hiçbiri.
Kenar durumlar: taslaklar, başarısız kayıtlar, iptaller ve düzenlemeler
Çoğu numaralandırma hatası dağınık kısımlarda ortaya çıkar: asla yayımlanmayan taslaklar, başarısız kayıtlar, iptal edilen faturalar ve birisi numarayı gördükten sonra yapılan düzenlemeler. Eşzamanlılığa dayanıklı fatura numaralandırması istiyorsanız, numaranın ne zaman “gerçek” sayılacağına dair net bir kuralınız olmalı.
En büyük karar zamanlamadır. Birine “Yeni fatura” anında bir numara atarsanız, terk edilen taslaklardan boşluklar alırsınız. Sadece fatura nihai hale getirildiğinde (post, issue, send veya işinizdeki “final” anlamına gelen neyse) numara atarsanız, numaralar daha sıkı ve açıklaması kolay olur.
Başarısız kayıtlar ve rollbackler beklentilerin veritabanı davranışıyla çakıştığı yerdir. Tipik bir sequence ile bir numara alındı mı alınır — işlem daha sonra başarısız olsa bile. Bu normal ve güvenlidir ama boşluk yaratabilir. Politikamız boşluksuz olmayı gerektiriyorsa, numara yalnızca son adımda ve işlem commit olduğunda atanmalıdır. Bu genellikle tek bir sayaç satırını kilitleyip son numarayı yazmayı ve commit etmeyi gerektirir. Bir adım başarısız olursa hiçbir şey atanmaz.
İptal ve geçersiz kılmalar numarayı asla yeniden kullanmamalıdır. Numara korunmalı, sadece durum ve sebep değiştirilmelidir. Denetçiler ve müşteriler geçmişin tutarlı kalmasını bekler.
Düzenlemeler daha basittir: bir numara sistem dışına görünür hale geldikten sonra onu kalıcı olarak kabul edin. Paylaşılan, dışa aktarılan veya yazdırılan bir faturayı yeniden numaralandırmayın. Düzeltme gerekiyorsa yeni bir belge oluşturun ve eskisine referans verin (örneğin kredi notu veya yedek belge), geçmişi yeniden yazmayın.
Birçok ekip tarafından benimsenen pratik kurallar:
- Taslaklar nihai numara almaz (dahili ID veya “DRAFT” kullanın).
- Numarayı sadece “Post/Issue” sırasında, durum değişikliğiyle aynı işlem içinde atayın.
- İptal ve void açıkça numarayı korur fakat farklı bir durum ve sebep belirtir.
- Yazdırılan/emaillenmiş numaralar değişmez.
- İçe aktarmalar orijinal numarayı korur ve sayacı maksimum değerden sonra başlatır.
Geçişler ve içe aktarmalar özel özen ister. Başka bir sistemden taşıyorsanız mevcut fatura numaralarını olduğu gibi alın, sonra sayacınızı maksimum içe aktarılan değerin sonrasından başlatın. Farklı formatlarla ne yapılacağına karar verin (örneğin yıl başına farklı ön ekler). Genellikle “görünen numarayı” tam olarak saklamak ve ayrı bir dahili birincil anahtar tutmak daha iyidir.
Örnek: yardım masası hızla bilet oluşturuyor, ama birçok taslak var. Bilet numarasını sadece temsilci “Müşteriye gönder” dediğinde atayın. Bu boş numaraların israfını önler ve görünen sırayı gerçek müşteri iletişimiyle hizalar.
AppMaster gibi bir araçta aynı fikir geçerli: taslakları numarasız kayıtlar olarak tutun, sonra final adımında son numarayı üretin.
Çakışmalara veya beklenmedik boşluklara neden olan yaygın hatalar
Çoğu numaralandırma sorunu basit bir fikrin yanlış uygulanmasından kaynaklanır: numarayı bir gösterim değeri gibi ele almak yerine paylaşılan durum olarak düşünmemek. Birden fazla kişi aynı anda kaydettiğinde sistemin sonraki numarayı belirleyecek tek bir yer ve başarısızlık halinde ne olacağına dair tek bir kuralı olması gerekir.
Klasik hata SELECT MAX(number) + 1 gibi bir yaklaşımı uygulama kodunda kullanmaktır. Tek kullanıcı testlerinde iyi görünür, ama iki istek herhangi biri commit etmeden aynı MAX’i okuyabilir. İkisi de aynı sonraki değeri üretir ve tekrar oluşur. "Kontrol et sonra yeniden dene" eklerseniz bile, yoğun trafik altında ekstra yük ve garip zirveler yaratabilirsiniz.
Başka bir kaynak ise final numarasını istemci tarafında (tarayıcı veya mobil) üretmektir. İstemci diğer kullanıcıların ne yaptığını bilemez ve kaydetme başarısız olursa güvenli bir şekilde numara rezerve edemez. İstemci tarafından üretilen numaralar geçici etiketler için uygun olabilir (“Taslak 12”), ama resmi fatura veya bilet ID'leri için uygun değildir.
Boşluklar, sequence'lerin boşluksuz olduğunu varsayan ekipleri şaşırtır. PostgreSQL’de sequence’ler benzersizlik için tasarlanmıştır, kusursuz süreklilik için değil. İşlem rollback olduğunda, ID önceden alındığında veya veritabanı yeniden başlatıldığında numaralar atlanabilir. Bu normaldir. Eğer gerçek gereksiniminiz “tekrar yok” ise, sequence + unique kısıt genelde doğru cevaptır. Gerçekten “boşluksuz” gerekiyorsa, farklı bir desen (çoğunlukla satır kilitleme) seçmelisiniz ve verimlilikte bazı ödünleri kabul etmelisiniz.
Kilitleme, çok geniş yapıldığında ters tepebilir. Tüm numaralandırma için tek bir global kilit, her oluşturma eylemini sıraya sokar; oysa sayaçları şirket, lokasyon veya belge türüne göre ayırmak mümkün olabilir. Bu tüm sistemi yavaşlatıp kullanıcıların kaydetmenin "rastgele" takıldığı hissine kapılmasına neden olabilir.
Kontrol etmeniz gereken hatalar:
- MAX + 1 kullanmak (veya “son numarayı bulmak”) ve veritabanı seviyesinde unique kısıt olmadan devam etmek.
- Final numaraları istemcide üretmek ve sonra “çatışmaları sonra düzeltmeye çalışmak”.
- PostgreSQL sequence'lerinin boşluksuz olduğunu bekleyip boşlukları hata saymak.
- Her şey için tek bir paylaşılan sayacı kilitlemek yerine sayaçları bölümlememek.
- Tek kullanıcıyla test etmek; yarış durumları ancak üretimde ortaya çıkabilir.
Pratik test ipucu: paralel olarak 100 ila 1.000 kayıt oluşturacak basit bir eşzamanlılık testi çalıştırın ve ardından çoğaltmalar ve beklenmedik boşluklar olup olmadığını kontrol edin. No-code bir araçta da aynı kural geçerlidir: nihai numara sunucu tarafı işlem içinde atanmalı, UI akışında değil.
Yayına almadan önce hızlı kontroller
Fatura veya bilet numaralandırmasını yayına almadan önce gerçek trafikte genellikle başarısız olan bölümler üzerinde hızlı bir kontrol yapın. Amaç basit: her kaydın tam olarak bir iş numarası olsun ve kurallarınız 50 kişi aynı anda "Oluştur" dediyse bile geçerli olsun.
İşte eşzamanlılığa dayanıklı fatura numaralandırması için pratik bir kontrol listesi:
- İş numarası alanının veritabanında unique kısıtı olduğundan emin olun (sadece UI kontrolü değil). Bu, iki istek çarpışırsa son savunma hattınızdır.
- Numaranın kaydı kaydeden aynı veritabanı işlemi içinde atandığından emin olun. Numara atama ve kaydetme istekler arasında bölünmüşse, eninde sonunda çakışmalar görürsünüz.
- Eğer boşluksuz numaralar gerekiyorsa, numarayı sadece kayıt nihai hale geldiğinde atayın (örneğin bir fatura yayınlandığında, taslak oluşturulduğunda değil). Taslaklar, terk edilen formlar ve başarısız ödemeler en yaygın boşluk kaynaklarıdır.
- Nadir çakışmalar için yeniden deneme stratejisi ekleyin. Satır kilitleme ya da sequence kullanırken de zaman zaman serialization hatası, deadlock veya unique ihlali görebilirsiniz. Kısa bir geri çekilme ile basit yeniden deneme genelde yeterlidir.
- UI, public API ve toplu içe aktarmalar dahil olmak üzere 20 ila 100 eşzamanlı oluşturma ile stres testi yapın. Gerçekçi karışımları test edin: ani patlamalar, yavaş ağlar ve çift gönderimler.
Kurulumunuzu doğrulamanın hızlı bir yolu: yoğun bir yardım masası anını simüle edin: iki temsilci "Yeni bilet" formunu açsın, biri web uygulamasından gönderirken bir içe aktarma işi aynı anda e-posta gelen kutusundan biletler eklesin. Koşunun sonunda tüm numaraların benzersiz, doğru formatta olduğunu ve başarısızlıkların yarım kalmış kayıt bırakmadığını kontrol edin.
AppMaster ile bu prensipler aynı şekilde geçerlidir: numara atamasını veritabanı işleminde tutun, PostgreSQL kısıtlarına güvenin ve hem UI hem de API uç noktalarından gelen oluşturma yollarını test edin. Birçok ekip manuel testlerde güven hisseder ama gerçek kullanıcı yoğunluğunda sürprizlerle karşılaşır.
Örnek: yoğun yardım masası biletleri ve sonraki adımlar
Gün boyu ajanların web uygulamasında bilet oluşturduğu, entegrasyonun chat aracı ve e-postadan da bilet oluşturduğu bir destek masası düşünün. Herkes T-2026-000123 gibi bilet numaraları bekliyor ve her numaranın tam olarak bir bilete işaret etmesini bekliyor.
Saf bir yaklaşım: “son bilet numarasını oku”, +1 yap, yeni bileti kaydet. Yük altında iki istek kaydetmeden önce aynı “son numarayı” okuyabilir. İkisi de aynı sonraki numarayı hesaplar ve tekrar oluşur. Hatası düzeltmek için yeniden deneme yaparsanız, genelde anlamlı boşluklar yaratırsınız.
Veritabanı, uygulama kodunuz naive olsa bile tekrarları durdurabilir. ticket_number sütununa unique kısıtı ekleyin. İki istek aynı numarayı denediğinde bir insert hata verir ve temiz bir yeniden deneme yapabilirsiniz. Bu, fatura numaralandırmasının da çekirdeğidir: benzersizliği UI'ya bırakmayın, veritabanına bırakın.
Boşluksuz numaralandırma iş akışını değiştirir. Eğer boşluk istemiyorsanız, final numarasını bilet ilk oluşturulduğunda atayamazsınız (taslak). Bunun yerine bilet status = Draft ve ticket_number = null ile oluşturun. Numarayı sadece finalize edildiğinde atayın, böylece başarısız kayıtlar ve terk edilen taslaklar numara yakmaz.
Basit bir tablo tasarımı şöyle görünür:
- tickets: id, created_at, status (Draft, Open, Closed), ticket_number (nullable), finalized_at
- ticket_counters: key (örnek "tickets_2026"), next_number
AppMaster’da bunu Data Designer ile modelleyip Business Process Editor'de şu mantığı kurabilirsiniz:
- Create Ticket: status=Draft ve ticket_number yok şekilde ticket ekle
- Finalize Ticket: bir işlem başlat, sayaç satırını kilitle, ticket_number ata, next_number'ı arttır, commit et
- Test: aynı anda iki Finalize işlemi çalıştırın ve asla çakışma olmadığını doğrulayın
Sonraki adım: kuralınızı (sadece benzersiz mi yoksa gerçekten boşluksuz mu) seçin. Boşluklara katlanabiliyorsanız, database sequence + unique kısıt genellikle yeterlidir ve akışı basit tutar. Gerçekten boşluksuz olmanız gerekiyorsa, numaralandırmayı finalize adımına taşıyın ve "taslak"ı birinci sınıf durum olarak kabul edin. Ardından birden fazla ajanın aynı anda tıkladığı ve API entegrasyonlarının eş zamanlı patlamalar gönderebildiği durumları yük testine tabi tutun ki gerçek kullanıcılar gelmeden davranışı görün.


