SUPER_OLEG_DEV Telegram 218
Привет!

Продолжая кейс с высоким потреблением памяти, обнаружили таки утечку в приложении, и она довольно интересная.

Есть такая либа day.js, и ее система плагинов - https://day.js.org/docs/en/plugin/plugin

В этой документации рекомендованный подход к расширению - перезапись прототипа класса dayjs:

// overriding existing API
// e.g. extend dayjs().format()
const oldFormat = dayjsClass.prototype.format

dayjsClass.prototype.format = function(arguments) {
// original format result
const result = oldFormat.bind(this)(arguments)
// return modified result
}


У приложения - своя обертка над day.js где как раз есть такой плагин, перезаписывающий метод прототипа.

Далее, что мы имеем:
- эта обертка и сам day.js вынесены в shared Module Federation чанк
- хостовое приложение поставляет этот чанк
- в приложении загружается код микрофронта, который использует этот чанк

Код микрофронта имеет такой жизненный цикл на сервере:
- загружаем код микрофронта как строку
- компилируем строку и получаем JS модуль с экспортами
- сохраняем этот модуль в LRU кэш (микрофронтов может быть много, новые версии появляются, старые становятся не нужны)
- инициализируем Module Federation

Проблема - код микрофронта и его скомпилированный результат многократно дублируются в памяти, при этом прослеживается очень длинный трейс - ссылка на метод dayjs.

После долгой отладки мы обнаружили, что:
- код микрофронта в какой-то момент вытесняется из кэша и загружается заново
- код микрофронта инициализируется и вызывает shared модуль - обертку над day.js, что бы получить его экспорты
- shared модуль переиспользуется тот же самый, но заново выполняется код плагина через dayjs.extend(plugin)

Только на этом вызове, метод в прототипе const oldFormat = dayjsClass.prototype.format - это уже тот метод, который один раз мы заманкипатчили на предыдущем вызове плагина!

В итоге на каждое вытеснение микрофронта из кэша мы получаем матрешку перезаписи метода в прототипе, если визуализировать это в псевдокоде:
dayjsClass.prototype.format = myFnc
- dayjsClass.prototype.format = myFnc(myFnc)
- dayjsClass.prototype.format = myFnc(myFnc(myFnc))
- dayjsClass.prototype.format = myFnc(myFnc(myFnc(myFnc)))
...


Наглядный кейс про сайд-эффекты, shared зависимости и синглтоны.

Что осталось не до конца понятным - как именно утекает ссылка на исходники микрофронта в контекст условной myFnc в коде shared чанка.
🔥8🥴7



tgoop.com/super_oleg_dev/218
Create:
Last Update:

Привет!

Продолжая кейс с высоким потреблением памяти, обнаружили таки утечку в приложении, и она довольно интересная.

Есть такая либа day.js, и ее система плагинов - https://day.js.org/docs/en/plugin/plugin

В этой документации рекомендованный подход к расширению - перезапись прототипа класса dayjs:

// overriding existing API
// e.g. extend dayjs().format()
const oldFormat = dayjsClass.prototype.format

dayjsClass.prototype.format = function(arguments) {
// original format result
const result = oldFormat.bind(this)(arguments)
// return modified result
}


У приложения - своя обертка над day.js где как раз есть такой плагин, перезаписывающий метод прототипа.

Далее, что мы имеем:
- эта обертка и сам day.js вынесены в shared Module Federation чанк
- хостовое приложение поставляет этот чанк
- в приложении загружается код микрофронта, который использует этот чанк

Код микрофронта имеет такой жизненный цикл на сервере:
- загружаем код микрофронта как строку
- компилируем строку и получаем JS модуль с экспортами
- сохраняем этот модуль в LRU кэш (микрофронтов может быть много, новые версии появляются, старые становятся не нужны)
- инициализируем Module Federation

Проблема - код микрофронта и его скомпилированный результат многократно дублируются в памяти, при этом прослеживается очень длинный трейс - ссылка на метод dayjs.

После долгой отладки мы обнаружили, что:
- код микрофронта в какой-то момент вытесняется из кэша и загружается заново
- код микрофронта инициализируется и вызывает shared модуль - обертку над day.js, что бы получить его экспорты
- shared модуль переиспользуется тот же самый, но заново выполняется код плагина через dayjs.extend(plugin)

Только на этом вызове, метод в прототипе const oldFormat = dayjsClass.prototype.format - это уже тот метод, который один раз мы заманкипатчили на предыдущем вызове плагина!

В итоге на каждое вытеснение микрофронта из кэша мы получаем матрешку перезаписи метода в прототипе, если визуализировать это в псевдокоде:
dayjsClass.prototype.format = myFnc
- dayjsClass.prototype.format = myFnc(myFnc)
- dayjsClass.prototype.format = myFnc(myFnc(myFnc))
- dayjsClass.prototype.format = myFnc(myFnc(myFnc(myFnc)))
...


Наглядный кейс про сайд-эффекты, shared зависимости и синглтоны.

Что осталось не до конца понятным - как именно утекает ссылка на исходники микрофронта в контекст условной myFnc в коде shared чанка.

BY SuperOleg dev notes


Share with your friend now:
tgoop.com/super_oleg_dev/218

View MORE
Open in Telegram


Telegram News

Date: |

The SUCK Channel on Telegram, with a message saying some content has been removed by the police. Photo: Telegram screenshot. Invite up to 200 users from your contacts to join your channel To delete a channel with over 1,000 subscribers, you need to contact user support Telegram Channels requirements & features Done! Now you’re the proud owner of a Telegram channel. The next step is to set up and customize your channel.
from us


Telegram SuperOleg dev notes
FROM American