നിങ്ങളുടെ SQL വേഗതയുള്ളതാണ്, പക്ഷേ API സാവധാനത്തിലാണ്

നിങ്ങളുടെ ഡാറ്റാബേസ് ക്വറികൾ വേഗതയുള്ളതാണ്. നിങ്ങളുടെ കാഷിംഗ് (caching) കൃത്യമായി പ്രവർത്തിക്കുന്നു. നിങ്ങളുടെ ബാക്ക്ഗ്രൗണ്ട് ജോബുകൾ (background jobs) കുഴപ്പമില്ലാതെ നടക്കുന്നു.

എന്നിട്ടും, നിങ്ങളുടെ API ഇപ്പോഴും സാവധാനത്തിലാണ്. നിങ്ങളുടെ CPU ഉപയോഗം കൂടുതലാണ്.

പ്രശ്നം നിങ്ങളുടെ ഡാറ്റാബേസല്ല. പ്രശ്നം റൂബി (Ruby) ലെയറിലാണ്.

ഡാറ്റ ഡാറ്റാബേസിൽ നിന്ന് പുറത്തുവന്ന് നിങ്ങളുടെ ആപ്ലിക്കേഷനിലേക്ക് പ്രവേശിക്കുന്ന ഘട്ടത്തിലാണ് പലപ്പോഴും തടസ്സങ്ങൾ (bottleneck) ഉണ്ടാകുന്നത്. പ്രധാനമായും മൂന്ന് കാരണങ്ങളാലാണ് ഇത് സംഭവിക്കുന്നത്:

  • അമിതമായ സെരിയലൈസേഷൻ (Bloated serialization)
  • അമിതമായ ഒബ്ജക്റ്റ് അലോക്കേഷൻ (Excessive object allocation)
  • ആവർത്തിച്ചുള്ള കമ്പ്യൂട്ടേഷൻ (Repeated computation)

ഇവ എങ്ങനെ പരിഹരിക്കാം എന്ന് താഴെ നൽകുന്നു.

1. അമിതമായ സെരിയലൈസേഷൻ ഒഴിവാക്കുക

പല ഡെവലപ്പർമാരും മുഴുവൻ മോഡലുകളെയും JSON ആക്കി മാറ്റാറുണ്ട്.

render json: @shipments

ഒരു ഷിപ്‌മെന്റിന് 40 കോളങ്ങൾ ഉണ്ടാവുകയും എന്നാൽ നിങ്ങളുടെ ഫ്രണ്ട്‌എൻഡിന് (frontend) 5 എണ്ണം മാത്രം ആവശ്യമുള്ളതാവുകയും ചെയ്താൽ, നിങ്ങൾ അനാവശ്യമായി CPU സൈക്കിളുകൾ പാഴാക്കുന്നു. കൂടാതെ API കീകൾ അല്ലെങ്കിൽ ചിലവുകൾ പോലുള്ള സ്വകാര്യ വിവരങ്ങൾ ചോരാനും ഇത് കാരണമായേക്കാം.

പരിഹാരം: നിങ്ങൾക്ക് ആവശ്യമുള്ള ഫീൽഡുകൾ മാത്രം തിരികെ നൽകുക.

render json: @shipments.as_json(only: [:id, :tracking_no, :status])

കൂടുതൽ വേഗത ലഭിക്കുന്നതിനായി, ഡാറ്റ അറേകളായി (arrays) എടുക്കാൻ pluck ഉപയോഗിക്കുക. ഇത് ഭാരമേറിയ ActiveRecord ഒബ്ജക്റ്റുകൾ നിർമ്മിക്കുന്നത് പൂർണ്ണമായും ഒഴിവാക്കുന്നു.

2. ഒബ്ജക്റ്റ് അലോക്കേഷൻ കുറയ്ക്കുക

റൂബി നിർമ്മിക്കുന്ന ഓരോ ഒബ്ജക്റ്റും മെമ്മറി ഉപയോഗിക്കുന്നു. ഒരു സിംഗിൾ റിക്വസ്റ്റ് സമയത്ത് ആയിരക്കണക്കിന് ഒബ്ജക്റ്റുകൾ നിർമ്മിക്കുന്നത് ഗാർബേജ് കളക്ടർ (Garbage Collector - GC) കൂടുതൽ കഠിനമായി പ്രവർത്തിക്കാൻ പ്രേരിപ്പിക്കുന്നു. ഇത് നിങ്ങളുടെ സിസ്റ്റം മുഴുവൻ സാവധാനത്തിലാക്കുന്നു.

ലൂപ്പുകൾക്കുള്ളിൽ (loops) പുതിയ ഹാഷുകളോ (hashes) സ്ട്രിംഗുകളോ (strings) നിർമ്മിക്കുന്നത് ഒഴിവാക്കുക.

മോശം രീതി:

@shipments.map do |s|
  { label: "#{s.tracking_no} - #{s.status.upcase}" }
end

നല്ല രീതി: സ്റ്റാറ്റിക് ഡാറ്റ ലൂപ്പിന് പുറത്തേക്ക് മാറ്റുക. റൂബിക്ക് പകരം ഡാറ്റാബേസിൽ കൂടുതൽ കാര്യങ്ങൾ ചെയ്യാൻ ശ്രമിക്കുക.

3. ആവർത്തിച്ചുള്ള കമ്പ്യൂട്ടേഷൻ ഒഴിവാക്കുക

ഒരു റിക്വസ്റ്റിൽ ഒരേ മെത്തേഡ് തന്നെ പലതവണ വിളിച്ചാൽ നിങ്ങൾ സമയം പാഴാക്കുന്നു.

ഉദാഹരണം:

def total_weight
  shipments.sum(&:weight)
end

നിങ്ങളുടെ വ്യൂ (view), ഹെൽപ്പർ (helper), സെരിയലൈസർ (serializer) എന്നിവയെല്ലാം ഇത് വിളിക്കുന്നുണ്ടെങ്കിൽ, നിങ്ങൾ മൂന്ന് തവണ തുക കണക്കാക്കുന്നു.

പരിഹാരം: മെമ്മോയിസേഷൻ (memoization) ഉപയോഗിക്കുക.

def total_weight
  @total_weight ||= shipments.sum(&:weight)
end

ഇത് ഓരോ റിക്വസ്റ്റിലും കണക്കുകൂട്ടലുകൾ ഒരു തവണ മാത്രം നടക്കുന്നുവെന്ന് ഉറപ്പാക്കുന്നു.

സംഗ്രഹം പട്ടിക:

പ്രശ്നം പരിഹാരം
അമിതമായ സെരിയലൈസേഷൻ (Bloated serialization) ആവശ്യമുള്ള ഫീൽഡുകൾ മാത്രം നൽകുക അല്ലെങ്കിൽ pluck ഉപയോഗിക്കുക.
ഉയർന്ന അലോക്കേഷൻ (High allocation) ലൂപ്പുകളിൽ കുറഞ്ഞ അളവിൽ ഒബ്ജക്റ്റുകൾ നിർമ്മിക്കുക.
ആവർത്തിച്ചുള്ള കമ്പ്യൂട്ടേഷൻ (Repeated computation) ഫലങ്ങൾ വീണ്ടും ഉപയോഗിക്കാൻ മെമ്മോയിസേഷൻ ഉപയോഗിക്കുക.

ഡാറ്റാബേസ് ഒപ്റ്റിമൈസേഷൻ എന്നാൽ കുറഞ്ഞ അളവിൽ ഡാറ്റ ആവശ്യപ്പെടുക എന്നാണ് അർത്ഥമാക്കുന്നത്. ആപ്ലിക്കേഷൻ ഒപ്റ്റിമൈസേഷൻ എന്നാൽ ഡാറ്റ ലഭിച്ചുകഴിഞ്ഞാൽ അനാവശ്യമായ ജോലികൾ കുറയ്ക്കുക എന്നാണ് അർത്ഥമാക്കുന്നത്.

Source: https://dev.to/danewu/your-sql-is-fast-but-the-api-is-slow-its-the-ruby-layer-2fno