Safari-യുടെ JavaScript എഞ്ചിനിൽ ഒരു മറഞ്ഞിരിക്കുന്ന പിശക് ഉണ്ട്: ഒരു മോഡ്യൂൾ ശൈലിയിലുള്ള (module-style) Web Worker-ന്റെ എൻട്രി സ്ക്രിപ്റ്റ് ബണ്ടിലിലെ (bundle) മറ്റെവിടെയെങ്കിലും ഇംപോർട്ട് ചെയ്യപ്പെട്ടാൽ, Safari ആ എൻട്രി സ്ക്രിപ്റ്റ് രണ്ടാമതൊന്ന് പ്രവർത്തിപ്പിക്കുന്നു. ഈ ഡ്യൂപ്ലിക്കേറ്റ് റൺ സിംഗിൾട്ടൺ സ്റ്റേറ്റിനെ (singleton state) തകരാറിലാക്കുകയും, ഷെയർഡ് കാഷെകളിലോ (shared caches) സിംഗിൾ ഇൻസ്റ്റൻസ് ഒബ്‌ജക്റ്റുകളിലോ (single-instance objects) ആശ്രയിക്കുന്ന വർക്കറുകളെ നിശബ്ദമായി തകരാറിലാക്കുകയും ചെയ്യുന്നു.

ProRes ഫയലുകൾ ഡീകോഡ് ചെയ്യാൻ Web Workers ഉപയോഗിക്കുന്ന ഒരു ബ്രൗസർ അധിഷ്ഠിത വീഡിയോ പ്രോസസ്സിംഗ് ആപ്പ് വികസിപ്പിച്ചുകൊണ്ടിരിക്കുമ്പോഴാണ് ഈ പ്രശ്നം ഉയർന്നുവന്നത്. Chrome-ഉം Firefox-ഉം യാതൊരു പ്രശ്നവുമില്ലാതെ കോഡ് കൈകാര്യം ചെയ്തപ്പോൾ, Safari വീഡിയോ ലോഡ് ചെയ്യുന്നതിൽ നിരന്തരം പരാജയപ്പെട്ടു. കൺസോളിൽ "cannot read video" എന്ന ഒരു പൊതുവായ എറർ മാത്രമാണ് റിപ്പോർട്ട് ചെയ്തത്, എന്നാൽ യഥാർത്ഥ കാരണം വർക്കറുടെ ഇനിഷ്യലൈസേഷൻ കോഡ് (initialization code) രണ്ടുതവണ പ്രവർത്തിക്കുകയും ഒരേ മോഡ്യൂളിന്റെ രണ്ട് സ്വതന്ത്ര കോപ്പികൾ മെമ്മറിയിൽ അവശേഷിപ്പിക്കുകയും ചെയ്തു എന്നതാണ്.

എങ്ങനെയാണ് ഈ ബഗ് പ്രകടമാകുന്നത്

ആധുനിക ബണ്ട്ലറുകൾ (Vite, Rollup, മുതലായവ) പലപ്പോഴും വർക്കറുടെ എൻട്രി ഫയലിലേക്ക് ഷെയർഡ് യൂട്ടിലിറ്റികൾ ഉൾപ്പെടുത്താറുണ്ട്, അങ്ങനെ ലേസി-ലോഡഡ് ചങ്ക്സിന് (lazy-loaded chunks) ആ കോഡ് എൻട്രി പോയിന്റിൽ നിന്ന് തിരികെ ഇംപോർട്ട് ചെയ്യാൻ സാധിക്കും. സ്റ്റാൻഡേർഡ് മോഡ്യൂൾ ലോഡർ ബിഹേവിയർ പിന്തുടരുന്ന ബ്രൗസറുകളിൽ, എൻട്രി മോഡ്യൂൾ ഇൻസ്റ്റാന്ഷ്യേറ്റ് ചെയ്തുകഴിഞ്ഞാൽ, ലോഡർ തുടർന്നുള്ള ഏതൊരു ഇംപോർട്ടിനും അതേ മോഡ്യൂൾ ഒബ്‌ജക്റ്റ് തന്നെ നൽകുന്നു, ഇത് രണ്ടാമതൊരു എക്സിക്യൂഷൻ തടയുന്നു.

Safari ഈ പ്രതീക്ഷയിൽ നിന്ന് വ്യതിചലിക്കുന്നു. ഒരു ലേസി-ലോഡഡ് ചങ്ക് വർക്കറുടെ എൻട്രി ഫയൽ ഇംപോർട്ട് ചെയ്യുമ്പോൾ, Safari അതിനെ ഒരു പുതിയ മോഡ്യൂൾ റിക്വസ്റ്റ് ആയി കണക്കാക്കുകയും എൻട്രി സ്ക്രിപ്റ്റ് വീണ്ടും പ്രവർത്തിപ്പിക്കുകയും ചെയ്യുന്നു. ഇതിന്റെ ഫലമായി അവിടെ നിർവചിച്ചിട്ടുള്ള ഓരോ വേരിയബിൾ, ക്ലാസ് അല്ലെങ്കിൽ സിംഗിൾട്ടൺ എന്നിവയുടെയും രണ്ട് പ്രത്യേക ഇൻസ്റ്റൻസുകൾ ഉണ്ടാകുന്നു.

എൻട്രി രണ്ടുതവണ പ്രവർത്തിക്കുമ്പോൾ എന്തൊക്കെ തകരാറിലാകുന്നു

  • സിംഗിൾട്ടണുകളും കാഷെകളും (Singletons and caches) ഇനി മുതൽ ഡാറ്റ പങ്കിടില്ല; ഒരു കോപ്പിക്ക് ശൂന്യമായ കാഷെ കാണപ്പെടുമ്പോൾ മറ്റേത് അത് പൂരിപ്പിക്കുന്നു.
  • രജിസ്ട്രികൾ (Registries) (ഉദാഹരണത്തിന്, മെസ്സേജ് ഹാൻഡ്‌ലറുകളുടെ ഒരു ലിസ്റ്റ്) രണ്ട് ഇൻസ്റ്റൻസുകൾക്കിടയിൽ വിഭജിക്കപ്പെടുകയും, ഇതിൽ ഒരു ഭാഗം ഫലപ്രദമായി ശൂന്യമായി മാറുകയും ചെയ്യുന്നു.
  • ഇവന്റ് ലിസണറുകൾ (Event listeners) രണ്ടുതവണ അറ്റാച്ച് ചെയ്യപ്പെടുന്നു, ഇത് ഡ്യൂപ്ലിക്കേറ്റ് ഹാൻഡ്‌ലിംഗിനോ മെമ്മറി വർദ്ധനവിനോ (memory bloat) കാരണമായേക്കാം.
  • WebAssembly (WASM) മോഡ്യൂളുകൾ രണ്ടുതവണ ലോഡ് ചെയ്യപ്പെടുന്നു, ഇത് ബാൻഡ്‌വിഡ്ത്തും ഇനിഷ്യലൈസേഷൻ സമയവും പാഴാക്കുന്നു.
  • ഈ പരാജയം നിശബ്ദമാണ്: ഒരു അൺകാറ്റ് എക്സെപ്ഷനും (uncaught exception) റിപ്പോർട്ട് ചെയ്യപ്പെടുന്നില്ല, പകരം നഷ്ടപ്പെട്ട സ്റ്റേറ്റിനെ ആശ്രയിക്കുന്ന ലോജിക് മാത്രമാണ് തെറ്റായി പ്രവർത്തിക്കുന്നത്.

ഒരു പ്രോജക്റ്റിൽ ഈ പ്രശ്നം എങ്ങനെ കണ്ടെത്താം

ബിൽറ്റ് അസറ്റുകളിൽ (built assets) ഒരു വേഗത്തിലുള്ള grep ഉപയോഗിച്ച് ഏതെങ്കിലും ചങ്ക് വർക്കറുടെ എൻട്രി ഫയൽ ഇംപോർട്ട് ചെയ്യുന്നുണ്ടോ എന്ന് കണ്ടെത്താം:

grep -l 'from"./your.worker-' dist/assets/*.js

കമാൻഡ് ഏതെങ്കിലും ഫയലുകൾ ലിസ്റ്റ് ചെയ്യുന്നുണ്ടെങ്കിൽ, ആ ഇംപോർട്ടുകൾ Safari-യിൽ ഡബിൾ-റൺ ബഗ് ഉണ്ടാക്കാൻ സാധ്യതയുണ്ട്.

പ്രായോഗികമായ പരിഹാരങ്ങൾ

  1. വർക്കർ എൻട്രിയിൽ നിന്ന് ഷെയർഡ് കോഡ് വേർതിരിക്കുക.
    സാധാരണ ലൈബ്രറികൾ അവയുടെ സ്വന്തം ചങ്കിലേക്ക് (ഉദാഹരണത്തിന്, Rollup-ന്റെ manualChunks ഉപയോഗിച്ച്) മാറ്റാൻ ബണ്ട്ലറിനെ കോൺഫിഗർ ചെയ്യുക. അപ്പോൾ വർക്കറും ലേസി-ലോഡഡ് മോഡ്യൂളുകളും ആ മൂന്നാമത്തെ ഫയലിൽ നിന്ന് ലൈബ്രറി ഇംപോർട്ട് ചെയ്യും, ഇത് വർക്കറുടെ എൻട്രി പോയിന്റ് ഇംപോർട്ട് ചെയ്യേണ്ടതില്ലെന്ന് ഉറപ്പാക്കുന്നു.

  2. ഒരു നേർത്ത (thin) എൻട്രി ഫയൽ ഉപയോഗിക്കുക.
    യഥാർത്ഥ ഇംപ്ലിമെന്റേഷൻ റീ-എക്സ്പോർട്ട് ചെയ്യുന്ന രീതിയിൽ വർക്കറുടെ എൻട്രി സ്ക്രിപ്റ്റിനെ ഒരു വരിയിലേക്ക് ചുരുക്കുക:

    // worker-entry.js
    import("./main.js");
    

    മറ്റേതെങ്കിലും ബണ്ടിൽ worker-entry.js ഇംപോർട്ട് ചെയ്യുന്നില്ലെങ്കിൽ, Safari രണ്ടാമതൊരു ഇംപോർട്ട് റിക്വസ്റ്റ് കാണില്ല, അതിനാൽ എൻട്രി ഒരു തവണ മാത്രമേ പ്രവർത്തിക്കൂ.

ഈ രണ്ട് സമീപനങ്ങളും ആപ്ലിക്കേഷനിലുടനീളം വർക്കറുടെ ഇനിഷ്യലൈസേഷൻ കോഡിനെ സിംഗിൾട്ടോണിക് (single-tonic) ആയി നിലനിർത്തുന്നു.

എന്തുകൊണ്ടാണ് ഈ ബഗ് പ്രധാനമാകുന്നത്

ഹെവി കമ്പ്യൂട്ടേഷൻ ജോലികൾ—വീഡിയോ എൻകോഡിംഗ്, ഇമേജ് പ്രോസസ്സിംഗ്, ക്രിപ്റ്റോഗ്രഫി—മെയിൻ ത്രെഡിൽ നിന്ന് മാറ്റുന്നതിനായി Web Workers ഒരു സാധാരണ രീതിയാണ്. ഒരു നിശബ്ദമായ സ്റ്റേറ്റ് സ്പ്ലിറ്റ് (state split), ഡെസ്ക്ടോപ്പ്, മൊബൈൽ ഉപകരണങ്ങളുടെ വലിയൊരു ശതമാനത്തിലും ഡിഫോൾട്ട് ബ്രൗസറായ Safari-യിൽ മാത്രം കാണപ്പെടുന്ന ഇടയ്ക്കിടെയുള്ള പരാജയങ്ങളായി മാറാം. ഈ എറർ ഒരു പൊതുവായ മീഡിയ-ലോഡ് പരാജയമായി കാണപ്പെടുന്നതിനാൽ, ഡെവലപ്പർമാർ തെറ്റായ ലക്ഷണങ്ങൾക്കായി മണിക്കൂറുകൾ ചിലവഴിച്ചേക്കാം.

ബ്രൗസറുകളിലുടനീളം ഒരേപോലെ നടപ്പിലാക്കപ്പെട്ടിട്ടില്ലാത്ത മോഡ്യൂൾ-ലോഡർ സെമാന്റിക്സുകളെ (module-loader semantics) ആശ്രയിക്കുന്നത് വലിയൊരു അപകടസാധ്യതയാണെന്നും ഈ ബഗ് ചൂണ്ടിക്കാണിക്കുന്നു. ഒരു ബണ്ട്ലറുടെ ഒപ്റ്റിമൈസേഷൻ സ്ട്രാറ്റജി ഒരു സിംഗിൾ ഷെയർഡ് മോഡ്യൂൾ ഇൻസ്റ്റൻസ് ഉണ്ടെന്ന് അനുമാനിക്കുമ്പോൾ, അതിൽ നിന്നുള്ള ഏതൊരു വ്യതിയാനവും ആ അനുമാനത്തെ തകർക്കും.

മറുവാദങ്ങളും തുറന്ന ചോദ്യങ്ങളും

Safari-യുടെ പെരുമാറ്റം അതിന്റെ തന്നെ മോഡ്യൂൾ റെസല്യൂഷൻ നിയമങ്ങളുമായി യോജിച്ചുപോകുന്നു, ഇത് വർക്കറുകളെ ഉൾപ്പെടുത്തിയുള്ള എഡ്ജ് കേസുകളിൽ (edge cases) സ്പെസിഫിക്കേഷനിൽ നിന്ന് നേരിയ വ്യത്യാസമുള്ളതാണ്. ബണ്ട്ലറുകൾ വർക്കറുടെ എൻട്രി ഫയലിൽ ഷെയർഡ് കോഡ് ഉൾപ്പെടുത്തുന്നത് ഒഴിവാക്കണമെന്നും, അതിനാൽ ഇത് ബ്രൗസർ പിശക് എന്നതിലുപരി ബിൽഡ്-ടൈം ഡിസിപ്ലിൻ (build-time discipline) എന്ന വിഷയമാണെന്നും ചില ഡെവലപ്പർമാർ വാദിക്കുന്നു. എന്നാൽ Safari-യുടെ ഈ വ്യതിയാനം രേഖപ്പെടുത്തിയിട്ടില്ലാത്തതിനാൽ, അത് മുൻകൂട്ടി കാണാൻ ഡെവലപ്പർമാർക്ക് വിശ്വസനീയമായ മാർഗ്ഗമില്ലെന്ന് മറ്റുള്ളവർ ചൂണ്ടിക്കാട്ടുന്നു.

ആപ്പിൾ ഈ പ്രശ്നം പരസ്യമായി അംഗീകരിച്ചിട്ടില്ല, കൂടാതെ ഇത് പരിഹരിക്കാനുള്ള സമയക്രമത്തെക്കുറിച്ചും അറിയിപ്പുകളില്ല. Safari അതിന്റെ ലോഡർ മാറ്റുന്നത് വരെ, ബണ്ടിലുകൾ പുനഃക്രമീകരിക്കാനോ അല്ലെങ്കിൽ അവരുടെ CI പൈപ്പ്‌ലൈനുകളിൽ ഡിറ്റക്ഷൻ ലോജിക് ചേർക്കാനോ ഉള്ള ഉത്തരവാദിത്തം ഡെവലപ്പർമാരുടെ മേൽ തന്നെയാണ്.

അടുത്തതായി ശ്രദ്ധിക്കേണ്ടവ

  • ബ്രൗസർ അപ്‌ഡേറ്റുകൾ – module-worker കൈകാര്യം ചെയ്യുന്നതിനെക്കുറിച്ച് Safari റിലീസ് നോട്ടുകളിൽ എന്തെങ്കിലും പരാമർശമുണ്ടോ എന്ന് ശ്രദ്ധിക്കുക.
  • ബണ്ട്ലർ കമ്മ്യൂണിറ്റി പാച്ചുകൾ – ഈ ബഗ്ഗിലേക്ക് നയിക്കുന്ന രീതി ഒഴിവാക്കാൻ Vite, Rollup എന്നിവയും മറ്റുള്ളവയും മുന്നറിയിപ്പുകളോ (warnings) അല്ലെങ്കിൽ ഓട്ടോമാറ്റിക് ചങ്കിംഗ് സ്ട്രാറ്റജികളോ (automatic chunking strategies) അവതരിപ്പിച്ചേക്കാം.
  • ടെസ്റ്റിംഗ് രീതികൾ – റിലീസ് ചെയ്യുന്നതിന് മുമ്പ് യഥാർത്ഥ മീഡിയ ഫയലുകളും Safari-യിൽ ഫുൾ-സ്റ്റാക്ക് വർക്കർ ടെസ്റ്റുകളും ഉൾപ്പെടുത്തുന്നത് ഈ നിശബ്ദമായ പരാജയം (silent failure) നേരത്തെ കണ്ടെത്താൻ സഹായിക്കും.

പ്രധാന കാര്യങ്ങൾ

നിങ്ങളുടെ Safari ഉപയോക്താക്കൾക്ക് വിശദീകരിക്കാനാവാത്ത വർക്കർ സംബന്ധമായ പരാജയങ്ങൾ അനുഭവപ്പെടുന്നുണ്ടെങ്കിൽ, വർക്കർ അല്ലാത്ത ഏതെങ്കിലും ബണ്ടിൽ (non-worker bundle) വർക്കറുടെ എൻട്രി സ്ക്രിപ്റ്റ് ഇംപോർട്ട് ചെയ്യുന്നുണ്ടോ എന്ന് പരിശോധിക്കുക. ഡബിൾ എക്സിക്യൂഷൻ ബഗ് (double execution bug) സിംഗിൾട്ടൺ സ്റ്റേറ്റിനെ (singleton state) നിശബ്ദമായി തകർക്കുന്നു, എന്നാൽ ഷെയർ ചെയ്ത കോഡ് എൻട്രി പോയിന്റിൽ നിന്ന് മാറ്റുന്നതോ അല്ലെങ്കിൽ എൻട്രി പോയിന്റിനെ ഒരു 'thin re-export' ആയി ചുരുക്കുന്നതോ ബ്രൗസർ ഫിക്സിനായി കാത്തുനിൽക്കാതെ തന്നെ ശരിയായ പ്രവർത്തനം വീണ്ടെടുക്കാൻ സഹായിക്കും.