«phpClub» — архив тем ("тредов"), посвящённых изучению PHP и веб-технологий.
Аноним 2019/04/27 11:10:09  №1389781 1
Если планируете перекатываьтся за границу, то забейте на пхп. он мертв в развитых странах.
Говорю на своем опыте. я перекатился в израиль и тут нет вакансий. нода в 2 раза востребованей. самые топовые языки тут это с#, питон и джава
Ответы: >>1389802
Аноним 2019/04/27 11:29:56  №1389802 2
Аноны, какой редактор посоветуете?
Я пишу в VScode, но сейчас стал писать больше, и блядь VScode даже с плагинами не устраиваетили я его настроить не могу. Я хочу порядочные автодополнения, что бы редактор нормально искал методы используемого класса, что бы показывал нормально аргументы вызываемой функции.

>>1389781
У меня смутное ощущение что когда ты въедешь в саму сферу - выбор платформы это дело пятое. Какая разница на чем MVC реализовывать, если ты понимаешь как это делать. Не вижу особой разницы в этом случае между например пхп или питоном. Тем более пхп7 быстр. Думаю что он свои позиции отвоюет. JS до 2015 тоже был ссаниной, а сейчас развился и расхайпован что пиздец.
Если это не так - то уже разъясните иную точку зрения.

Ответы: >>1389813 >>1390167 >>1394587
Аноним 2019/04/27 11:40:32  №1389813 3
>>1389802
П.С.
Я к чему - я сам выбирал в какой бекенд вкатываться, выбирал между пхп и питономи нодой. Я на тот момент знал только синтаксис этих ЯП.
По итогу порешал рыночек, и количество информации. На пхп проще заработать на первый доширак, если суетитьсяособенно удобно если у тебя уже есть работа и ты вкатываешься по вечерам. Сейчас пришел к выводу что понимание предметной области значит больше чем конретный ЯП.
Может я нуб, но я реально не понимаю какая разница между пхп и питоном в типовых задачах.

С нодой отдельная песня. У нее своя ниша, а за ее рамками не понятно зачем она нужна. Хотя и на ней можно магазины ебашить, ток нахуя.
Ответы: >>1389824
Аноним 2019/04/27 11:46:33  №1389824 4
>>1389813
П.П.С
Вот что мне реально не нравится в пхп - то что он не создает процесса. Рожден что бы умереть короче. Ну и конфигурационные файлы сервера - то еще говно.
Но опять же, наверное пхп можно запустить как постоянный процесс, но про это я ничего не знаю пока что.

В ноде создал объект сервера http, поставил событие listening - это как то солиднее на мой взгляд.
Ответы: >>1394587
Аноним 2019/04/27 18:03:45  №1390167 5
Аноним 2019/05/05 15:43:23  №1394587 6
>>1389946

> Для этого нужно проверить, что конференции между этими пользователями не существует. То есть нужно найти двух Получателей с одинаковым Диалогом и чтобы этот Диалог был приватным.

Такие запросы с джойнами будут плохо работать на больших нагрузках. Они же почти не оптимизируются индексами никак и требуют перебор строк. Возможно, тут придется сделать денормализацию, например, добавить в Dialog либо в Participant дополнительные поля. Чтобы, например, запрос бы имел вид

SELECT FROM Participant WHERE user = ? AND partner = ? AND private =1

Это ложится на индексы. Но, конечно, денормализацию стоит делать во вторую очередь.

> Можно я оптимизацию БД оставлю на последнее?

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

Более того, часто под мессенджеры даже пишутся специализированные хранилища.

Насчет reply - а недостаточно тут просто сделать поле в сообщении "replyToUuid" со ссылкой на исходное сообщение? Без вложений.

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

Также, в таблицах вроде Conference_Reference можно не вводить отдельный uuid, а использовать например составной ключ (uuid конференции + id юзера). Возможно, так будет проще. А может и нет.

>>1389824

То, что он умирает, во-первых, делает его stateless, во-вторых, исключает утечки памяти, которые возможны в node-приложениях. Это зачастую плюс, а не минус. Ты знаешь, как в ноде искать утечку памяти, есть ли для нее бесплатные профайлеры памяти?

>>1389802

По идее в VSCode умное дополнение есть, оно называется intellisense или как-то так и его надо ставить отдельно для каждого языка.

>>1389598

> Я думаю, что пока подходы к авторизации методов WAMP не изучены, следует писать сырой код на if'ах, а подход с роутингом взять на заметку и держать в уме. А как думаете вы?

Я, увы, так хорошо авторизацию в WAMP не знаю и сейчас не могу подсказать. Разделение прав нам обязательно нужно будет при подписке, чтобы клиент не мог подписаться на чужие сообщения (кстати, я сейчас себя поймал на мысли, что возможно, другие разработчики этим не озаботились и в интернете куча уязвимых WAMP-серверов).

Возможно, придется использовать роутер и в нем проверять переданный клиентом токен. Или вместо id пользователя в идентификаторах каналов использовать трудноподбираемый токен (new-msgs-for-112345678765432) - тогда может быть, авторизация для подписки не потребуется.

Ответы: >>1397648
someApprentice 2019/05/11 13:13:34  №1397648 7
image.png (249, 1920x1080)
1080x1920
image.png (276, 1920x1080)
1080x1920
image.png (356, 1920x1080)
1080x1920
image.png (290, 1920x1080)
1080x1920
>>1394573
>По поводу импортов: я не очень понимаю, зачем ты в model/message.py импортируешь наследников. Обычно импортируют только то, что нужно в данном файле. И обычно наследник импортирует предка, а не наоборот. Потому я думаю, эти импорты надо просто убрать:
>
># models/message.py
>import text_message
>import voice_message
>
>Зачем они добавлены? Странно, что у тебя предок зависит от своих наследников (что они ему нужны).
Я понял свою ошибку. Я когда пытался получить все сообщения я использовал команду вида session.query(Message).join(Message_Reference).filter(Message_Reference.user == self.user).all() и получал только сущности родительского класса Message, и я подумал что родителю необходимо знать о детях чтобы получить их всех.
Я теперь понял, что импорты должны выполнятся там где выполняется этот запрос - это работает.

>Также, ты похоже выбрал Concrete Table Inheritance, возможно, что запросы к ней потребуют лишних UNION, судя по мануалу: https://docs.sqlalchemy.org/en/13/orm/inheritance.html#concrete-table-inheritance
К сожалению, мне не удалось найти какой паттерн наследования использует psql. Я косвенно предположил, что это именно он.pic-1

Использование UNION приводит к нагрузке?

Вообще, ORM не очень дружат с наследованием, как я могу посудить. Отношения между сущностями тяжело совершить с помощью встроенных инструментов и приходится делать метод для совершения запроса в ручную.pic-2 При получении сущностей выдается массив из сначала сущностей родителя, затем сущности ребёнка.pic-3 Я не знаю должно ли быть такое поведение. Хочется самому отсортировать только сущности детей и возвращать этот массив.


>> Следует сделать скрипт, который будет принимать все credentials, выполнять всю установку, а на выход выдавать все хэши и токены. Возможно генерировать сразу .env файл.
>
>Я думаю, достаточно только сгенерировать токены. В .env может быть еще куча других параметров. Но можно, конечно, выдавать заготовку .env файла.
>
>> Как обычно делаются такие файлы? Через Докер (не разу с ним не знакомился)?
>
>В dev среде можно использовать docker-compose для оркестрации докеров с отдельными приложениями. Докер, как правило, используется чтобы упаковать в образ программу с нужными ей библиотеками, например, определенную версию Питона или Ноды, чтобы ее не надо было устанавливать в систему руками. Код твоего приложения в докер-образ не кладется, а подмонтируется в него как внешний раздел. docker-compose заниамется тем, что просто запускает несколько докеров (например: микросервис авторизации и основное приложение). На Винде и Маке Докер запускает код в виртуальной машине с линуксом, а файлы прокидываются через сетевую файловую систему со всеми вытекающими.
>
>Ты можешь найти готовый пример приложения на PHP + nginx + mysql в докере и разберешься, я думаю.
Я ошибся когда писал, что у разных ролей wamp'а разные uri. Сейчас я сделал авторизацию действий засчет ролей и даже для сервера не нужно применять динамическую авторизацию.pic-4

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


>> Лучше сделать это на системном языке и чтобы он был кроссплатформенный. Можете что-нибудь посоветовать пожалуйста?
>
>Обычно используют Питон, bash для таких скриптов. Если у тебя код на Питоне, логично в нем сделать CLI скрипт для генерации токенов.
>Если у тебя код на Питоне
Страница выдаётся из Ноды, API я собираюсь переписать на PHP, а код wamp'а на Питоне.


>>1394587
>Такие запросы с джойнами будут плохо работать на больших нагрузках. Они же почти не оптимизируются индексами никак и требуют перебор строк. Возможно, тут придется сделать денормализацию, например, добавить в Dialog либо в Participant дополнительные поля. Чтобы, например, запрос бы имел вид
>
>SELECT FROM Participant WHERE user = ? AND partner = ? AND private =1
>
>Это ложится на индексы. Но, конечно, денормализацию стоит делать во вторую очередь.
Я думаю, что партнер должен добавляться скорее в ссылку на конференцию, потому что получатель это отдельная сущность, которая не отвечает за то с кем она должна вести диалог. А если партнёров будет много (публичная конференция)? Конечно тут можно прийти к созданию отдельной таблицы Partners... не будет ли и здесь плохо работать запрос с джоинами на больших нагрузках?

Далее, даже учитывая что мы создадим поле partner в Conference_Reference, это создаст запрос для получения вида:

// неизвестно какой пользователь сначала "создал" конференцию,
// а какой оказался получателем (партнёром)
SELECT FROM Conference_Reference WHERE (user = sender.id OR user = receiver.id) AND (partner = receiver.id OR partner = sender.id)

Такой запрос может вернуть две записи и сама его форма не элегантна.

Я предлагаю вернуться к идеи выше с массивами >>1387125

SELECT FROM Conference WHERE private = true AND (sender.id = ANY(participants) AND receiver.id = ANY(participants));

Как писалось выше такой запрос использует индексы (https://stackoverflow.com/questions/4058731/can-postgresql-index-array-columns).

У вас есть опыт с работой с индексами. Как вам кажется, индексирование массивов приведёт к желаемому повышению производительности?



>> Можно я оптимизацию БД оставлю на последнее?
>
>Можно. Но я просто хотел подчеркнуть, что мессенджеры это высоконагруженные штуки и там приходится об этом думать. И о шардинге, если ты хочешь, чтобы проект расширялся. Потому мы сначала проектируем нормализованную схему, а потом смотрим на типичные запросы и оптимизируем схему для них.
>
>Более того, часто под мессенджеры даже пишутся специализированные хранилища.
Я это прекрасно понимаю..

>Насчет reply - а недостаточно тут просто сделать поле в сообщении "replyToUuid" со ссылкой на исходное сообщение? Без вложений.
Судя по API телеграма там используется как раз такой подход ( https://core.telegram.org/bots/api#message ), но мне никогда не было понятно почему только можно ответить на одно сообщение, мне иногда хочется ответить на несколько сразу, да и выглядит это как прикрепление к сообщению.

>Так, схема выглядит хорошо. Позже стоит продумать операции, которые будут выполняться (их явно будет больше, например, может понадобиться постраничное получение участников огромного чата). И как их оптимизировать.
Опять же с массивами это тоже делается очень просто

SELECT participants[offset:limit] FROM Conference WHERE id = conference.id;


Мне хочется сделать, так же, и наследование и в конференциях, чтобы была отдельно приватная конференция и отдельно публичная. Публичная будет иметь свой приватный ключ (иначе шифровать сообщения для каждого отдельного пользователя будет затратно).


Также, я озаботился вопросом как работает с ключами protonmail, и оказалось, что он хранит их на сервере зашифрованные паролем пользователя, как я изначально и собирался это и делать. Я считал, что ключи должны храниться только у пользователя, но подход с генерацией ключей для каждого клиента пользователя создаёт только больше проблем - если пользователь часто меняет клиент (чистит кэш браузера/удаляет приложения/использует несколько устройств), это может превратиться в кошмарный список ключей и при зашифровки сообщения для них всех будет требовать всё больше и больше ресурсов.
Вынуждать пользователя самому заниматься менеджментом своих ключей было бы отталкивающе и тяжело доступно для понимания.

https://protonmail.com/support/knowledge-base/how-is-the-private-key-stored/

Я собираюсь повторить эту практику.

Такой подход вновь открывает возможность создания NoJS версии, но тратить время на это нужно только в случае, если за это можно выиграть лакомый кусочек пользователей. Или для надёжности, которые будут требовать текущие пользователи.


>> Я думаю, что пока подходы к авторизации методов WAMP не изучены, следует писать сырой код на if'ах, а подход с роутингом взять на заметку и держать в уме. А как думаете вы?
>
>Я, увы, так хорошо авторизацию в WAMP не знаю и сейчас не могу подсказать.
Рано или поздно мы разберёмся какая архитектура будет лучше.

P.S.
Я понимаю, что ваши советы по архитектуры базы данных содержат опыт, и я парой прихожу к тому что ваш совет, в конечном итоге, оказывается проще, но я всё равно нахожу либо ошибку в своих действиях в применении новых технологий, либо решение появляйщейся проблеме. Я чувствую, что я на правильном пути применяя новые технологии и идеи. В конечном итоге, это кристаллизуется в опыт и можно будет сравнить плюсы или минусы того или иного подхода.



Ответы: >>1397655 >>1398326
Аноним 2019/05/11 13:16:23  №1397655 8
>>1397648
Илюша,ты когда трезвый,такой добрый! кек
Аноним 2019/05/12 11:52:50  №1398326 9
>>1397648

> Я теперь понял, что импорты должны выполнятся там где выполняется этот запрос - это работает.

Общий принцип такой, что ты импортируешь только то, что как-то используешь именно в этом файле (вызываешь, создаешь объекты этого класса итд). Если не используешь - то не импортируешь.

> К сожалению, мне не удалось найти какой паттерн наследования использует psql

Эти паттерны наследования реализуются не на уровне Postgres, а на уровне ORM. И там в документации к SQLAlchemy перечислено несколько вариантов на выбор.

> Я косвенно предположил, что это именно он

Это Concrete TI, так как при STI таблица одна, а при Class TI в text_message были бы только те поля, которые отсутствуют в message.

> Использование UNION приводит к нагрузке?

Это надо смотреть EXPLAIN-ом и делать тесты, но скорее всего, да. Представь, что ты хочешь получить последние 10 сообщений: самый быстрый способ сделать это - это пройтись по индексу (где сообщения отсортированы по времени) и взять первые 10 записей из единственной общей таблицы. В случае с UNION нам надо брать по несколько сообщений из каждой таблицы и как-то объединять списки. Это в случае, если СУБД будет работать максимально оптимально, а это не факт.

Но вообще, выбор схемы наследования зависит от того, какие мы запросы делаем и какие у нас данные.

> Отношения между сущностями тяжело совершить с помощью встроенных инструментов и приходится делать метод для совершения запроса в ручную

Вообще, в документации есть похожий пример и там используются дополнительные параметры (foreign_keys): https://docs.sqlalchemy.org/en/13/orm/join_conditions.html#creating-custom-foreign-conditions

Может, в этом проблема?

Ведь ORM надо знать, как связывать объекты между собой. Допустим, у тебя есть объект класса A, и он связан отношением с объектом класса B. Если ты создаешь несколько объектов класса B, добавишь их в класс A, и попробуешь сбросить данные в БД, то ORM должен понять: как сохранить в БД связь между объектами?

Думаю, явное указание foreign_keys и remote_side как раз говорит ORM, что надо взять id из одного поля объекта и прописать его в поле другого объекта.

Но я могу ошибаться, так как лишь бегло пролистал документацию.

> При получении сущностей выдается массив из сначала сущностей родителя, затем сущности ребёнка.

Если ты используешь наследование, то Text_Message - это наследник Message, в соответствие с принципом Лисков наследник можно использовать вместо предка, и когда ты запрашиваешь список Message, то в нем будут и объекты Text_Message. Если ты это не хочешь, то тебе надо указать дополнительное условие (например: искать только Text_Message).

> Сейчас я сделал авторизацию действий засчет ролей и даже для сервера не нужно применять динамическую авторизацию

Только не забудь, что надо использовать принцип "белого списка" и все, что не указано явно - запрещается.

> Лучше сделать это на системном языке и чтобы он был кроссплатформенный

Вообще, "системное программирование" - это написание всяких модулей ядра, драйверов на языках вроде Си. А так, пиши на чем удобнее - на Питоне, JS или bash.

> А если партнёров будет много (публичная конференция)?

Часто в больших публичных конференциях список участников не показывают, пока ты не нажмешь какую-то кнопку. Потому, тут стоит учесть только такие варианты:

- получить полный список для маленькой конференции
- получить (по запросу пользователя) полный список для средней конференции
- если у тебя есть большие конференции (> 1000 - 5000 - 10000 польз.), то там может потребоваться получать список постранично

> не будет ли и здесь плохо работать запрос с джоинами на больших нагрузках?

Это зависит от того, как выглядит запрос. Одно дело - приджойнить 1 запись по ключу, другое дело - сджойнить два больших списка, к каждому из которых применяется какое-то условие.

> // неизвестно какой пользователь сначала "создал" конференцию,

Я думал, что у тебя там у каждого пользователя есть своя запись для конференции (со своим числом непрочитанных сообщений), и потому такой сложный запрос не нужен: мы ищем именно свою запись, а не любую.

> Как вам кажется, индексирование массивов приведёт к желаемому повышению производительности?

Без индексов тут нельзя - это я точно могу сказать. Но вид запроса очень неудачный, и скорее всего будет плохо индексироваться. Вот пример хорошего запроса:

SELECT WHERE a = 1 AND b = 2

Такой запрос при использовании UNIQUE INDEX (a, b) будет искать в индексе пару (1; 2) и будет оптимальным. Индекс выглядит так:

(a, b -> id)

Он отсортирован по возрастанию пар значений a; b.

А как выглядит индекс в твоем случае? Скорее всего, как отсортированный список (participant -> id). И как в нем найти соответствующие условию записи? Ищем по participant = sender, получаем большой список id, ищем по participant = receiver, получаем большой список id, ищем в 2 списках одинаковые id.

Твой запрос эквивалентен такому:

SELECT a.id FROM table AS a, table AS b WHERE a.participant = :sender AND b.participant = :receiver AND a.id = b.id

То есть, попробуй этот индекс нарисовать и придумать, как вообще в теории в нем найти нужную запись, и ты возможно увидишь, какие запросы будут работать быстро, а какие - в принципе не могут работать быстро.

Потому я и советовал поступать так:

- сделать нормализованную, не оптимальную схему
- сделать список запросов, которые будут выполняться
- оптимизировать схему под них

> но мне никогда не было понятно почему только можно ответить на одно сообщение,

Потому что это упрощает пользовательский интерфейс, наверно. Но твой подход тоже имеет право на жизнь. Кто знает, может это будет как раз преимуществом в сравнении с другими мессенджерами.

> Опять же с массивами это тоже делается очень просто

Только там происходит полная выборка списка в память. Если участников очень много (десятки тысяч), кто знает, может это неэффективно (нужно мерять).

> Я считал, что ключи должны храниться только у пользователя, но подход с генерацией ключей для каждого клиента пользователя создаёт только больше проблем

Это решается, если делать клиентское приложение на Electron (или на любой другой технологии) - оно работает на стороне пользователя и может хранить что угодно, при этом очистка кук ему не грозит. Но с синхронизацией да, придется помучаться.

Шифрование ключа паролем - это симметричное шифрование. Пароль относительно короткий и владелец сервера может попытаться его сбрутфорсить. Особенно, если у него есть "друзья" из NSA с дата-центрами, построенными специально для брутфорса паролей. Но можно придумать более интересные схемы синхронизации: 2 устройства одного пользователя одновременно выходят в сеть и передают ключ друг другу с использованием асимметричного шифрования, так, что сервер не может увидеть передаваемый ключ (т.к. при асиметричном шифровании используется случайный очень длинный ключ, как в HTTPS).

Это просто рассуждения на тему того, что тут можно сделать в теории.