Computer >> कंप्यूटर >  >> प्रोग्रामिंग >> Ruby

अद्वितीय आईडी के बिना डेटाबेस के लिए ActiveRecord

कभी-कभी अनोखी स्थितियां और हमारे नियंत्रण से बाहर की चीजें बेतहाशा अपरंपरागत आवश्यकताओं की ओर ले जाती हैं। हाल ही में, मेरे पास एक अनुभव था जहां मुझे किसी भी रिकॉर्ड के लिए डेटाबेस आईडी पर भरोसा किए बिना ActiveRecord का उपयोग करने की आवश्यकता थी। अगर कोई ऐसा करने पर विचार कर रहा है, तो मैं अत्यधिक दूसरा तरीका खोजने की सलाह देता हूं! लेकिन, चलिए बाकी की कहानी पर चलते हैं।

निर्णय किए गए। छोटे डेटाबेस (संरचना में क्लोन लेकिन डेटा में नहीं) को मर्ज करने की आवश्यकता है। मैं प्रोजेक्ट में उसी तरह शामिल हुआ जैसे टीम एक स्क्रिप्ट पर अंतिम रूप दे रही थी जो डेटाबेस रिकॉर्ड को एक डेटाबेस से दूसरे डेटाबेस में कॉपी और पेस्ट करती है। इसने आईडी सहित, सब कुछ ठीक वैसा ही कॉपी किया जैसा है।

डेटाबेस A

<थ>फल
आईडी user_id
... ... ...
123 नारंगी 456
... ... ...

डेटाबेस बी

<थ>फल
आईडी user_id
... ... ...
123 केला 74
... ... ...

डेटाबेस ए मर्ज के बाद

<थ>फल
आईडी user_id
... ... ...
123 नारंगी 456
123 केला 74
... ... ...

यह आईडी होने के मूल कारण को तोड़ता है:विशिष्ट पहचान। मैं विशिष्टताओं को नहीं जानता था, लेकिन मुझे लगा कि सिस्टम में डुप्लीकेट आईडी पेश किए जाने के बाद सभी प्रकार की समस्याएं दिखाई देंगी। मैंने कुछ कहने की कोशिश की, लेकिन मैं इस परियोजना के लिए नया था, और दूसरों को यकीन था कि यह आगे का सबसे अच्छा रास्ता था। कुछ दिनों में, हम कोड को लागू करने जा रहे थे और डुप्लिकेट आईडी के साथ डेटा को संभालना शुरू कर दिया था। अब सवाल यह नहीं था, "क्या हमें यह करना चाहिए?"; इसके बजाय, सवाल थे, "हम यह कैसे करते हैं?" और "इसमें कितना समय लगेगा?"

डुप्लिकेट आईडी के साथ कार्य करना

तो, आप डुप्लीकेट आईडी वाले डेटा को कैसे हैंडल करते हैं? समाधान कई क्षेत्रों की एक समग्र आईडी बनाना था। हमारे अधिकांश डीबी फ़ेच इस तरह दिखते थे:

# This doesn't work, there may be 2 users with id: 123
FavoriteFruit.find(123)

# Multiple IDs scope the query to the correct record
FavoriteFruit.find_by(id: 123, user_id: 456)

सभी ActiveRecord कॉल इस तरह से अपडेट किए गए थे, और जैसा कि मैंने कोड के माध्यम से देखा, यह समझ में आया। जब तक हम इसे तैनात नहीं करते।

सभी नरक टूट जाते हैं

कोड लागू करने के कुछ ही समय बाद, फोन बजने लगे। ग्राहक ऐसे नंबर देख रहे थे जो जुड़ नहीं रहे थे। वे अपने स्वयं के रिकॉर्ड अपडेट नहीं कर सके। सभी प्रकार की सुविधाएँ टूट रही थीं।

क्या करे? हमने केवल कोड परिनियोजित नहीं किया; हमने डेटा को एक डेटाबेस से दूसरे डेटाबेस में भी स्थानांतरित किया (और हमारे द्वारा तैनात किए जाने के बाद नया डेटा बनाया/अपडेट किया गया)। यह एक साधारण रोलबैक स्थिति नहीं थी। हमें चीजों को तेजी से ठीक करने की जरूरत थी।

रेल क्या कर रही है?

डिबगिंग में पहला कदम यह देखना था कि वर्तमान व्यवहार क्या था और त्रुटि को कैसे पुन:उत्पन्न किया जाए। मैंने उत्पादन डेटा का क्लोन लिया और रेल कंसोल शुरू किया। आपके सेटअप के आधार पर, जब आप ActiveRecord क्वेरी निष्पादित करते हैं, तो आप स्वचालित रूप से SQL क्वेरी को रेल नहीं देख सकते हैं। यह सुनिश्चित करने का तरीका यहां बताया गया है कि SQL कथन आपके कंसोल पर दिखाई दे रहे हैं:

ActiveRecord::Base.logger = Logger.new(STDOUT)

उसके बाद, मैंने कुछ सामान्य रेल प्रश्नों की कोशिश की:

$ FavoriteFruit.find_by(id: 123, user_id: 456)

FavoriteFruit Load (0.6ms)
SELECT  "favorite_fruits".*
FROM "favorite_fruits"
WHERE "favorite_fruits"."id" = $1
AND "favorite_fruits"."user_id" = $2
[["id", "123"], ["user_id", "456"]]

find_by ठीक काम करने लग रहा था, लेकिन फिर मैंने कुछ इस तरह का कोड देखा:

fruit = FavoriteFruit.find_by(id: 123, user_id: 456)
...
...
fruit.reload

वह reload मुझे जिज्ञासु बनाया, इसलिए मैंने उसका भी परीक्षण किया:

$ fruit.reload

FavoriteFruit Load (0.3ms)
SELECT  "favorite_fruits".*
FROM "favorite_fruits"
WHERE "favorite_fruits"."id" = $1
LIMIT $2
[["id", 123], ["LIMIT", 1]]

उह ओह। इसलिए, भले ही हमने शुरू में find_by . के साथ सही रिकॉर्ड प्राप्त किया हो , जब भी हम reload . कहते हैं , यह रिकॉर्ड की आईडी लेगा और एक साधारण खोज-दर-आईडी क्वेरी करेगा, जो निश्चित रूप से, हमारे डुप्लिकेट आईडी के कारण अक्सर गलत डेटा देगा।

ऐसा क्यों किया? मैंने सुराग के लिए रेल स्रोत कोड की जांच की। यह रूबी ऑन रेल्स के साथ कोडिंग का एक बड़ा पहलू है, स्रोत कोड सादा रूबी है और एक्सेस के लिए स्वतंत्र रूप से उपलब्ध है। मैंने बस "ActiveRecord पुनः लोड" को गुगल किया और जल्दी से यह पाया:

# File activerecord/lib/active_record/persistence.rb, line 602
def reload(options = nil)
  self.class.connection.clear_query_cache

  fresh_object =
    if options && options[:lock]
      self.class.unscoped { self.class.lock(options[:lock]).find(id) }
    else
      self.class.unscoped { self.class.find(id) }
    end

  @attributes = fresh_object.instance_variable_get("@attributes")
  @new_record = false
  self
end

यह दर्शाता है कि reload कमोबेश, self.class.find(id) . के लिए एक आवरण है . इस पद्धति में केवल एक आईडी द्वारा क्वेरी करना हार्डवायर्ड था। डुप्लिकेट आईडी के साथ काम करने के लिए, हमें या तो कोर रेल विधियों को ओवरराइड करना होगा (कभी अनुशंसित नहीं) या reload का उपयोग करना बंद करना होगा पूरी तरह से।

हमारा समाधान

इसलिए, हमने प्रत्येक reload . के माध्यम से जाने का निर्णय लिया कोड में और इसे find_by . में बदलें डेटाबेस को एकाधिक कुंजियों के माध्यम से लाने के लिए।

हालाँकि, यह केवल कुछ बगों का समाधान था। अधिक खुदाई के बाद, मैंने अपने update . का परीक्षण करने का निर्णय लिया कॉल:

$ fruit = FavoriteFruit.find_by(id: 123, user_id: 456)
$ fruit.update(last_eaten: Time.now)

FavoriteFruit Update (43.3ms)
UPDATE "favorite_fruits"
SET "last_eaten" = $1
WHERE "favorite_fruits"."id" = $2
[["updated_at", "2020-04-16 06:24:57.989195"], ["id", 123]]

उह ओह। आप देख सकते हैं कि भले ही find_by जब हमने update . को कॉल किया, तो विशिष्ट क्षेत्रों द्वारा रिकॉर्ड को स्कोप किया रेल रिकॉर्ड पर, इसने एक साधारण WHERE id = x . बनाया क्वेरी, जो डुप्लिकेट आईडी के साथ भी टूट जाती है। हम इसके आसपास कैसे पहुंचे?

हमने एक कस्टम अपडेट विधि बनाई है, update_unique , जो इस तरह दिखता है:

class FavoriteFruit
  def update_unique(attributes)
    run_callbacks :save do
      self.class
        .where(id: id, user_id: user_id)
        .update_all(attributes)
    end
    self.class.find_by(id: id, user_id: user_id)
  end
end

जो हमें आईडी से अधिक के दायरे में रिकॉर्ड अपडेट करने देता है:

$ fruit.update_unique(last_eaten: Time.now)

FavoriteFruit Update All (3.2ms)
UPDATE "favorite_fruits"
SET "last_eaten" = '2020-04-16 06:24:57.989195'
WHERE "favorite_fruits"."id" = $1
AND "favorite_fruits"."user_id" = $2
[["id", "123"], ["user_id", "456"]]

इस कोड ने रिकॉर्ड्स को अपडेट करने की एक सीमित गुंजाइश सुनिश्चित की, लेकिन क्लास के update_all को कॉल करके विधि, हमने कॉलबैक खो दिया है जो आम तौर पर एक रिकॉर्ड को अपडेट करने के साथ आता है। इसलिए, हमें मैन्युअल रूप से कॉलबैक चलाना पड़ा और update_all के बाद से अपडेट किए गए रिकॉर्ड को पुनः प्राप्त करने के लिए एक और डेटाबेस कॉल करना पड़ा। अद्यतन रिकॉर्ड वापस नहीं करता है। अंतिम उत्पाद भी नहीं है गन्दा, लेकिन यह निश्चित रूप से fruit.update . की तुलना में पढ़ना अधिक कठिन है ।

असली समाधान

धँसी हुई लागत, प्रबंधन और समय की कमी के कारण, हमारा समाधान सभी डेटाबेस कॉल के लिए कई कुंजियों का उपयोग करने के लिए मंकी पैच रेल्स का था। इसने काम किया, इस अर्थ में कि ग्राहक अभी भी उत्पाद खरीदेंगे और उसका उपयोग करेंगे, लेकिन यह कई कारणों से एक बुरा विचार था:

  • भविष्य में कोई भी विकास सामान्य रेल विधियों का उपयोग करके अनजाने में बग को फिर से प्रस्तुत कर सकता है। नए डेवलपर्स को कोड को छिपे हुए बग से मुक्त रखने के लिए सख्त प्रशिक्षण की आवश्यकता होगी, जैसे कि reload . का उपयोग करना विधि।
  • कोड अधिक जटिल, कम स्पष्ट और कम रखरखाव योग्य है। यह तकनीकी ऋण है जो परियोजना के आगे बढ़ने के साथ-साथ विकास की गति को धीमा कर देता है।
  • परीक्षण बहुत धीमा हो जाता है। आपको न केवल यह जांचने की आवश्यकता है कि कोई फ़ंक्शन काम करता है बल्कि यह भी काम करता है कि जब विभिन्न ऑब्जेक्ट्स में डुप्लिकेट आईडी हों। परीक्षण लिखने में अधिक समय लगता है, और फिर हर बार जब परीक्षण सूट चलाया जाता है, तो सभी अतिरिक्त परीक्षणों को चलाने में अधिक समय लगता है। यदि प्रोजेक्ट का प्रत्येक डेवलपर सभी संभावित परिदृश्यों का सावधानीपूर्वक परीक्षण नहीं करता है, तो परीक्षण में आसानी से बग छूट सकते हैं।

इस समस्या का असली समाधान यह है कि पहली बार में कभी भी डुप्लीकेट आईडी न हों। यदि डेटा को एक डेटाबेस से दूसरे डेटाबेस में स्थानांतरित करने की आवश्यकता है, तो ऐसा करने वाली स्क्रिप्ट को आईडी के बिना डेटा एकत्र करना और सम्मिलित करना चाहिए, जिससे प्राप्त करने वाले डेटाबेस को प्रत्येक रिकॉर्ड को अपनी विशिष्ट आईडी देने के लिए अपने मानकीकृत ऑटो-इंक्रीमेंट काउंटर का उपयोग करने की अनुमति मिलती है।

एक अन्य समाधान सभी रिकॉर्ड के लिए यूयूआईडी का उपयोग करना होगा। इस प्रकार की आईडी यादृच्छिक रूप से बनाए गए वर्णों की एक लंबी स्ट्रिंग है (चरण-दर-चरण गणना के बजाय, एक पूर्णांक आईडी के साथ)। फिर, डेटा को अन्य डेटाबेस में ले जाने से कोई विरोध या समस्या नहीं होगी।

लब्बोलुआब यह है कि रेल को इस समझ के साथ बनाया गया था कि आईडी प्रति रिकॉर्ड अद्वितीय हैं और डेटाबेस में विशिष्ट डेटा में हेरफेर करने का एक त्वरित और आसान तरीका है। रेल एक विचारित ढांचा है, और इसकी सुंदरता यह है कि जब तक आप चीजों को करने के रेल तरीके से चिपके रहते हैं, तब तक सब कुछ कितनी आसानी से चलता है। यह न केवल रेल पर बल्कि प्रोग्रामिंग के कई अन्य पहलुओं पर भी लागू होता है। जब चीजें जटिल हो जाती हैं, तो हमें पता होना चाहिए कि समस्या की पहचान कैसे करें; हालांकि, अगर हम स्पष्ट, अनुरक्षणीय और पारंपरिक कोड लिखते हैं, तो हम इनमें से कई जटिलताओं से बच सकते हैं।


  1. Next.js के लिए सर्वश्रेष्ठ डेटाबेस

    Next.js डेवलपर्स को सर्वर साइड रेंडरिंग क्षमता के साथ पूर्ण स्टैक एप्लिकेशन बनाने में सक्षम बनाता है। Vercel और Netlify सर्वर रहित कार्यों के साथ बैकएंड API लिखने में उनकी मदद करते हैं। तो अगला सवाल यह है कि Next.js ऐप्स के लिए आदर्श डेटाबेस क्या है। इस पोस्ट में, मैं उन डेटाबेस की समीक्षा करूँगा जो

  1. Microsoft Edge पाठकों के लिए अनूठी विशेषताएं

    माइक्रोसॉफ्ट एज विंडोज पर एक इन-बिल्ट वेब ब्राउजर है जिसे 2015 में लॉन्च किया गया था। तब से, माइक्रोसॉफ्ट इसमें फीचर्स जोड़कर इसे बेहतर बनाने पर काम कर रहा है। बड़े पैमाने पर लोकप्रिय वेब ब्राउज़र के रूप में Google Chrome के साथ, Edge बनाए रखने की कोशिश कर रहा है। आज हम चर्चा करेंगे कि माइक्रोसॉफ्ट

  1. Windows 7 को Windows 11 में मुफ़्त में कैसे अपग्रेड करें (बिना डेटा हानि के)

    यदि आप अभी भी विंडोज 7 का उपयोग कर रहे हैं, तो यह नवीनतम विंडोज 11 में अपग्रेड करने का समय है, क्योंकि माइक्रोसॉफ्ट ने विंडोज 7 के लिए समर्थन समाप्त कर दिया है। तो आपके मन में एक सवाल हो सकता है, क्या विंडोज़ 7 मुफ्त में विंडोज़ 11 में अपग्रेड कर सकता है ? और इसका उत्तर हां है लेकिन पहले, आपको wind