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: |

3How to create a Telegram channel? Polls Over 33,000 people sent out over 1,000 doxxing messages in the group. Although the administrators tried to delete all of the messages, the posting speed was far too much for them to keep up. In handing down the sentence yesterday, deputy judge Peter Hui Shiu-keung of the district court said that even if Ng did not post the messages, he cannot shirk responsibility as the owner and administrator of such a big group for allowing these messages that incite illegal behaviors to exist.
from us


Telegram SuperOleg dev notes
FROM American