4 पॉइंट द्वारा GN⁺ 2025-05-23 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • केंद्रीय सर्वर-आधारित सहयोगी ऐप में अगर टेक्स्ट को array index से एडिट किया जाए, तो concurrent editing के दौरान पोज़िशन खिसक जाती है। इसलिए हर अक्षर को globally unique ID दी जाती है और सर्वर स्टेट को “किसी खास ID के बाद insert” करने के तरीके से अपडेट किया जाता है
  • वास्तविक सेवाओं में इस्तेमाल होने वाले CRDT और OT शक्तिशाली हैं, लेकिन total-order algorithms या operation transformation rules जटिल होते हैं, इसलिए ऐप की ज़रूरतों के अनुसार उनके अंदरूनी व्यवहार को बदलना कठिन होता है
  • प्रस्तावित तरीका यह है कि client और server Array<{ id: ID; char?: string; isDeleted: boolean }> के रूप में ID सूची बनाए रखें, और delete किए गए अक्षरों को भी tombstone की तरह रहने दें ताकि बाद की insertion position references टूटें नहीं
  • optimistic local updates को server reconciliation से संभाला जाता है। remote operations मिलने पर, pending local operations को पहले undo किया जाता है, फिर remote operations और unacknowledged local operations को क्रम से दोबारा apply किया जाता है
  • इसमें concurrent insertion order, rich text formatting, distributed variants, और Articulated लाइब्रेरी तक शामिल हैं, और सर्वर ऐप-विशिष्ट semantics के अनुसार insert/delete से आगे बढ़कर flexible operations भी परिभाषित कर सकता है

index-आधारित editing concurrent editing में क्यों टूटती है

  • collaborative text editing में client उपयोगकर्ता द्वारा किए गए operations सर्वर को भेजता है, और सर्वर को अपनी authoritative state अपडेट करनी होती है
  • अगर टेक्स्ट को character array माना जाए और index 17에 " the" 삽입 जैसे operation भेजे जाएँ, तो सर्वर तक पहुँचने से पहले किसी दूसरे उपयोगकर्ता की insertion के कारण वही index किसी दूसरी जगह को point कर सकता है
    • उदाहरण के लिए, अगर Alice आगे की ओर " gray" insert करती है, तो Bob का index 17 अब मूल स्थान नहीं रहेगा
    • सर्वर को Bob के operation को index 22 पर rebase करना होगा
  • मूल सवाल यह है कि client सर्वर को कौन-सा operation भेजे, और सर्वर उसे कैसे समझे ताकि टेक्स्ट को “स्पष्ट रूप से सही” तरीके से अपडेट किया जा सके
  • यह index rebase समस्या सिर्फ Google Docs जैसे real-time collaborative apps में ही नहीं, बल्कि list items insert करने वाले web forms, या inline comments और edit history संभालने वाले single-threaded local apps में भी आ सकती है

CRDT और OT व्यावहारिक काम में कहाँ भारी पड़ते हैं

  • मौजूदा समाधान मोटे तौर पर CRDT और OT में बँटते हैं
    • CRDT हर अक्षर को immutable ID या “position” देता है, और IDs को किसी विशेष mathematical total order, जैसे tree traversal, से sort करता है
    • OT concurrent editing को ध्यान में रखकर operations को ही transform करता है; उदाहरण में index 17 insert को index 22 insert में बदल दिया जाता है
  • दोनों approaches पहले से वास्तविक सेवाओं में इस्तेमाल हो रही हैं
    • Google Docs OT का उपयोग करता है
    • Yjs CRDT लाइब्रेरी कई ऐप्स में इस्तेमाल होती है
  • बोझ conceptual complexity से आता है
    • text editing CRDT का total order अक्सर academic papers में परिभाषित एक सूक्ष्म algorithm होता है
    • OT algorithms को algebraic “transformation properties” पूरी करनी होती हैं, case combinations वर्गानुपाती बढ़ते हैं, और formal verification के बिना bugs आम होते हैं
  • जटिल algorithms implementation को भी जटिल बना देते हैं, और आमतौर पर विशेषज्ञों द्वारा बनाई गई लाइब्रेरियों को network black box की तरह इस्तेमाल करना पड़ता है
  • जब लाइब्रेरी द्वारा न सोची गई किसी feature की ज़रूरत होती है, तब monolithic structure बाधा बनता है
    • बड़े दस्तावेज़ के सिर्फ ज़रूरी हिस्से को memory में रखना और बाकी को disk पर छोड़ना
    • paragraph-level editing permissions या कुछ formatting permissions जैसे subdocument permissions को सर्वर पर enforce करना
    • Google Docs-शैली के suggested changes को मुख्य टेक्स्ट के भीतर या उसके बगल में दिखाना
    • Replicache जैसे key-value store के साथ आसानी से sync होने वाले representation में टेक्स्ट को store करना
    • insert/delete के अलावा text move, document tree manipulation, paragraph split/merge जैसे operations को support करना

character ID और “insert after” तरीका

  • मूल विचार यह है कि array index की जगह हर अक्षर को globally unique ID दी जाए
    • मुख्य data structure Array<{ id: ID; char: string }> के रूप में है
    • client index 17 पर insert की बजाय सर्वर को f1bdb70a के बाद " the" insert जैसा operation भेजता है
    • सर्वर target ID ढूँढकर उसके ठीक बाद नए अक्षर जोड़ देता है
  • नए अक्षरों की IDs भी client को साथ में तय करनी होती हैं
    • उदाहरण: f1bdb70a के बाद " the" को ids [...] के साथ insert
    • client IDs generate करे तो सर्वर response आने से पहले भी बाद के insert after operations में उन नई IDs को refer कर सकता है
  • अगर delete किए गए अक्षरों को पूरी तरह हटा दिया जाए, तो insertion position खो सकती है
    • जब Bob 26085702 के बाद insert करना चाहता हो और इस बीच किसी दूसरे उपयोगकर्ता ने 26085702 अक्षर को delete कर दिया हो, तो सर्वर को पता नहीं होगा कि insertion कहाँ करनी है
    • इसलिए सर्वर को deleted IDs भी अपनी internal list में रखनी होंगी
  • संशोधित state representation इस तरह है
Array<{ id: ID; char?: string; isDeleted: boolean }>
  • उपयोगकर्ता को दिखने वाला टेक्स्ट सिर्फ non-deleted items को जोड़कर बनाया जा सकता है
list.filter(elt => !elt.isDeleted).map(elt => elt.char).join('')

insertion और deletion को संभालना

  • character input के समय client और server का व्यवहार सरल है
    • client insertion point के ठीक पहले वाले character की ID before ढूँढता है
    • नए अक्षर के लिए UUID जैसी globally unique ID id बनाता है
    • सर्वर को operation भेजता है कि before के बाद char को id के साथ insert किया जाए
    • सर्वर deleted items सहित before ढूँढता है और उसके तुरंत बाद { id, char, isDeleted: false } insert करता है
  • character deletion भी ID-आधारित है
    • client delete किए जाने वाले character की id ढूँढता है
    • सर्वर को उस ID वाले item को delete करने का operation भेजता है
    • सर्वर वह item ढूँढकर, अगर वह पहले से deleted न हो, तो entry.isDeleted = true सेट कर देता है
  • इस तरीके से CRDT या OT papers का सहारा लिए बिना, सर्वर को भेजे जाने वाले edit operation की position problem सीधे हल की जा सकती है
  • साधारण array implementation अक्षम हो सकती है क्योंकि हर character के लिए UUID store करना पड़ता है; optimization पर Articulated में चर्चा है

optimistic updates और server reconciliation

  • Google Docs-शैली की collaborative editing में उपयोगकर्ता को सर्वर response का इंतज़ार किए बिना अपने input का परिणाम तुरंत दिखना चाहिए
  • कठिन हिस्सा तब आता है जब client पर अभी भी सर्वर द्वारा मंज़ूर न किए गए pending local operations हों, और उसी दौरान उससे concurrent remote operations सर्वर से मिलें
  • इस स्थिति में CRDT अनिवार्य नहीं है; इसे server reconciliation से संभाला जा सकता है
    1. सभी pending local operations को undo करके client state को पुराने server state के दृष्टिकोण तक rollback करें
    2. remote operations apply करके client को server state के अनुसार लाएँ
    3. अभी तक acknowledge न हुए local operations को फिर से apply करें
  • एक और सरल रणनीति Wait for Ack है, जिसमें pending local operations होने पर remote operations को process ही नहीं किया जाता
    • Bob का client अपना message process हो जाने वाली server state मिलने तक पहला server message ignore कर सकता है
    • लेकिन अगर Bob लगातार typing करता रहे या network delay अधिक हो, तो delay अनिश्चित रूप से लंबा हो सकता है, इसलिए यह server reconciliation जितना real-time नहीं है

CRDT से क्या अलग है

  • प्रस्तावित तरीका हर character को ID देने और isDeleted marker इस्तेमाल करने के कारण CRDT से कुछ समानताएँ रखता है
  • अंतर order को संभालने के तरीके में है
    • इस तरीके में client सर्वर से कहता है X को Y के बाद insert करो, और सर्वर वैसा ही करता है या developer द्वारा परिभाषित किसी दूसरे तरीके से उसे संभालता है
    • text editing CRDT में IDs को जटिल algorithms के अनुसार sort किया जाता है
  • अलग-अलग text editing CRDTs के बीच का मुख्य अंतर भी यही ID sorting algorithm बनाता है, और यह approach उसी हिस्से से बचती है

concurrent insertion से क्या परिणाम बनते हैं

  • अगर कई उपयोगकर्ता एक ही जगह पर एक साथ input करते हैं, तो परिणाम सर्वर को operations जिस क्रम में मिले, उसके reverse order में व्यवस्थित होता है
  • उदाहरण के लिए टेक्स्ट "My name is" है, और Charlie " Charlie" तथा Dave " Dave" एक साथ input करते हैं
    • अगर Charlie का operation पहले पहुँचता है, तो सर्वर "My name is Charlie" बनाता है
    • Dave का operation भी is के s की उसी ID के बाद insert करता है, इसलिए परिणाम "My name is Dave Charlie" बनता है
  • एक ही target ID के बाद होने वाले insert after operations में, concurrency न होने पर भी, सर्वर receive order का reverse दिखता है
  • फिर भी बाएँ से दाएँ typed शब्द character-level पर आपस में गड़बड़ाकर नहीं मिलते
    • Dave हर character को अलग operation के रूप में भेजे, तब भी a को D के बाद, v को a के बाद insert किया जाएगा
    • सर्वर state "My name is D Charlie""My name is Da Charlie""My name is Dav Charlie""My name is Dave Charlie" की तरह बदलती है
  • दाएँ से बाएँ input के मामले में, अगर Charlie और Dave के operations interleaved order में सर्वर तक पहुँचें, तो परिणाम टेक्स्ट भी interleaved हो सकता है
    • व्यवहार में ऐसा तब हो सकता है जब दोनों उपयोगकर्ता एक साथ online हों और एक-दूसरे की ongoing edits को नज़रअंदाज़ कर रहे हों

सर्वर अधिक flexible operations परिभाषित कर सकता है

  • server reconciliation का उपयोग करने पर सर्वर client operations को लगभग किसी भी वांछित तरीके से process कर सकता है, और client अंततः उसी state तक पहुँच जाएगा
  • यह CRDT और OT से अलग है, जहाँ सामान्यतः केवल वे operations स्वीकार्य होते हैं जो कठोर algebraic rules पूरी करते हों
  • एक ही position पर concurrent insertion के लिए सर्वर कई तरह से प्रतिक्रिया दे सकता है
    • operation को ignore करके no-op बना देना
    • ID को internal list में जोड़ना लेकिन तुरंत deleted mark कर देना, ताकि बाद के Dave operations पुरानी ID को refer कर सकें
    • टेक्स्ट insert करना, लेकिन दोनों शब्दों पर review के लिए special formatting लागू करना
    • Dave के edit को मुख्य टेक्स्ट के बगल में दिखने वाले “suggestion” में बदल देना
    • LLM से पूछना कि टेक्स्ट को कैसे बदला जाए
  • client भी उपयोगकर्ता की मंशा को बेहतर दिखाने वाले operations भेज सकता है
    • insert before का उपयोग paragraph के ऊपर title बनाते समय किया जा सकता है, ताकि पिछले paragraph के अंत पर हुई concurrent insertions के बीच title न घुस जाए
    • fix typo operation में जैसी शर्त रखी जा सकती है: color के उस o के बाद, जिसकी ID X है, u insert करो, लेकिन सिर्फ तब जब आसपास का शब्द अब भी color हो
  • सर्वर ऐसे operations भी परिभाषित कर सकता है जिनकी insertion position सर्वर तक पहुँचने के बाद बदल जाती है
    • एक ही जगह की concurrent insertions को alphabetic order में reorder किया जा सकता है
    • drag-and-drop के लिए move operation जोड़ने पर moved text के भीतर का insert after मूल स्थान के बजाय moved text के भीतर apply किया जा सकता है

rich text formatting को संभालना

  • rich text में bold, font size, hyperlink जैसे inline formats होते हैं
  • range formatting को भी index के बजाय character IDs से व्यक्त किया जा सकता है
    • उदाहरण: ID X से ID Y तक bold लागू करें
    • अगर इसे ID X inclusive से ID Y exclusive तक परिभाषित किया जाए, तो range के अंत पर होने वाली concurrent insertions को भी bold किया जा सकता है
  • ProseMirror जैसे rich text editor के साथ उपयोग करने पर, सर्वर ID X और Y के मौजूदा array indices ढूँढ सकता है और local ProseMirror state को उस range पर bold लागू करने का निर्देश दे सकता है
  • ProseMirror बाद में उस range के भीतर insert होने वाले टेक्स्ट पर भी bold बनाए रख सकता है
    • हालाँकि सर्वर bold set to false जैसी insertion operations के अनुसार इसे अलग तरह से भी संभाल सकता है
  • collaborative rich text semantics को समझने के लिए Peritext essay उपयोगी संदर्भ है

distributed variants और CRDT से संबंध

  • अब तक यह माना गया था कि केंद्रीय सर्वर operations की total order सर्वर receive order से तय करता है और authoritative state अपडेट करता है
  • अगर केंद्रीय सर्वर न हो, या सर्वर वैकल्पिक हो, तो operations को final total order distributed तरीके से दी जा सकती है
    • उदाहरण के लिए Lamport timestamps से operations sort किए जा सकते हैं
    • हर client अब तक प्राप्त operations को क्रम में process करने के परिणाम को authoritative state मान सकता है
  • इस स्थिति में per-character IDs और insert after operations distributed “server-less” reconciliation में भी काम करते हैं
  • तकनीकी रूप से यह परिणाम text editing CRDT बन जाता है
    • क्योंकि यह distributed है और eventual consistency वाला collaborative text editing algorithm है
  • कौन-सी ordering scheme अपनाई जाए, उसके आधार पर इसका संबंध मौजूदा CRDTs से जुड़ता है
    • अगर operations को Lamport timestamps से sort किया जाए, तो resulting list order RGA / Causal Trees के समकक्ष होता है
    • Lamport timestamps के साथ formatting operations जोड़ने पर व्यवहार Peritext से काफ़ी मिलता-जुलता हो जाता है
    • depth-first topological sort इस्तेमाल करने पर resulting list order Fugue के समकक्ष होता है
  • इस equivalence claim का विस्तृत proof नहीं दिया गया है

Articulated: implementation helper लाइब्रेरी

  • वास्तविक implementation में टेक्स्ट स्वयं किसी दूसरी जगह, जैसे ProseMirror state, में store किया जा सकता है, और इस approach के लिए केवल इस प्रकार की ID list पर्याप्त हो सकती है
Array<{ id: ID; isDeleted: boolean }>
  • इस list पर अक्सर चार काम ज़रूरी होते हैं
    • ID और मौजूदा array index के बीच conversion
    • किसी निर्दिष्ट ID के बाद नई ID insert करना
    • किसी ID को deleted mark करना
    • storage के लिए state को serialize और restore करना
  • साधारण array इन कामों के लिए उपयुक्त नहीं है
    • काम 1~3 linear time लेते हैं
    • हर character के लिए object और UUID store करने से memory और storage usage बढ़ता है
  • Articulated एक छोटी npm लाइब्रेरी है जो इस array जैसी functionality देती है
  • इसका मुख्य data structure IdList लोकप्रिय text editing CRDT लाइब्रेरियों जैसी optimizations का उपयोग करता है
    • IDs का रूप { bunchId, counter } है, जहाँ bunchId कई IDs द्वारा साझा किया जाने वाला UUID हो सकता है
    • जब एक ही bunch की IDs साथ-साथ हों, जैसे बाएँ से दाएँ insertion के सामान्य मामले में, तो memory और serialized state में उन्हें एक ही object के रूप में store किया जाता है
    • मुख्य data structure array नहीं बल्कि B+Tree है, इसलिए method calls का समय log या log^2 होता है
  • IdList एक persistent data structure भी है
    • client सर्वर से आख़िरी बार मिली state और optimistic state को सस्ते में साथ रख सकता है
    • remote operations मिलने पर सर्वर की आख़िरी state तक rollback करना आसान होता है
  • अतिरिक्त सामग्री के रूप में docs, शुरुआती demos, और 300 SLOC से कम का सरल implementation IdListSimple उपलब्ध है
  • IdListSimple में optimizations और persistence नहीं हैं, लेकिन functional रूप से यह समान है और fuzz tests से सत्यापित किया गया है

1 टिप्पणियां

 
GN⁺ 2025-05-23
Hacker News की रायें
  • काफी साफ-सुथरा। एल्गोरिथम हर टेक्स्ट कैरेक्टर को UUID जैसी globally unique ID देता है, ताकि लगातार बदलते array index के बजाय समय के साथ उसे consistent तरीके से refer किया जा सके
    क्लाइंट किसी मौजूदा ID को refer करने वाला “insert after” operation सर्वर को भेजता है, और सर्वर target ID ढूंढकर नया कैरेक्टर उसके ठीक बाद insert करता है। Delete में कैरेक्टर को सिर्फ display में hide किया जाता है; “insert after” की position calculate करने के लिए कैरेक्टर रखे जाते हैं। टेक्स्ट editing के बाहर भी, जैसे game world synchronization, इसमें संभावना दिखती है

    • यह सचमुच एक degenerate CRDT है। conflict order को central server से तय कराने का तरीका Google Wave के समय से रहा है
    • मुझे शक है कि यह वाकई इतना नया है या नहीं। distributed system को serialize करने के लिए central process का इस्तेमाल करना लगभग obvious starting point है—जब तक आप network partition और CAP जैसी चीज़ों की चिंता शुरू न करें। अब आपके पास single point of failure भी है। मैंने बस सरसरी तौर पर देखा, पर उत्सुक हूं कि performance की चर्चा थी या नहीं
    • आपने जो बताया, वह CRDT नहीं है क्या?
    • ctrl+a, ctrl+x, ctrl+v करने पर शायद किस्मत का सहारा लेना पड़े
  • ऐसा लेख देखकर अच्छा लगा। कुछ साल पहले मैंने यही तरीका खोजा था, और सोचता था कि यह academic literature में ज्यादा क्यों नहीं दिखता
    हालांकि मैंने इसे decentralized context में CRDT के रूप में implement किया था, ताकि commutativity, idempotence और associativity जैसी properties बनी रहें

    • अगर idea CRDT का विकल्प बनाने का था, तो उसे CRDT बनाकर आपको क्या मिला, यह जानना चाहूंगा
  • dict/map या arbitrary type array जैसी दूसरी data structures पर बात न होना हैरानी की बात थी। अच्छा होगा अगर उन्हें भी आसानी से extend किया जा सके। मेरे अनुभव में apps को pure collaborative text editing की तुलना में collaborative data structures की ज्यादा बार जरूरत होती है
    motivated examples—update validation, partial loading, high-level operations—दिलचस्प हैं, लेकिन यह दावा कमजोर लगता है कि Yjs जैसी चीज़ों में ये features इसलिए नहीं हैं क्योंकि underlying CRDT implementation ऐसा है, या फिर इसलिए कि ये features बनाना शुरू से ही मुश्किल है

    • पूरी तरह सहमत। अगर यह ऐसे “atomic” objects की array हो जिनकी properties बदली नहीं जा सकतीं, तो लगता है कि string को अपनी type से replace कर देना ही काफी होगा। object के अंदर changes ज्यादा tricky होंगे, लेकिन शायद यह tree को efficiently store और traverse करने की समस्या भी हो सकती है
      OP की terminology में कहूं तो मुझे हमेशा लगा है कि helper library के users को हल्का semantic model logic plug in करने में सक्षम होना चाहिए, ताकि invalid states को रोका या manage किया जा सके। उदाहरण के लिए, कोई todo item isDone: true होने के साथ-साथ state: inProgress नहीं हो सकता। यह linked article में rich text formatting semantics की बात से मिलता-जुलता है
    • CRDTs मूल रूप से conflict होने पर एक side को deterministically choose करके काम करते हैं। समस्या यह है कि आम तौर पर यह guarantee नहीं करता कि data loss नहीं होगा या data valid रहेगा
      कल्पना करें कि Git के सभी merge conflicts एक side को अपने-आप चुनकर resolve हो जाएं। ज्यादातर गलत result आएगा, और कभी-कभी code compile भी नहीं होगा। अगर उसी समय उसे ठीक करने वाला कोई न हो, तो और भी confusing परिणाम हो सकते हैं
      इसलिए मुझे लगता है कि CRDTs ज्यादा widely adopted नहीं हुए। CRDTs सिर्फ उस “समस्या” को ठीक करते हैं जो उन्हें लगी थी, लेकिन असली समस्या—data, validity और meaning को preserve करते हुए conflict resolution—को हल नहीं करते। बल्कि conflict resolution को सिर्फ deterministically replicable तरीकों तक सीमित करके वे इस समस्या को और खराब भी कर सकते हैं
  • CRDT से मुख्य फर्क यह लगता है कि अगर central server है, तो synchronization—यानी concurrent events के बीच order तय करना—data structure itself से lexicographic order में न कराकर server से कराया जाए
    चूंकि सारा communication सिर्फ client-server के बीच होता है, clients के बीच नहीं, इसलिए जब कोई client server से connect होता है, server यह guarantee कर सकता है कि नए remote updates भेजने से पहले उस client के सभी local operations process कर दे

  • क्या इस लेख का core message यह है कि CRDT/OT की पूरी complexity सिर्फ तब चाहिए जब central server न हो?

    • central server न होने पर भी, अगर operations को आखिरकार total order में sort करके उसी order में apply करने का decentralized तरीका हो, तो CRDT/OT की complexity से बचा जा सकता है: https://mattweidner.com/2025/05/21/text-without-crdts.html#d...
      जैसा दूसरे comments कह रहे हैं, technically यह भी CRDT है, और काफी general form है। साथ ही operations को undo और replay करने की implementation भी आसान नहीं है। फिर भी उम्मीद है कि हर data type के लिए traditional CRDT/OT इस्तेमाल करने से यह सरल होगा
    • यही CRDT का core है। एक ही data structure की कई replicas कई nodes पर maintain होती हैं, हर replica independent तरीके से update होती है, और आखिरकार सभी converge करती हैं
    • OT को central server चाहिए
  • मैं इस field का expert नहीं हूं, लेकिन Automerge जैसे CRDTs से मुख्य अंतर server coordination लगता है। उदाहरण के लिए यह लेख [1] देखें: Automerge concurrent insertions को handle करते समय sequence numbers का इस्तेमाल करता है, और अगर insertions simultaneously हों तो agreed agent ID order पर निर्भर करता है। इसके उलट यह तरीका server द्वारा arrival order में processing पर निर्भर करता है
    लेख में लिखा है: “text editing CRDTs में कोई fancy algorithm ID order तय करता है। वही ordering algorithm अलग-अलग text editing CRDTs के बीच फर्क बनाता है, और CRDT papers का जटिल हिस्सा है। हम उसे पूरी तरह avoid करते हैं।” कई apps में वैसे भी central server होता है, इसलिए “fancy algorithm” से बचने की बात समझ आती है। लेकिन server coordination में local edits को undo और replay करना पड़ता है, इसलिए यह सचमुच बहुत ज्यादा सरल है या नहीं, इस पर 100% भरोसा करना मुश्किल है। [1] https://josephg.com/blog/crdts-go-brrr/

    • सहमत हूं कि undo और replay भी खास सरल नहीं हैं। persistent B+Tree भी कोई बहुत simple चीज नहीं है
    • मेरी समझ में Automerge internally सभी operations को आखिरकार एक consistent total order में store करता है, और server coordination में इसे server के substitute के तौर पर इस्तेमाल किया जा सकता है: https://mattweidner.com/2025/05/21/text-without-crdts.html#d...
      लेकिन Automerge असल में ऐसा नहीं करता, और text operations को traditional CRDT RGA से handle करता है। शायद, जैसा आपने कहा, operations का undo और replay implement करना आसान नहीं है
  • तो यानी यह unoptimized CRDT है? maximum set size को 1 रखकर बस आगे बढ़ जाने जैसा?

    • यह किसी तरह की irreducible complexity जैसा लगता है, इसलिए आकर्षक है। वास्तव में जो होता है, उसके करीब और सरल। जैसा आपने कहा, optimization तो शायद नहीं है
  • server coordination इस्तेमाल करने की वजह से client-side coordination मुश्किल लगती है। server updates आते ही उन्हें apply करते हुए smooth editor UX कैसे बनाए रखेंगे?
    उदाहरण के लिए, अगर client द्वारा भेजी गई character insertion request fail हो जाए तो क्या बस retry करेंगे? इस बीच updates आ गए हों तो? Edit: “Client-Side” section में इस case को acknowledge किया गया है, rewind के बाद replay suggest किया गया है, और एक simpler option के रूप में pending queue खाली होने तक block करने का तरीका भी सुझाया गया है। frontend perspective से unspecified UI/UX exceptions लंबी tail बना सकते हैं, इसलिए कुल मिलाकर CRDT ज्यादा simple भी लग सकता है। और connection बार-बार टूटने वाली New York subway में editing feel कैसी होगी, यह भी जानना चाहूंगा

    • ProseMirror और modern CodeMirror के पास इस समस्या का काफी elegant solution है। वे document के हर change को node/text identifiers नहीं, बल्कि index track करने वाले steps के रूप में model करते हैं, और “position maps” नाम की data structure का इस्तेमाल करके buffered steps को नई positions पर map करने के बाद document पर apply करते हैं
      असल में यह काफी अच्छी तरह काम करता है। details यहां हैं:
      https://marijnhaverbeke.nl/blog/collaborative-editing.html
      https://marijnhaverbeke.nl/blog/collaborative-editing-cm.htm...