Нервный программист
123 subscribers
54 photos
1 video
1 file
31 links
Эмоционально об айти и программировании
Download Telegram
Когда вместо реляционной СУБД решил использовать NoSQL
🌚62😁1😢1
Внимание! В сеть утекли исходники 11 винды!
😁7🔥1
В прошлый раз рассказывал про langgraph. а сегодня расскажу о том, почему у меня сгорела дыра из-за него🌚

Дело в том, что langgraph пытается казаться на столько удобным решением, что они даже запилили собственную IDE, и назвали ее langgraph studio (далее lgs). Очень блять оригинально.

Как же выглядит IDE для разработки агентов? Нужно ли там писать код? Можно ли там вайбкодить с помощью агента, которого сам же пишешь? 🤔 Все эти вопросы сразу остро встают на повестке дня.

Но все гораздо прозаичнее (см. картинку). Эта присрачка позволяет запускать каждый узел в графе (функцию) отдельно, мейнтейнить стейт, менять входные параметры. Короче говоря, позволяет всячески эксперементировать с узлами без необходимости работать с полным циклом запрос-ответ. Удобно? Не то слово! Идея просто бомбическая.

Разумеется, наш специально обученный человек сразу захотел lgs использовать. И тут его трудно осуждать.

Сначала все было хорошо. Сплошные востороженные возгласы. Но внезапно мне прилетает тревожное сообщение "В lgs не работает подключение к бд"😱

Начал разбираться в проблеме. Как вы можете помнить, коннекшн к бд я храню в contextvars. Подробнее об этом я писал тут. Когда lgs запускает отдельно ноду, то contextvars пустые. Поэтому и подключения нет. Надо как-то подключиться к бд и положить коннекшн в переменную перед запуском каждой ноды. Изи!

И вот тут меня настигло абсолютное разочарование. Потому что никакого способа это сделать разработчики не предоставили. Ни хуков, ни api для плагинов, вообще ни-ху-я. Просто нет возможности выполнить код в этой IDE помимо кода узла😔

ТО ЕСТЬ, разрабы lgs дали способ работать с каждым узлом изолированно от всего остального кода, но не подумали, что может потребоваться ЧТО-ТО от этого остального кода. Гении ебать!

В общем ключе задача формулируется так: "У библиотеки или тулзы отсутствует нужный api для взаимодействия, а вам позарез надо вмешаться в ее модель выполнения". Звучит конечно так себе. Поэтому добавим "...для разработки или тестирования"😏

Это был пост-затравка. В следующем посте я расскажу, как решал эту проблему в python. Но подобный метод может применяться практически в любых интерпретируемых ЯП. В комментах пишите, как бы вы решали подобную проблему👇
5
Итак, я обозначил проблему. Вкратце: у нас библиотека или тулза, которая настолько говяная, что к ней не подступиться через публичный api, но недостаточно говяная, чтоб от нее отказываться. А нам позарез надо что-то поменять в ее поведении.

В комментах никто не написал, как бы это делал (эх 😔), поэтому дам ответ сам: monkey patch - это возможность менять код в рантайме.

Как же это выглядит в моем случае. У меня тулза - это вонючий langgraph studio (lgs), и мне надо, чтоб во всем коде, при запуске через lgs было доступно подключение к бд.

Напоминаю, что все питонисты обмазываются декораторами. Я решил не отставать. Написал декоратор db_aware такой, что

@db_aware
async def some_func():
# db стопроц будет тут
db = get_db()
# далее в коде юзаем db в хвост и гриву


Пусть точка входа в наш граф находится в файлике my_agent/graph.py, его мы подаем на вход lgs. В файлике my_agent/nodes.py находится код всех нод графа. Напоминаю, что ноды - это просто функции. Тогда можно написать новую точку входа только для lgs

# Исходный пакет с нодами
NODES_MODULE: Final[str] = "my_agent.nodes"

class DbAwareNodes(ModuleType):
def __init__(self, module: str):
super().__init__(module)
# Сохраняем исходные функции
self._original_nodes = importlib.import_module(NODES_MODULE)

# __getattr__ вызывается когда интерпретатор импортирует имя из модуля
def __getattr__(self, name):
# Находим исходную функцию
node = getattr(self._original_nodes, name)
# Декоратор работает только с корутинами
if iscoroutinefunction(node):
# Декорируем исходную функцию
return db_aware(node)
return node

# Здесь подменяем дефолтный модуль на наш пропатченный
sys.modules[NODES_MODULE] = DbAwareNodes(NODES_MODULE)

# Экспортируем наш граф, будто это точка входа.
from pain_agent.graph import graph
__all__ = ["graph"]


Выглядит потно, согласен. Но на самом деле, главное здесь - это подход. Ведь он применим ко всем популярным интерпертируемым языкам программирования: python, php, ruby, js.

Например, в php можно тупо перегрузить stream для чтения файлов, и менять код прямо во время чтения интерпретатором исходников. Именно так работает пакет dg/bypass-finals, позволяющий мокать final классы.

Самое главное, помните, что monkey patch следует использовать только при разработке! В продакшне использовать подобное - это надо быть очень смелым, либо очень тупым. Лучше не надо.
3👍3🙈2
Кто понял, тот понял
😁4
Поторопился с предыдущей картинкой. Выкладываю исправленный вариант
😁5👍1
Вышло очередное обновление индекса языков программирование TIOBE. Меня прикалывает, как создатели TIOBE продолжают срать себе в штаны, делая вид, что все в порядке👌

"У нас Ada обогнал PHP и Kotlin, а Visual Basic будет попизже Golang. Perl попал в 10ку, а Ruby, даже в 20ке не видно. Typescript мы поместили на 35е место. Вас что-то смущает?.. Нам поебать!" - так прокомментировал свежие данные рейтинга глава TIOBE Вротстислав Хуепутолов.

Ну а если серьезно, когда видишь в одном рейтинге matlab и php, то сразу же вопрос возникает, а для кого это составляется вообще? в чем польза?

The index can be used to check whether your programming skills are still up to date or to make a strategic decision about what programming language should be adopted when starting to build a new software system.

Я считаю, что индекс можно использовать для проверки того, на сколько челики в TIOBE ебнулись в своих метриках, который нахер никому кроме них самих не нужны. Тупее было бы только составить рейтинг по количеству букв в названии языка. Хотя нет, даже это имело бы больше смысла😡

Единственное, что могло бы оправдать этот рейтинг из жопы, это если бы TIOBE просто бабок занесли. Но мы об этом никогда не узнаем.
У нас наоборот
😁5😢2
Че-то в последнее время тяжеловато идет написание постов. Много работы и уходящее лето, когда надо успеть чуток почиллить, - делают свое дело!

Контент план есть, тем много. Темы появляются быстрее, чем успевается о них писать. Но я тот еще писака, поэтому на написание одного поста у меня уходит от 2х часов 😔 Сами понимаете, как с такими таймингами тяжело вписаться в текущую жизнь.

Тем не менее, будем надеяться, что с приходом осени все стабилизируется. На улице будет нечего делать, и придется заниматься эскопизмом в канале 😊
2🤯2
Но чтобы перерывы в постах о программировании не были такими уж длинными, я воспользуюсь читерной рубрикой "подписчики набрасывают на вентилятор".

Мне тут скинули старую, но презабавнейшую статью What is {} + {} in JavaScript?. В статье автор задается вопросом, а что будет при выполнении следующих операций в js

[]+{}
[]+[]
{}+[]
{}+{}

Спойлер: будут приколы.

Для []+{} ответ '[object Object]'. []+[] - это ''. И наконец, два моих "любимых": {} + [] дает 0, а {} + {} вернет NaN.

Для "любимых" объяснение такое, что первый {} js интерпретирует как пустой блок кода 🤡, а дальше уже приводит второй аргумент к числу в соответствии с действием унарного оператора +. Ну не красота ли! Об остальных можно прочесть в статье.

Может показаться, что всем результатам есть логичное объяснение. И в статье это объяснение дается. Но по факту, это просто торчащие уши плохого дизайна js, с которым приходится мириться и по сей день, используя костыли в виде ts. Запоминать такое поведение категорически противопоказанно для сохранения рассудка! 👆

А если на собеседованиях вас спрашивают про подобные особенности, то можете смело проваливать это интервью, потому что тех. лид долбаеб, и делать в такой компании нечего. Ну либо можете зазубрить кучу таких кейсов и задоминировать! Но будьте осторожны, ведь тогда можно самому ебнуться наглухо и стать вонючим душнилой🤓 Смотрите сами короче
😁5💯21
Лайфхацк для любимых подпищеков♥️
😁4👍3🥰21
Кто-то решил покреативить со шрифтами, а я минуту стоял и не мог понять, что тут написано🥲
😁4👍1
Прикол. У меня прям сейчас таск по обновлению пакетов. Обновляю-обновляю, и вдруг перестает все собираться. И ошибка такая интересная

npm error 404 Not Found - GET https://registry.npmjs.org/debug/-/debug-4.4.2.tgz - Not found


Захожу на гитхаб, а тут issue час назад была создана Version 4.4.2 published to npm is compromised.

С одной стороны, как бы круто что в npm так быстро сработали. С другой стороны, че то я себя не очень безопасно щас почувствовал, даже несмотря на то, что этой версии уже нет в npm. Какое-то время эта херабора запускалась у меня локально, пусть и в контейнере.

Вообщем в мире js все как всегда 😔
👍1😱1
А я отвечу постом на комментарий выше про то, что это касается не только js.

Да, безусловно, такое может произойти в любой экосистеме, не только в js. Везде есть уязвимости!

Но js отличает то, что здесь какое-то неадекватное количество peer dependencies. Авторы пакетов на каждый чих используют другой сторонний пакет. Мемный пакет is-odd все еще с нами, и у него ебейшее количество скачиваний в неделю. А истории про то, что папка node_modules занимает гигабайты - это вообще-то норма, а не исключение.

Именно потому что в папке node_modules лежат миллиарды пакетов, вероятность получить уязвимость кратно возрастает!

У меня 3 экосистемы на поддержке: js, php и python. И раз в полгода мне надо обновлять пакеты для аудита. И я хочу сказать, что если у вас более менее серьезный проект, то довести количество уязвимостей до 0 в js нереально. Мне ни разу не удавалось это даже для внутренних простеньких проектов. В то же время для php и python отсутствие уязвимостей - это скорее норма.

Конкретно в случае с пакетом debug это зависимость express

+-- debug@4.4.1
`-- express@5.1.0
+-- body-parser@2.2.0
| `-- debug@4.4.1 deduped
+-- debug@4.4.1 deduped
+-- finalhandler@2.1.0
| `-- debug@4.4.1 deduped
+-- router@2.2.0
| `-- debug@4.4.1 deduped
`-- send@1.2.0
`-- debug@4.4.1 deduped

А самое смешное то, что я вот откатил этот злоебучий пакет до предыдущей версии, а в нем 7 критических уязвимостей 🤡

Поэтому нет, я не соглашусь, что оно везде все так. В js количество уже перетекло в качество. Точнее в его отсутствие.
👍21
Я уже 5 лет плотничком работаю с Doctrine ORM. Работа у меня разнообразная. Пишем мы и новый код, с новыми таблицами в бд. Но также имеется большая легаси база говна данных, на которую мы старательно натягиваем Doctrine. Короче, всякое повидал за эти годы, в том числе даже поконтрибьютил в саму Doctrine.

Несмотря на то, что в целом впечатления от Doctrine благостное (по сравнению с тем же Eloquent хех), эта ORM может вставить палки в колеса. Как и любой инструмент. Об этом я написал свой очередной лонгрид Как Doctrine ORM ломает вашу архитектуру.

Я постарался рассказать об основных принципах работы этой ORM так, чтоб было понятно даже тем, кто никогда не работал с Doctrine. А по скольку Doctrine - это классический пример Data Mapper, все эти принципы могут быть применены к другим Data Mapper ORM, например Hibernate и SQLAlchemy.

А от тех, кто тоже успел поработать с Doctrine, жду накидывания на меня какашек о том, что я просто все неправильно понял 😘
👍7
😁8🆒21👏1
Говорят, чем больше у тебя пхп-слоников, тем хуже пишешь на голэнг) сегодня у меня +1🥳
🎉9🤯4
Все еще работаем над llm-based FastAPI сервисом. Создал сам себе issue: нужна возможность периодически выполнять какие-то задачи, в народе именуемых cronjobами. Поскольку fastapi - это современный веб фреймворк тупо роутер запросов, такой функционал нам не завезли из коробки. Но задача довольно классическая, проблем быть не должно.

Кроме того, бох девопс дал нам кубернетис с его встроенными CronJobs. Поэтому изечно можем раз в минуту дергать какой-то сервис по ендпоинту /run-cron. Нам лишь нужно
* хранить и отслеживать статус джоб
* парсить cronexpr и решать, какие джобы щас запустить
* определять, если джоба зависла и сбрасывать статус
Это такой bare minimum, который бы меня устроил.

Гугл и ллмки выдали мне решение: билиотека apscheduler. Куча звезд на гитхабе, делает все то, что описано выше и даже больше. Короче, царский подгодон.

Начал интегрироваться и вместо cronjobов сделал blowjobы. Потому что этот apscheduler сам себе экзекутор задач. Он ни в какую не хотел привязываться к внешнему тригеру, потому что он сам знает, когда ему запускать задачи. А мне зачем экзекутор джобов в каждом поде приложения??

Потратив целый день на попытки скрестить жопу с ужом питоном, я понял, что за этот день я бы уже написал свое решение. Единственное, что мне нужно, это либа для парсинга cronexpr. Ну уж с этим то проблем быть не должно? Не должно ведь, python?

Самая популярная либа для парсинга cron экспрешнов называется croniter. И она официально не поддерживается с конца 2024 года 🤡 На стэковерфлоу в связи с этим советуют воспользоваться новой либой cron-converter, у которой невероятные 46 звезд на гитхабе (кстати, поставил 47ю) 🤩 Ну тут особо выбора нет, ибо писать парсер с нуля - это задача для пет проектов.

И вот получается, что в 2к25 я все еще занимаюсь тем, что было сделано во многих фреймворках и цмсках в нулевые. Странно это как-то... Может я неправильно это готовлю.

Кто-то может подумать, что я тут опять накидываю на питон. На самом деле уже 4 года в проде на Symfony также крутится собственное решение с кронджобами, которое делает все тоже самое. Ибо Scheduler компонент завезли только в версии 6.3. И я его даже не пробовал 😔 Но хотя бы в пхп у либы dragonmantank/cron-expression, которая парсит cron expression, 4.7к звезд, а у питоновсокого croniter 485 🤷 вообще нет уверенности, что кому-то это надо.

Поделитесь, как сами решаете проблему с кроном? сразу ставите celery? запускаете отдельный сервис с тем же кодом, но чисто повыполнять таски? или целиком на кубовых джобах живете?
🔥1
Итак, настал момент истины. На сколько же php быстрее golang?

И нет, я не ошибся. Дмитрий Кириллов в своем шикарном докладе показал, что php с jit может быть быстрее Си (базового). Это на столько меня вдохновило, что я решил поиграться с этим сам. Пришлось даже виртуалочку для этого арендовать.

Я использовал ту же самую задачу о рюкзаке из доклада. Вот код на golang.
package main

import "fmt"

const w = 700
const h = 700
const l = 700
const target = 1500

func main() {
ans := 0
for i := 0; i < w; i++ {
for j := 0; j < h; j++ {
for k := 0; k < l; k++ {
vol := (i + 3) * (j + 4) * (k + 5)
if vol == target {
ans += 2
}
}
}
}
fmt.Println(ans)
}


Код на пхп взял прям отсюда.

Итак, вот, что нам выдал perf для 10 запусков golang(1.25.2)
0.353181 +- 0.000158 seconds

А вот для php(8.4.13)
0.2895 +- 0.0119 seconds


Таким образом, мы видим, что php реально очень быстр, когда дело касается CPU-bound задач. Ну и конечно, когда вы поприседали с ним, чтоб убрать все ненужные опкоды. Мы получили, что пхп быстрее почти на 20%.

Изначально я тестировал этот код на версии php 8.3. И там результат был таков
0.33962 +- 0.00224

Что конечно все еще быстрее голэнг, но не так впечатляюще. То есть разрабы php действительно хорошо поработали над jit в 8.4!👍

А еще я поставил nodejs и python, раз уж пошла такая пляска. И я вынужден признать, что ребята, которые пилят V8, рил настоящие гении. Для ноды результат получился
0.32645 +- 0.00288

Что медленее php8.4, но быстрее и 8.3 и golang. Но при этом, в отличии от php, мне не нужно было делать ничего специального. Я просто написал
const calc = (w, h, l, target) => {
let ans = 0;
for (let i = 0; i < w; i++) {
for (let j = 0; j < h; j++) {
for (let k = 0; k < l; k++) {
const vol = (i + 3) * (j + 4) * (k + 5);
if (vol === target) {
ans += 2;
}
}
}
}
return ans;
};

console.log(calc(700, 700, 700, 1500));

и оно уже быстрее голэнг!

Что касается питона (3.12), то тут все выглядит как-то так 😂
25.958 +- 0.235 seconds

Лан, нет смысла сравнивать язык с jit и без jit. Понятно, что это будет избиение младенца.

Поэтому я решил отключить jit в php. Более того, я решил использовать версию кода на пхп без всяких извращений с проверками на out of bound conditions. Таким образом, код стал выглядеть, как обычный код на пыхе. Итого имеем
4.9320 +- 0.0439 seconds

То есть питон более, чем в 5 раз медленее пхп.

А что в итоге? Понятно, что это все синтетика. Понятно, что, когда мы начнем писать бизнес логику, начнем вызывать методы у объектов, начнем массивами всячески оперировать, то картина будет немного другой. Конечно статически типизированный и компилируемый голэнг все таки в другой лиге играет. Особенно в плане потребления памяти.

Но если вдруг перед вами стоит задача, где надо тупо дробить числа, а у вас пхп - не бегите переписывать этот кусок на голэнг, шоб было побыстрее! Лучше вместо создания нового микросервиса посмотрите доклад Дмитрия, охуейте, а затем примените новые знания на практике 👆

P.S. Мечтаю стать Дмитрием, когда вырасту
👏12👍5🔥3