RESP മനസ്സിലാക്കാം: Redis-ന് പിന്നിലെ പ്രോട്ടോക്കോൾ

എന്റെ മുൻപത്തെ പോസ്റ്റിൽ, Node.js ഉപയോഗിച്ച് നിർമ്മിച്ച എന്റെ Redis ക്ലോണിനെക്കുറിച്ചുള്ള ഒരു അവലോകനം ഞാൻ പങ്കുവെച്ചിരുന്നു. ഇപ്പോൾ, ഈ പ്രോജക്റ്റിന്റെ ഒരു നിർണ്ണായക ഭാഗം വിവരിക്കാൻ ഞാൻ ആഗ്രഹിക്കുന്നു: RESP, അതായത് Redis Serialization Protocol.

Redis-ന് നേരിട്ട് JavaScript അറേകൾ ലഭിക്കുന്നില്ല. ഒരു TCP സോക്കറ്റ് വഴി ലഭിക്കുന്ന റോ (raw) ബൈറ്റുകളാണ് ഇതിന് ലഭിക്കുന്നത്. ഇവ പ്രവർത്തിക്കുന്നതിന് ഒരു ഘടന (structure) ആവശ്യമാണ്. ഒരു കമാൻഡ് എവിടെ തുടങ്ങുന്നു എന്നും എവിടെ അവസാനിക്കുന്നു എന്നും സെർവറിന് അറിയണം. എത്ര ആർഗ്യുമെന്റുകൾ (arguments) ഉണ്ട് എന്നും ഓരോന്നിന്റെയും നീളം എത്രയാണെന്നും സെർവർ അറിഞ്ഞിരിക്കണം.

RESP ഈ ഘടന നൽകുന്നു. ക്ലയന്റുകളും സെർവറുകളും തമ്മിൽ ആശയവിനിമയം നടത്താൻ ഉപയോഗിക്കുന്ന ഭാഷയാണിത്.

ഉദാഹരണത്തിന്, RESP-യിൽ GET name എന്ന കമാൻഡ് ഇപ്രകാരമാണ് കാണപ്പെടുന്നത്: *2\r\n$3\r\nGET\r\n$4\r\nname\r\n

ഇതിന്റെ വിശദീകരണം താഴെ നൽകുന്നു:

  • *2 എന്നാൽ 2 എലമെന്റുകളുള്ള ഒരു അറേ എന്നാണ് അർത്ഥം.
  • $3 എന്നാൽ അടുത്ത ഭാഗം 3 ബൈറ്റുകൾ നീളമുള്ളതാണ് എന്നാണ് അർത്ഥം: GET.
  • $4 എന്നാൽ അടുത്ത ഭാഗം 4 ബൈറ്റുകൾ നീളമുള്ളതാണ് എന്നാണ് അർത്ഥം: name.

പാർസർ (parser) ഈ ബൈറ്റുകളെ ["GET", "name"] എന്നാക്കി മാറ്റുന്നു. ഇപ്പോൾ എഞ്ചിന് കമാൻഡ് പ്രവർത്തിപ്പിക്കാൻ കഴിയും.

എന്തുകൊണ്ട് വെറുതെ സ്പേസ് (space) ഉപയോഗിച്ച് സ്ട്രിംഗുകളെ വിഭജിച്ചില്ല? SET message "hello world" എന്നതുപോലെ സ്പേസ് അടങ്ങിയ വാല്യൂകൾ വരുമ്പോൾ ലളിതമായ വിഭജനം പരാജയപ്പെടും. ബൈനറി ഡാറ്റയുടെ കാര്യത്തിലും ഇത് പരാജയപ്പെടും. ഓരോ വാല്യൂവിന്റെയും നീളം (length) ഉൾപ്പെടുത്തുന്നതിലൂടെ RESP ഈ പ്രശ്നം പരിഹരിക്കുന്നു. നിർദ്ദേശിക്കപ്പെട്ട കൃത്യമായ ബൈറ്റുകളുടെ എണ്ണം മാത്രമേ സെർവർ വായിക്കുന്നുള്ളൂ. ഇത് ഇതിനെ ബൈനറി-സുരക്ഷിതവും (binary-safe) വിശ്വസനീയവുമാക്കുന്നു.

TCP എന്നത് മെസേജ് അടിസ്ഥാനത്തിലുള്ളതല്ല (message-based), മറിച്ച് സ്ട്രീം അടിസ്ഥാനത്തിലുള്ളതാണ് (stream-based) എന്ന് മനസ്സിലാക്കിയതായിരുന്നു ഏറ്റവും പ്രയാസകരമായ പാഠം. ഒരു സെർവറിന് എപ്പോഴും ഒരേസമയം ഒരു പൂർണ്ണമായ കമാൻഡ് തന്നെ ലഭിക്കണമെന്നില്ല. ഒരു കമാൻഡ് രണ്ട് വ്യത്യസ്ത ഭാഗങ്ങളായി (chunks) വന്നേക്കാം. അല്ലെങ്കിൽ, ഒന്നിലധികം കമാൻഡുകൾ ഒരൊറ്റ ഭാഗമായി വന്നേക്കാം.

ഇതിനർത്ഥം ഒരു ഡാറ്റാ ഇവന്റ് എന്നാൽ ഒരു കമാൻഡ് എന്ന് പാർസർക്ക് കരുതാൻ കഴിയില്ല എന്നാണ്. പാർസർ ഒരു ഇന്റേണൽ ബഫർ (internal buffer) ഉപയോഗിക്കണം.

എന്റെ RESP പാർസർ ഇപ്രകാരമാണ് പ്രവർത്തിക്കുന്നത്:

  1. സോക്കറ്റിൽ നിന്ന് ബൈറ്റുകൾ സ്വീകരിക്കുക.
  2. ബൈറ്റുകൾ ഒരു ബഫറിലേക്ക് ചേർക്കുക.
  3. ഒരു പൂർണ്ണമായ കമാൻഡ് പാർസ് ചെയ്യാൻ ശ്രമിക്കുക.
  4. കമാൻഡ് അപൂർണ്ണമാണെങ്കിൽ, കൂടുതൽ ഡാറ്റയ്ക്കായി കാത്തിരിക്കുക.
  5. കമാൻഡ് പൂർണ്ണമാണെങ്കിൽ, അത് റിട്ടേൺ ചെയ്യുക.
  6. അധിക ഡാറ്റ അവശേഷിക്കുന്നുണ്ടെങ്കിൽ, അടുത്ത പാർസിംഗിനായി അത് ബഫറിൽ സൂക്ഷിക്കുക.

ഒരു ഡാറ്റാബേസ് പ്രോട്ടോക്കോൾ ലെയറിൽ (protocol layer) നിന്നാണ് ആരംഭിക്കുന്നത്. Redis ഡാറ്റ സംഭരിക്കുന്നതിന് മുമ്പ്, അതിന് കമാൻഡ് മനസ്സിലാക്കേണ്ടതുണ്ട്. ഒരു വാല്യൂ തിരികെ നൽകുന്നതിന് മുമ്പ്, അത് റെസ്‌പോൺസ് എൻകോഡ് (encode) ചെയ്യണം.

RESP നടപ്പിലാക്കിയത് ഒരു ലളിതമായ TCP സെർവറിനെ യഥാർത്ഥത്തിൽ Redis-ന് അനുയോജ്യമായ ഒരു സിസ്റ്റമാക്കി മാറ്റി. ബാക്കെൻഡ് സിസ്റ്റങ്ങളെക്കുറിച്ചുള്ള എന്റെ കാഴ്ചപ്പാട് ഇത് മാറ്റിമറിച്ചു. ഒരു പ്രോട്ടോക്കോൾ എന്നത് വെറുമൊരു ഫോർമാറ്റ് മാത്രമല്ല. അത് രണ്ട് സിസ്റ്റങ്ങൾ തമ്മിലുള്ള ഒരു കരാറാണ് (contract).

എന്റെ അടുത്ത പോസ്റ്റിൽ, ഇൻ-മെമ്മറി സ്റ്റോറേജ് ലെയറിനെക്കുറിച്ച് (in-memory storage layer) ഞാൻ വിവരിക്കും.

റെപ്പോസിറ്ററി: https://github.com/Abhinov007/redis_clone ലൈവ് സാൻഡ്‌ബോക്സ്: https://abhinov007.github.io/Redis_Clone/ സ്രോതസ്സ്: https://dev.to/abhinov007/understanding-resp-the-protocol-behind-redis-50p4