- Datomic Pro 1.0.7075 में tests के दौरान transactions के बीच safety, दस्तावेज़ों में किए गए दावे से ज़्यादा मजबूत दिखी, लेकिन transaction के अंदर की semantics आम sequential execution model से काफी अलग थी
- सभी test histories Serializable लगीं, और एकल peer session Strong Session Serializable था; writes और
d/sync reads Strong Serializable के करीब थे
- Datomic के add, retract और transaction function, transaction के अंदर क्रम से accumulate होकर execute नहीं होते; हर function केवल शुरुआत के समय की DB state को देखकर काम करता है
- अगर एक ही transaction में
approve और deny जैसे independently safe transaction functions को combine किया जाए, तो combined result invariant violation पैदा कर सकता है
- एक transaction में कई transaction functions डालते समय read set और write set के संबंध की जाँच करनी चाहिए, और entity predicate, attribute predicate, entity spec जैसी explicit constraints भी साथ में इस्तेमाल करनी चाहिए
Datomic Pro का model और architecture
- Datomic एक Entity-Attribute-Value OLTP database है, जो time concept को explicit रूप से model करता है
- किसी खास समय पर DB state को
[entity, attribute, value] रूप के datoms के set के रूप में represent किया जाता है
- हर datom में यह भी preserved रहता है कि उसे किस transaction ने add या retract किया था
- पूरा datom
[entity, attribute, value, transaction, asserted-or-retracted?] रूप का 5-tuple होता है
- Datomic एक temporal database है, इसलिए सिर्फ current नहीं, बल्कि past logical time या wall-clock time के आधार पर snapshots भी request किए जा सकते हैं
- full history view से यह भी query किया जा सकता है कि past में कोई specific fact मौजूद था या नहीं
- query methods के रूप में Datalog-style API, graph traversal API, और ODM-style
Entity type उपलब्ध कराता है
- Datomic Pro वह version है जिसे users खुद operate कर सकते हैं, और Datomic Cloud AWS पर चलता है तथा उसकी architecture कुछ अलग है
- Datomic Pro कई components के साथ मिलकर काम करने वाली structure है
- Transactor write transactions execute करने, indexes maintain करने, और storage में record करने के लिए responsible होता है
- Peer JVM library embedded वाला thick client है, जो transaction submission, storage target पर read queries, और caching करता है
- दूसरी language applications के लिए thin client और peer server based client-server model भी उपलब्ध कराता है
- Internally, Datomic हर transaction को chronological log में append करता है, और entity·attribute·value·time combinations के अनुसार sorted 4 indexes maintain करता है
- log और indexes Cassandra या DynamoDB जैसे storage में persistent और immutable trees के रूप में store होते हैं
- tree nodes immutable होने की वजह से backing storage के लिए eventual consistency guarantee करना ही काफी होता है
- commit के समय transactor नए immutable tree nodes store करने के बाद root pointer को compare-and-set(CaS) से आगे बढ़ाता है, और इस CaS को Sequential consistency चाहिए होती है
- Sequential CaS transactions का global order guarantee करता है, लेकिन write throughput को single transactor speed से बाँध देता है
- Datomic आम तौर पर एक समय में केवल एक active transactor रखता है, और fault tolerance के लिए कई transactors deploy करता है
- peers सीधे storage और transactor से connect करते हैं, और हर एक अपने पास monotonically increasing root pointer की copy बनाए रखता है
- reads immutable tree nodes को cache कर सकते हैं, इसलिए peers की संख्या बढ़ाने पर लगभग linear read scaling संभव होती है
Datomic का transaction model
- Datomic आम OLTP databases की तरह interactive transaction provide नहीं करता
- यह ऐसा model नहीं है जिसमें transaction शुरू किया जाए, operation result मिले, फिर next operation submit किया जाए, और अंत में commit किया जाए
- stored procedure जैसे transaction functions होते हैं, लेकिन वे caller को arbitrary values return नहीं कर सकते
- read और write paths सख्ती से अलग हैं
db peer को ज्ञात latest DB state return करता है
d/sync सभी peers के आधार पर latest state या किसी specific time के बाद की state पाने के लिए transactor के साथ synchronize करता है
d/as-of past point-in-time की DB state देता है
- DB state immutable होती है, इसलिए same state पर multiple queries बिल्कुल same logical time पर execute होती हैं
- write transaction को operations की ordered list के रूप में represent किया जाता है
- examples में
:db/add, :db/retract, db/cas, और user-defined transaction function calls शामिल हैं
- transaction function transaction की शुरुआत वाली DB state और arguments लेकर नए operations का set return करता है
- function calls recursively expand होते हैं, जब तक सिर्फ assertions और retractions बाकी न रह जाएँ
- transaction function अपने अंदर reads कर सकता है ताकि conditional writes तय कर सके, लेकिन read results या arbitrary information को
transact caller को directly return नहीं करता
transact transaction से ठीक पहले की DB state, transaction result DB state, और expanded datoms का set return करता है
- caller pre-state और post-state का उपयोग करके तय कर सकता है कि conditional write हुआ या नहीं
- Datomic को cheap और transferable DB snapshots के आसपास problems solve करने के लिए designed model माना जा सकता है
- Nubank, Datomic की current developer company है, जो लगभग 9.4 करोड़ users को financial services देती है और हर दिन औसतन 2.3 अरब user transactions process करती है
- Nubank के लगभग सभी products Datomic को system of record के रूप में इस्तेमाल करते हैं
consistency दावों और test design
- Datomic docs ACID transactions का दावा करते हैं, और मानते हैं कि transaction storage में single atomic write के रूप में लिखा जाता है और हर peer किसी खास समय तक पूरी हुई सभी transactions को total order में observe करता है
- client acknowledgement से पहले transaction durable storage में flush हो जाता है
- जनवरी 2024 की शुरुआत में analysis शुरू होने के समय के docs अनौपचारिक रूप से दावा करते थे कि write transactions Serializable हैं
- docs ने Datomic को “single-writer” system भी कहा था, लेकिन Jepsen के मुताबिक यह विवरण दो कारणों से inaccurate है
- fault tolerance के लिए कई transactors चलाए जा सकते हैं, और failure detector perfect नहीं हो सकता, इसलिए ऐसी window बन सकती है जिसमें कई transactors खुद को एक साथ active मानते हों
- भले ही केवल एक transactor हो, network delay के कारण storage messages दूसरे transactor messages के साथ interleave हो सकते हैं
- Datomic की safety “single-writer” logic से नहीं, बल्कि storage CaS operation की Sequential consistency से आती है, ऐसा आकलन किया गया
- कई concurrent transactors होने पर भी CaS को safety प्रदान करनी चाहिए
- tests Jepsen testing library से लिखे गए Datomic test suite का उपयोग करते हैं
- Datomic Pro 1.0.7075 को Debian Bookworm node cluster पर install किया गया
- storage के लिए AWS DynamoDB table का उपयोग किया गया
- दो nodes transactor चलाते हैं, और बाकी nodes peer चलाते हैं
- peer एक छोटा Clojure program था जो Datomic peer library का उपयोग करता था, और test operations के लिए HTTP API expose करता था
- tests ने
d/db का उपयोग करने वाला stale read संभव mode और d/sync से freshness guarantee करने वाला mode, दोनों चलाए
- fault injection transactor और peer दोनों पर लागू किया गया
- process pause, crash, clock error inject किए गए
- transactor·peer के बीच और node·storage के बीच network partitions बनाए गए
- Datomic garbage collection भी request किया गया
- अगर transactor storage के साथ stable connection बनाए नहीं रख पाता, तो वह खुद exit हो जाता है
- AWS के बाहर nodes पर default 5-second timeout इस्तेमाल करने पर normal network fluctuation से भी हर कुछ मिनट में exit हो जाता था
- EC2 test environment में भी 1-second timeout पर हर 10–20 मिनट में exit होता था
- Datomic recommend करता है कि operator supervisor daemon से transactor को restart करे, और tests ने
Restart=on-failure setting वाली systemd service का उपयोग किया
चार workloads
-
List Append
- List Append workload Elle transaction checker के साथ इस्तेमाल किया गया
- logically यह integer element lists को handle करता है, और हर list को integer primary key से identify किया जाता है
- clients list read या unique element append से बने random transactions execute करते हैं
- Elle aborted read, intermediate read, internal consistency violation, element order mismatch check करता है और dependency graph cycle ढूंढकर consistency model violation का फैसला करता है
- Datomic में list को एक entity और दो attributes के रूप में encode किया गया
append/key primary key की भूमिका निभाता है
append/elements multi-valued attribute है जो list के integer elements store करता है
- multi-valued attribute unordered set होता है, इसलिए Jepsen ने हर datom के transaction timestamp के आधार पर elements को sort कर Elle के लिए जरूरी order हासिल किया
- mixed read-write transaction न होने की constraint को transaction function और pre-state calculation से bypass किया गया
- writes transaction function से किए गए
transact द्वारा लौटाए गए pre-state का उपयोग करके calculate किया गया कि transaction के अंदर reads ने क्या देखा होगा
- वही function
transact में एक बार, और peer में pre-state आधारित read supplement के लिए एक बार चलाया गया
-
List Append with CaS
- List Append with CaS workload
db/cas pattern का उपयोग करता है
- user
d/db से current state पढ़ता है, और उदाहरण के लिए value 4 होने पर ही 5 में बदलने वाला [:db/cas 123 :counter/value 4 5] submit कर सकता है
- सभी writes में
db/cas इस्तेमाल करने से logical “user transaction” के ऊपर ad hoc Snapshot Isolation बनाया जा सकता है
- यह workload list को multi-valued नहीं, बल्कि single-valued comma-separated string के रूप में store करता है
- transaction start पर पढ़ता है, local में reads·writes apply करता है, फिर ऐसा CaS transaction बनाता है जो guarantee करता है कि पढ़ने के बाद value बदली नहीं है
-
Internal
- Internal workload transaction-internal consistency को सीधे measure करता है
- इसमें cases शामिल हैं: एक ही entity attribute पर 1 assert करने के बाद 2 assert करना
- उसी transaction में fact assert और retract करना
- value assert करने के बाद CaS से बदलने की कोशिश करना
- 1→2, 2→3 जैसे कई CaS perform करना
- entity create करने के बाद lookup ref से modify करना
- transaction function से value को दो बार increment करने की कोशिश करना
-
Grant
- Grant workload जांचता है कि transaction function function invariant को preserve करता है या नहीं
- grant को
created-at, approved-at, denied-at तीन attributes वाली single entity के रूप में encode किया गया
- grant एक साथ approved और denied state में नहीं होना चाहिए
approve और deny functions पहले check करते हैं कि grant पहले से approved या denied है या नहीं, और जरूरत पड़ने पर abort करते हैं
- कई transaction boundary combinations में यह check किया गया कि grant एक साथ approved और denied बनता है या नहीं
test results: transactions के बीच safety मजबूत दिखी
- Jepsen को Datomic के core safety claims के खिलाफ कोई behavior नहीं मिला
- transactions ऐसे दिखे जैसे वे total order में apply हुए हों
- वह order हर peer पर local operation order से match करता है
- केवल write transactions तक सीमित histories और reads में
(d/sync conn) का उपयोग करने वाली histories real-time order से match करती थीं
- Jepsen ने इसे Strict Serializable जैसा दिखने वाला माना
- अगर session को single peer से bound करके interpret किया जाए, तो Datomic Strong Session Serializability guarantee करता हुआ दिखता है
- transaction history किसी total order में execute हुई history से distinguish नहीं की जा सकती
- वह order हर peer पर observed order से match करता है
d/db asynchronously updated DB copy return करता है, इसलिए stale read संभव है
- Datomic docs भी साफ कहते हैं कि peer read हाल में committed transactions में से कुछ को observe नहीं कर सकता
d/sync transactor के साथ synchronize करके stale read रोकता है
- experimental verification की सीमाएं बनी रहती हैं
- bug की मौजूदगी prove की जा सकती है, लेकिन absence prove नहीं की जा सकती
- Datomic जिस storage system पर निर्भर करता है, उसमें correctness error Datomic guarantees के violation तक ले जा सकता है
- DynamoDB पर Datomic उतना ही safe है जितना DynamoDB का compare-and-set operation
ट्रांज़ैक्शन के अंदरूनी semantics: क्रम नहीं, concurrency
- ज़्यादातर databases और प्रमुख transaction isolation formalizations ट्रांज़ैक्शन के अंदर serial execution semantics देते हैं
- अगर
set x = 1; read x; हो, तो read आम तौर पर 1 देखता है
- Adya, Cerone·Bernardi·Gotsman, Crooks·Alvisi·Pu·Clement आदि की formalizations ट्रांज़ैक्शन के अंदर operation के क्रम और इस गुण को स्पष्ट करती हैं कि “बाद वाला read, पहले वाले write को observe करता है”
- Datomic का transaction request ordered list होता है, लेकिन execution उस क्रम को preserve नहीं करता
- add, retract, transaction function ऐसे व्यवहार करते हैं जैसे वे एक-दूसरे के साथ simultaneously execute हो रहे हों
- transaction function हमेशा ट्रांज़ैक्शन शुरू होने के समय की DB state observe करता है
- यह पिछले assertion, retraction, transaction function के प्रभाव नहीं देखता
- जिस entity की current value
0 है, उसमें वही CaS दो बार डालने पर Datomic में दोनों starting state 0 देखते हैं और सफल हो जाते हैं
[[:db/cas 123 :internal/value 0 1]
[:db/cas 123 :internal/value 0 1]]
- serial model में पहला CaS value को
1 में बदल देगा और दूसरा CaS fail होना चाहिए
- Datomic में दोनों CaS duplicate assertion बनाते हैं और final value
1 हो जाती है
- दो increment transaction function भी serial model से अलग नतीजा देते हैं
[['internal/increment "x"]
['internal/increment "x"]]
- अगर starting value
0 है, तो serial model का result 2 होगा
- Datomic में दोनों functions starting state
0 देखते हैं और final value 1 हो जाती है
- transaction function पहले वाले assertion को भी नहीं देखता
[[:db/add id-of-x :internal/value 1]
['internal/increment "x"]]
- Datomic में final value
2 नहीं, बल्कि 1 हो जाती है
- lookup ref भी ट्रांज़ैक्शन शुरू होने के समय की DB state का इस्तेमाल करता है
- उसी ट्रांज़ैक्शन में entity जोड़ने के बाद lookup ref से उस entity को reference नहीं किया जा सकता
- इस case में
Unable to resolve entity error के साथ abort होता है
conflict detection और pseudo write skew
- Datomic में अगर एक ही ट्रांज़ैक्शन के अंदर single cardinality attribute पर अलग-अलग values assert की जाएँ, तो यह
:db.error/datoms-conflict के साथ abort करता है
- starting value
0 के लिए value 2 assert की जाए और साथ ही increment function value 1 assertion बनाए, तो conflict होता है
- यह conflict detection transaction function की गलत composition से पैदा होने वाले कई चौंकाने वाले results को रोक सकता है
- अगर write set अलग-अलग
[entity, attribute] pairs हों, तो सिर्फ conflict detection से invariants बचाना मुश्किल है
- grant workload यह स्थिति दिखाता है
approve और deny प्रत्येक पहले check करते हैं कि grant अभी न approved है न denied, फिर अलग-अलग attributes जोड़ते हैं
- अलग-अलग transactions में
approve और deny call करने पर Datomic की Serializable transactions invariant guarantee करती हैं
- लेकिन एक ही ट्रांज़ैक्शन के अंदर दोनों को साथ call करने पर दोनों starting state देखते हैं और सफल हो जाते हैं
[['grant/approve id]
['grant/deny id]]
- result grant में
approved-at और denied-at दोनों आ जाते हैं
- “grant एक साथ approved और denied नहीं होना चाहिए” वाला invariant टूट जाता है
- Datomic का in-transaction conflict checker इसे नहीं रोकता, क्योंकि दोनों functions ने अलग-अलग attributes पर assertion बनाए
- यह phenomenon Berenson आदि के Write Skew जैसा है
- दोनों functions एक-दूसरे के effects नहीं देख पाते, इसलिए read-write anti-dependency cycle बनता है
- अगर transaction function को transaction की तरह देखें, तो यह Repeatable Read और Serializability द्वारा prohibit किए गए G2-item anomaly जैसा है
- Datomic और Nubank इस behavior को bug नहीं, बल्कि Datomic का expected behavior मानते हैं
- Nubank Datomic की concurrent intra-transaction semantics बनाए रखने की योजना में है
entity predicate से invariant मजबूत करना
- Datomic type, uniqueness, specific attribute predicate, entity predicate जैसे constraint mechanisms देता है
- entity predicate सभी transaction effects लागू होने के बाद candidate DB state और entity ID लेकर commit allow करना है या नहीं, इसे
true या false में return करता है
- नाम entity predicate है, लेकिन यह पूरे DB state तक access कर सकता है, इसलिए किसी खास entity से आगे global constraints भी express कर सकता है
- grant example में
valid-grant? predicate से approved-at और denied-at को simultaneously मौजूद होने से रोका जा सकता है
(defn valid-grant?
[db eid]
(let [{:grant/keys [approved-at denied-at]}
(d/pull db '[:grant/approved-at
:grant/denied-at]
eid)]
(not (and approved-at denied-at))))
- schema में entity spec जोड़कर इस predicate को reference किया जाता है
- entity spec से जुड़ा entity predicate हर transaction पर automatically apply नहीं होता
- Datomic entity spec apply करने या न करने को domain decision मानता है, और इसका रुख है कि हर transaction को इसे explicitly request करना चाहिए
approve और deny functions attribute जोड़ने के बाद :db/ensure virtual datom से entity spec apply करने की request कर सकते हैं
(defn approve
[db id]
[[:db/add id :grant/approved-at (Date.)]
[:db/add id :db/ensure :grant/valid?]])
- इस entity spec का use करने पर, जब same transaction में approve और deny को साथ try किया जाता है, तो entity predicate error आती है और invariant preserve रहता है
- error में
:db.error/entity-pred, :db.error/pred-return false शामिल होते हैं
दस्तावेज़ बदलाव और यूज़र सलाह
- Jepsen के साथ सहयोग के बाद Datomic ने अपने दस्तावेज़ों में बड़े बदलाव किए
- transaction safety documentation अब उस अधिक मजबूत safety को दर्शाता है जिसे Datomic वास्तव में प्रदान करता माना जाता है
- global Serializability, प्रति-peer monotonicity, और write या
sync का उपयोग करने वाली reads के लिए Strict Serializability को स्पष्ट किया गया
- safety दस्तावेज़ से “single-writer” तर्क हटा दिया गया
- transaction syntax and semantics दस्तावेज़ transaction request की संरचना, map form और transaction function expansion rules, और transaction लागू होने की प्रक्रिया को व्यापक रूप से कवर करता है
- transaction functions दस्तावेज़ भी संशोधित किया गया
- consistency सुनिश्चित करने वाले कई mechanisms, function creation और invocation, तथा built-in functions के व्यवहार को समझाता है
- यह wording हटा दी गई कि transaction function “atomically analyze and transform database values” कर सकता है या “atomic read-modify-write processing” की गारंटी देता है
- Datomic आगे से
d/transact को दी जाने वाली data structure को “transaction” नहीं, बल्कि transaction request कहना चाहता है
- उसके elements को “statements” या “operations” के बजाय “data” कहना चाहता है
[:db/add ...] और [:db/retract ...] क्रमशः assertion request और retraction request हैं
- इससे वास्तविक assertion datom और transaction request के भीतर मौजूद अधूरी assertion request में फर्क करना आसान होता है
- यूज़र्स के लिए ज़रूरी सावधानियां स्पष्ट हैं
- Datomic के transactions के बीच Serializability पर भरोसा किया जा सकता है
- transaction के अंदर concurrent execution semantics एक असामान्य विकल्प है, इसलिए एक ही transaction में कई transaction functions call करते समय सावधान रहना चाहिए
- खासकर उन मामलों में सावधानी रखें जहां read set overlap करता हो और write set अलग-अलग हों
- कई increments चुपचाप collapse होकर एक ही update बन सकते हैं
- attribute predicate और entity spec का इस्तेमाल किया जा सकता है, लेकिन entity spec को हर ज़रूरी transaction में स्पष्ट रूप से request करना होगा
- operations के लिहाज़ से भी transactor restart और network fluctuations को ध्यान में रखना चाहिए
- Datomic transactor अगर कुछ मिनट तक storage से communicate नहीं कर पाता, तो खुद ही बंद हो जाता है
- Jepsen सलाह देता है कि network fluctuations के प्रति ज्यादा resilient बनाने के लिए transactor में retry loop जोड़ा जाए
सीमाएं और आगे के research questions
- इस test में evaluation scope से बाहर कुछ items बाकी हैं
- excision और historical query का evaluation नहीं किया गया
- Datomic client library की भी जांच नहीं की गई, लेकिन Jepsen मानता है कि उसका behavior test peer जैसा हो सकता है
- storage engine के रूप में केवल DynamoDB का उपयोग किया गया
- Datomic Cloud का भी evaluation नहीं किया गया, और Datomic Cloud थोड़ी अलग architecture का उपयोग करता है
- Jepsen बताता है कि उसे ऐसे systems या formalizations बहुत कम मालूम हैं जो transactions के बीच Serializability और transaction के भीतर concurrent semantics दोनों साथ में प्रदान करते हों
- Datomic का model कई research questions पैदा करता है
- क्या Datomic transaction को पारंपरिक transaction के dual या “co-transaction” model के रूप में देखा जा सकता है
- क्या ऐसे model के फायदे-नुकसान को static analysis, runtime check, और API extension से कम किया जा सकता है
- वास्तविक users द्वारा invariants तोड़ने वाले transaction लिखने की संभावना कितनी है
- तुलना के लिए temporal Datalog research project Alvaro’s Dedalus और Fauna का उल्लेख किया गया
- Dedalus में भी Datomic की तरह transaction “all at once” होता है
- Fauna एक temporal database है और Strong Serializability तक support करता है; Datomic के विपरीत, यह transaction के भीतर serial execution और incremental side effect प्रदान करता दिखता है
- Datomic के end-of-transaction conflict checker और Snapshot Isolation के first-committer-wins rule के बीच समानता भी research opportunity के रूप में बनी हुई है
- Snapshot Isolation literature के कौन-से हिस्से Datomic पर लागू किए जा सकते हैं
- Datomic transaction के भीतर cycle किस anti-dependency edge के रूप में व्यक्त होता है
- lost update, Fractured Read, read-only transaction anomaly, Long Fork जैसी phenomena के corresponding internal-semantics analogues हैं या नहीं, यह सवाल बाकी है
- CALM theorem से connection भी आगे देखा जा सकता है
- क्या logically monotonic transaction functions को एक ही Datomic transaction के भीतर सुरक्षित रूप से combine किया जा सकता है
- यह research किया जा सकता है कि negation के बिना Datalog program इस execution model में भी safe हैं या नहीं
1 टिप्पणियां
Hacker News की राय
मैंने यह काम होते हुए पास से देखा था, और चर्चा की प्रक्रिया देखना वाकई दिलचस्प था
यह भी हैरान करने वाला था कि Jepsen को कोई गंभीर bug नहीं मिला, और सिर्फ documentation व intended unusual behavior को स्पष्ट कर देना भी बहुत उपयोगी नतीजा था
यह सोचते हुए कि हम Datomic पर बैंक चला रहे हैं, भरोसा बनाने की exercise के तौर पर यह पूरी तरह मूल्यवान था
वाकई शानदार लेख है, और जब भी मुझे लगता है कि मैं काफ़ी smart हूं, Jepsen analysis पढ़ना विनम्र बने रहने के लिए अच्छा रहता है
यह पहली बार है जब मैंने Jepsen report को गहराई से पढ़ा, और मुझे Datomic के transaction internals को साफ़ तरीके से समझाने वाला हिस्सा पसंद आया
मुझे यह भी एहसास हुआ कि Datomic transactions और SQL database transactions के बीच का फर्क मैं कितना कम समझता था
खासकर यह paragraph ध्यान खींचता है: “Datomic पहले
d/transactको दिए जाने वाले data structure को ‘transaction’ कहता था, और उसके elements को ‘statements’ या ‘operations’ कहता था। आगे से हम इस structure को ‘transaction request’ और उसके elements को ‘data’ कहना चाहते हैं”मैं सोच रहा हूं कि इसका
datomic.apinamespace के d/transact-async और related features के लिए क्या मतलब हैमैंने लगभग 1 साल से Datomic इस्तेमाल नहीं किया है, और लगता है काफी कुछ बदल गया है
datomic.apiकी सभी functionalities वैसी ही हैंयह एक सचमुच अच्छे database पर बेहतरीन और विस्तृत report है
documentation का स्पष्ट और updated होना भी बहुत अच्छी बात है
साथ ही, काश Apple FoundationDB पर Jepsen analysis का खर्च उठाए
मुझे पता है Aphyr ने कहा था कि “उनके tests शायद बेहतर हैं”, लेकिन अगर Jepsen को सच में FoundationDB में कोई समस्या नहीं मिलती, तो यह एक और शानदार database होने का मजबूत आधार होगा
मैं इस field का expert नहीं हूं, लेकिन जब “काश $foo खर्च उठा देता” जैसी बात सुनता हूं तो मेरे कान खड़े हो जाते हैं
capital तो भरपूर है, लेकिन Apple से कुछ करवाने का इंतज़ार करना मेरे अनुभव में लंबा खिंचता है
Jepsen ने invariant violation तक ले जाने वाली एक स्पष्ट स्थिति खोजी, और Datomic की तरफ़ से प्रतिक्रिया सिर्फ़ documentation को स्पष्ट करने तक सीमित दिखी—यह बात दिलचस्प लगी
तो क्या आखिर में Datomic टीम यह मानती है कि ऐसे violation होते हैं, लेकिन उन्हें इसकी चिंता नहीं है?
लेख में कहा गया है: “Datomic के नज़रिए से grant workload में invariant violation user error है। transaction functions क्रम से atomically execute नहीं होते। अगर transaction के अंदर कोई दूसरा operation उनकी precondition को अमान्य कर सकता है, तो transaction function में precondition check करना सुरक्षित नहीं है”
user के नज़रिए से इसका मतलब क्या है, यह देखने के लिए इस तरह का transaction pseudo-data सोच सकते हैं
[ [Stu favorite-number 41] ;; maybe more stuff [Stu favorite-number 42] ]इसे operational तरीके से पढ़ें तो transaction की शुरुआत में ऐसा लगता है कि मुझे 41 पसंद था, और बाद में 42 पसंद होने लगा
transaction खत्म होने के बाद observer यह देखने की उम्मीद करेगा कि मुझे सिर्फ़ 42 पसंद है, और उसे यह चिंता करनी होगी कि किन conditions में 41 दिखाई दे सकता है
transaction के अंदर की semantics की ऐसी operational interpretation कई databases में आम है, लेकिन यह मानती है कि transaction के भीतर कई time points मौजूद हैं
Datomic में ऐसे time points नहीं हैं, न ही वह उन्हें चाहता है, और वह इस बात को प्राथमिकता देता है कि “transaction के बीच में” क्या हुआ, इसकी चिंता न करनी पड़े
Datomic में transaction के सभी facts एक ही time point पर होते हैं, इसलिए यह transaction कहता है कि मुझे दोनों numbers एक साथ पसंद आने लगे
अगर Datomic transaction को कई operations के combination के रूप में गलत पढ़ें, तो स्वाभाविक रूप से तरह-तरह की “invariant anomalies” मिल सकती हैं
उल्टा, SQL transaction पर Datomic model गलत तरीके से चढ़ा दें, तो भी “invariant anomalies” मिल सकती हैं
ऐसी गलतफहमी की संभावना की वजह से अच्छे documentation की ज़रूरत थी, और Jepsen के साथ मिलकर हमने docs बेहतर किए [1], लापरवाह wording को सुधारा और गलतफहमी घटाने की कोशिश की
इस खास गलतफहमी को सीधे address करने वाला एक tech note भी जोड़ा [2]
[1] https://docs.datomic.com/transactions/transactions.html#tran...
[2] https://docs.datomic.com/tech-notes/comparison-with-updating...
असल में यह कितना महत्वपूर्ण है, यह इस पर निर्भर करता है कि user कौन-से invariants preserve करने के लिए transaction functions लिख रहा है, और क्या वे functions invariant को सिर्फ़ तब बनाए रखते हैं जब वे simultaneous नहीं बल्कि sequentially executed हों
Datomic का रुख, या बेहतर होगा अगर Datomic की तरफ़ से कोई बीच में आकर बताए, यह है कि users ऐसे transaction functions बहुत बार नहीं लिखते
यह position defend की जा सकती है। क्योंकि docs में साफ़ लिखा था कि transaction functions एक-दूसरे को observe नहीं करते, बल्कि transaction की initial state को observe करते हैं
दूसरी ओर docs में ऐसा wording भी था जो संकेत देता था कि transaction functions को invariants preserve करने के लिए इस्तेमाल किया जा सकता है: “[txn fns] can atomically analyze and transform database values. You can use them to ensure atomic read-modify-update processing, and integrity constraints...”
इस wording और इस तथ्य की वजह से कि लगभग सभी दूसरी serializable databases transaction के अंदर sequential semantics इस्तेमाल करती हैं, report में इस issue को काफी जगह दी गई
यह जटिल सवाल है, इसलिए कोई साफ़ जवाब नहीं है; मैं सुनना चाहूँगा कि general database community और खास तौर पर Datomic users इस semantics को कैसे स्वीकार करते हैं
“invariant violation तक ले जाने वाली स्थिति” कहने से यह Datomic का bug जैसा सुनाई देता है, लेकिन यह वैसा नहीं है
आपको Datomic transactions को process करने के तरीके को समझना होगा और उसी के हिसाब से code लिखना होगा
Nubank से मेरा कोई संबंध नहीं है, लेकिन Datomic को general-purpose database के रूप में इस्तेमाल करते हुए मुझे ऐसी कोई स्थिति नहीं मिली जहाँ यह समस्या बनी हो
जिन्हें नहीं पता, उनके लिए जोड़ दूँ कि Jepsen नाम “call me maybe” गाने वाली Carly Rae Jepsen पर आधारित एक wordplay है
distributed systems research project के नाम के लिए मुझे यह perfect लगता है
पहला commit 2013 का है
https://github.com/jepsen-io/jepsen/tree/4b112e7046a20efa80a...
गाना 2011 में आया था; समय सचमुच बहुत तेज़ भागता है
मैंने Datomic को production में लंबे समय तक इस्तेमाल नहीं किया है, लेकिन यह इतना अलग है कि सोचता हूँ इसमें चौंकने जैसा क्या है
Datomic transactions मूल रूप से batch के करीब हैं, और मैंने हमेशा उन्हें single-threaded माना है, इसलिए race conditions कम होना स्वाभाविक लगता है
design के हिसाब से यह slow और safe वाली दिशा में है
लगता है काफी सावधान रहना पड़ेगा
धन्यवाद Kyle
यह स्पष्ट है कि हमारा documentation पर्याप्त नहीं था
Rich के साथ मिलकर हमने Datomic के transaction model पर ज्यादा स्पष्ट और व्यापक documentation लिखने की कोशिश की
उम्मीद है कि आम गलतफहमियों को पहले ही रोका जा सकेगा; सभी feedback का स्वागत है
https://docs.datomic.com/transactions/model.html
Datomic का डेटा मॉडल अगर आप triple store या RDF से परिचित हैं तो काफ़ी सहज लगता है
लेकिन documents या online discussions में इस समानता का अक्सर ज़िक्र नहीं होता
सोचता हूँ कि क्या लोग ऐसे concepts से परिचित नहीं हैं, या semantic web से जुड़ी associations को distraction मानते हैं, या फिर कोई बुनियादी अंतर है जो मुझसे छूट रहा है
मैं इस analysis का सच में इंतज़ार कर रहा था
हाल में मैं खुद Datomic-जैसा data store बना रहा हूँ, इसलिए यह उपयोगी लगेगा, और अभी पढ़ रहा हूँ
MongoDB analysis भी दिलचस्प था, और Redis, RethinkDB वगैरह के दूसरे analyses भी ज़रूर देखने लायक हैं
कभी rqlite/dqlite या turso/libsql पर भी analysis हो तो अच्छा होगा
2 साल पहले rqlite पर Jepsen-style analysis हुआ था [1]
https://www.philipotoole.com/testing-rqlite-read-consistency...
report खुद यहाँ है: https://github.com/wildarch/jepsen.rqlite/blob/main/doc/blog...
[1] https://www.rqlite.io