Как стать автором
Обновить
18
0

Пользователь

Отправить сообщение
Ещё лет 5 назад на собеседованиях с backend-разработчиками был популярен вопрос: как переименовать колонку в высоконагруженной таблице (возможны вариации: как сделать любой другой alter table)? Ответ мог быть примерно следующий:

Зачем такие сложности с создаем новой колонки и постепенной миграцией? Почему бы просто не задать всем колонкам уникальное имя которое никогда не захочется поменять (случайный айдишник) а уже в коде хранить соответствие имя -> id при работе с этой бд. Тогда переименование колонки сведется к простейшей операции замены одного имени на другое в коде вообще без каких-либо затрат со стороны бд

вы только представляете а кто-то уже так делает и зарабатывает миллионы — вот один комментарий на HN который недавно набрал популярность в твиттере https://news.ycombinator.com/item?id=27454589


I currently have 10 fully remote engineering jobs. The bar is so low, oversight is non-existent, and everyone is so forgiving for under performance I can coast about 4-8 weeks before a given job fires me. Currently on a $1.5M run-rate for comp this year. And the interviewing process is so much faster today, companies are desperate, it takes me 2-3hrs of total effort to land a new job with thousands to chose from.

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


<div>
  ...
  <button onClick={()=> {
    project.tasks.push(new Task({text: project.newTaskText});
    project.newTaskText = "";
    ....
  }}>
      add task
  </button>
  ...
</div>

Тут можно сказать мол "да ты захламляешь шаблон как такое вообще читать и поддерживать когда мол верстка в перемешку с логикой?" на что я отвечу "а code folding (сворачивание) в редакторе кода для кого придумали?" Если хотите видеть/редактировать только верстку то можно легко свернуть весь код обработчиков и увидеть чистый шаблон. И наоборот — если хотите посмотреть что делает какая-то кнопка то перемещаем мышку влево на пару сантиметров и кликаем-раскрываем обработчик кнопки.


Это намного удобнее чем переходить в отдельный файл или скроллить экран вверх-вниз (когда выносим обработчик функции в другое место). Это также удобно в поддержке — если допустим у нас поменялся дизайн/ux/логика и поменялась верстка в результате которой нужно удалить какой-то блок вместе с кнопкой то я удаляю нужный кусок верстки и вместе с этим удалится и обработчик этой кнопки поскольку он записан инлайном — мне не нужно беспокоится а не удалил/почистил ли я различные обработчики в другом файле или других местах по коду


И то же самое с добавлением какой-то фичи — я пишу кнопку и тут же пишу обработчик этой кнопки рядом и мне не нужно думать над тем куда вынести этот обработчик а главное — какое имя придумать этому обработчику. И также это удобно при ревью кода — когда добавляется какая-то фича то в git diff видишь изменения только в одном месте а не размазаны по кусочкам на кучу файлов/мест (верстка в одном месте, обработчик в другом месте и т.д)

Как вебсокеты могут быть более тяжёлой штукой чем http-запросы? Вы понимаете как работает http? HTTP-запрос это точно такое же тсп-соединение как и вебсокеты и ничем не легче по "тяжести". И если интернет-соединение нестабильное то http-запрос будет точно также обрываться с ошибкой как и вебсокет-соединение (и соотвественно нужно делать повторный запрос/установку соединения), в этом плане я вообще не вижу никакой разницы.


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


Ну и наконец вебсокеты это очень простая спецификация (по сути хедер с размером сообщения поверх тсп-протокола) и написать парсер вебсокетов можно за пару часов (формат фрейма объясняют даже для новичков) в то время как написать парсер http (не говоря уже про http2) это приключение на месяц учитываю кучу разнообразных нюансов самого текстового http формата, так и специфику корректно обработки различных хедеров (в частности keep-alive)


А все эти REST/RPC PUT/DELETE/OPTIONS/GET/POST и прочее это просто уровни абстракции придуманные самими разработчиками для удобства. Если писать большое приложение на WebSocket-ах вы придумаете свои такие же.

Нет, такого беспорядка и дилеммы выбора как с http не будет, зачем? Зачем нужны эти имена методов и статусы кодов ошибок если можно просто передать скажем объект {action: "createProject", params: [...]} и вернуть объект {error: "..." | null, data: "..." | null} и вебсокет-обработчик на сервере просто вызовет нужную функцию и уже в самой функции будет происходит вся необходимая обработка — будь то получение (GET) или обновление данных (POST, PUT) или удаление (DELETE) или когда все эти действия происходят одновременно вместе с if-ами и циклами для батч-обработки каких-то данных (что просто не имеет соответствия "семантическим" методам в случае http). То есть исходная задача — вызывать какую-то процедуру на сервере и передать ей нужные параметры чтобы она обработала "запрос" и получила/обновила нужные данные в бд и вернула ответ назад с вебсокетами решается на порядок проще чем с http (где вызов нужной процедуры почему-то додумались делать через ручное придумывание и конфигурацию имен для роутов с маппингом на path/query http-запроса)

А объясните пожалуйста зачем какому-то условному новичку (который примерно понимает как работает веб, как ходят запросы и что такое tcp) использовать все эти express/koa/fastify/nest/etc nodejs-фреймворки? Вот стоит перед новичком задача — со стороны клиента получить какие-то объекты с данными из бд или отправить объект с метаданными (для того чтобы изменить данные в бд). Новичок и так будет знать что вся информация в вебе ходит поверх тсп-протокола (который уже обеспечивает целостность данных при передаче), но чистый тсп в браузерах недоступен и браузеры предлагают два варианта — http или websockets
И какой самый простой способ решить эту задачу? Зачем этому новичку следует выбирать всякие express/fastify/nest-фреймворки со всякими непонятными middleware/сontrollers поверх http/rest подходов со всякими тонкостями/нюансами/дилеммами выбора методов (post vs put), способа передачи информации (path/query/headers/body) и выбора кодов для ошибок, зачем все это если можно просто установить вебсокет-соединение и просто пересылать нужные объекты/json от клиента к серверу и обратно. То есть исходная задача (установка транспорта между клиентом и сервером чтобы пересылать данные) с вебсокетами решается на порядок проще без всяких фреймворков чем c этими http/rest-фреймворками вроде express/fastify/nest и со стороны новичка становится совершенно непонятно зачем выбирать тот же fastify вместо вебсокетов

Вот он — триумф вертикального масштабирования! In-memory база данных поверх такого процессора способна будет обрабатывать десятки, сотни миллионов или возможно даже миллиард serializable транзакций в секунду и способна заменить сотни/тысячи серверов (и тот огромный оверхед при реализации распределенных транзакций в случае горизонтального масштабирования) определенно точно найдет своего покупателя.
Интересно как в этом процессоре работает механизм cache-coherence который обеспечивает CAS ("compare-and-swap") и другие атомарные обновления памяти? Cache-coherence это другими словами реализация атомарного бродкаста и поскольку это и так самая сложная часть при проектировании современных процессоров то здесь при 850 тысяч ядрер и возможном отказе этих ядер по отдельности проектировщики должны были выйти на уровень распределенных систем (интересно они реализовали paxos в железе?)

а чем плох вариант просто вызывать в цикле функцию вроде parseNextHttpRequest(buf) с передачей буфера и получением словаря http-хедеров и значений (а также статуса и тела). Вполне себе органично вписывается в тот пример кода выше где вместо вызова do_use_fd(..) будет вызов parseNextHttpRequest а потом уже обработка самого http-запроса вызовом еще одной функции

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

А чем плох вариант просто пробросить системный вызов epoll в линуксе? Linux уже предоставляет возможности асинхронного io виде синхронного вызова epoll_wait в бесконечном цикле. Вот я нашел такую библиотеку (https://github.com/chopins/php-epoll), какие есть недостатки у такого подхода ?


for (;;) {
    $nfds = $epoll->wait($events, MAX_EVENTS, -1);
    if ($nfds == -1) {
        perror("epoll_wait");
        exit(EXIT_FAILURE);
    }

    for ($n = 0; $n < $nfds; ++$n) {
        if ($events[$n]->data->fd == $listen_sock) {
            $conn_sock = stream_socket_accept($stream);
            if (!$conn_sock) {
                perror("accept");
                exit(EXIT_FAILURE);
            }
            stream_set_blocking($conn_sock, false);
            $ev->setEvent(Epoll::EPOLLIN | Epoll::EPOLLET);
            $connFdno = $epoll->getFdno($conn_sock, Epoll::RES_TYPE_NET);
            $ev->setData(['fd' => $connFdno]);
            if ($epoll->ctl(Epoll::EPOLL_CTL_ADD, $connFdno,
                        $ev) == -1) {
                perror("epoll_ctl: conn_sock");
                exit(EXIT_FAILURE);
            }
        } else {
            do_use_fd($events[$n]->data->fd);
        }
    }
}

Понятно что это только работа с голым tcp но ведь можно подключить какую-нибудь библиотеку которая будет парсить из tcp-потока http-запросы и таким образом получим на php асинхронный http-сервер который будет не хуже чем в NodeJS (и также это избавляет от необходимости использовать Apache или Nginx перед php)

Использование этой простой стратегии делает компоненты более декларативными и упрощает понимание их кода.

Серьезно? Что же, давайте более подробно проанализируем этот кусок кода


import React from 'react'

const Student = ({ name }) => <p>Student name: {name}</p>
const Teacher = ({ name }) => <p>Teacher name: {name}</p>
const Guardian = ({ name }) => <p>Guardian name: {name}</p>

const COMPONENT_MAP = {
    student: Student,
    teacher: Teacher,
    guardian: Guardian
}

export default function SampleComponent({ user }) {
    const Component = COMPONENT_MAP[user.type]

    return (
        <div>
            <Component name={user.name} />
        </div>
    )
}

Сколько раз ревьюеру (который видит этот кусок кода первый раз) нужно будет промотать глазами чтобы понять что происходит в коде?
1) видим строчку COMPONENT_MAP[user.type] — ага, нужно найти взглядом эту переменную из внешнего скоупа и понять что там хранится,
2) видим строчку рендера какого-то компонента <Component name={user.name} /> — опять нужно найти взглядом переменную из внешнего скоупа и понять что записано в переменную Component.
3-6) и наконец по значениям-ссылкам на три компонента которые были записаны в COMPONENT_MAP мы теперь должны найти каждый компонент и понять что он рендерит


А теперь сравним это с такой версией


import React from 'react'

export default function SampleComponent({ user }) {
    return (
        <div>
          {user.type == "student" && 
             <p>Student name: {user.name}</p>
          }
          {user.type == "teacher" && 
             <p>Teacher name: {user.name}</p>
          }
          {user.type == "guardian" && 
             <p>Guardian name: {user.name}</p>
          }
        </div>
    )
}

Сколько раз в этой версии разработчику нужно будет мотать глазами чтобы понять что происходит в коде? Не проще ли становится код когда вообще не нужно мотать глазами? Не является ли вторая версия более "декларативной" ?

А что вы скажете насчет веб-компонентов? Разве они были придуманы не для того чтобы избавиться от проблемы ограниченного набора и неоднозначности "семантических" html-тегов? Почему вместо разнообразных <div>, <span>, <header>, <footer>, <main>, <section>, <article>, <h1>, <ul>, etc нельзя использовать кастомные веб-компоненты которые будут лучше подходить по семантике и функционалу к конкретной ситуации и лучше соответствовать дизайн-системе и семантически-функциональной структуре сайта/веб-приложения?

А кто-нибудь знает почему не взлетела идея с FPGA? Ведь тогда каждая программа могла бы сама определять наиболее эффективные для своего исполнения инструкции, кеши и пайплайны. Или, например, с++/rust компилятор после всевозможных оптимизаций мог бы компилировать программу не в набор ассемблерных инструкций а сразу в схему транзисторов и связей которая будет максимально эффективна для конкретной программы

А почему такой скромный заголовок? Судя по тому что в википедии написано что температура ядра солнца всего лишь 15 млн градусов то достижение в 100 млн градусов надо было назвать как-нибудь "Ученые создали и двадцать секунд удерживали самое горячее вещество в солнечной системе, в 7 раз горячее солнца!!!"


А вообще цифры конечно впечатляют и сразу возникает такой вопрос почему так несимметрично — значит если нагревать так и до 100 млн можно а если охлаждать то всего лишь до -273 градусов и больше нельзя (так как абсолютный ноль), разница аж целых 6 порядков!

Ну вот я использую голый реакт и получается даже более удобно чем с mobx, вот пример — https://codesandbox.io/s/competent-engelbart-cltwg. Нет никакой магии геттеров-сеттеров или прокси-объектов, просто вызываем функцию актуализации когда нужно актуализировать view с состоянием. Есть и недостаток в виде менее эффективного обновления (реакт будет делать diff всего приложения при изменении состояния). Но тормоза появляются лишь на больших приложениях (> 10к дом-нод) что встречается нечасто и для 99% mvp-приложений можно обойтись без mobx (и потом можно легко подключить его только в случае появления тормозов diff-механизма реакта).

Сравнение некорректное. Java это язык а нода это рантайм (конкретная реализация взаимодействия с IO операционной системы). И скорость ноды и этой хваленый event loop и т.д — это все заслуга системного IO операционной системы — например на линуксе это системный вызов epoll_wait(..) — который позволяет одним потоком обслуживать много сокетов. Это значит что аналогичную эффективность NodeJS (скорости работы с io и обслуживание одним потоком многих tcp/http запросов) можно получить на всех популярных языках — не только на java но и например на php — достаточно всего лишь прокинуть системные вызовы epoll_wait/epoll_create/… в интерпретатор или компилятор языка.

Как известно, в Node.js реализовано однопоточная событийно-ориентированная модель. Это отлично подходит для большого количества запросов, но совершенно не годится для распараллеливания вычислений или реализации сложной бизнес-логики.

Вы серьезно? Тогда такой вопрос — как вы собираетесь поддерживать атомарность при выполнении сложной бизнес-логики? Многопоточная модель это прямой путь к race-conditions и неконсистетности данных. Вы знаете что в базах данных (включая postgres, mysql) по умолчанию не включен serializable уровень изоляции и поэтому любую бэкенд-логику которая работает с бд отдельными запросами нужно врапить в эти транзкции (от начала и до конца) иначе рано или поздно у вас появятся race-conditions и неконсистентность данных (https://www.youtube.com/watch?v=5ZjhNTM8XU8)?
А теперь вопрос — какой тип бд позволяет обрабатывать транзакции с любой логикой (запросы к различным таблицам, включая условия и циклы) c seriazlizable-уровнем изоляции со скоростью > 100к транзакций в секунду? Вы не поверите но это такие базы данных которые выполняют все транзакции в одном потоке и вполне могут быть написаны на Node.js. Яркий пример такой бд — это Tarantool (https://www.youtube.com/watch?v=yrTF3qH8ey8)


А что касается скорости одного потока javascript — то он вполне находится на уровне с++ (если не относиться наплевательски на советы по производительности) — вот доклад где сравнивается с++ и js — https://www.youtube.com/watch?v=UJPdhx5zTaw и js всего лишь на 17% медленнее чем с++ с O3-флагом оптимизации (и это было еще в 2012 году)

Вспомнил ваш ник в статьях CodeRush :) Вы случайно не в курсе что изменилось с тех пор? Насколько сложно сейчас вирусу с правами админа или рута пережить переустановку ос? И кстати если ли смысл как-то защищаться на этом уровне? Мол из-за таких защит сам юзер (имея права админа) уже не сможет просто обновить bios и перезагрузить — ему придется делать дополнительные манипуляции (писать на флешку, зажимать горячие клавиши при включении и т.д). И если мы не хотим доставлять неудобств юзеру то появляются такие уже мысли — мол пусть операционные системы становятся менее дырявыми и делают гранулярные права на те или иные возможности (как в ios и android), и если уж программа получила права рута так лишь бы не перезатерла одноразовые fuses в биос — чтобы можно было при переустановке (или покупке б/у) вычистить/сбросить весь перезаписываемый код в биос (и других разнообразных firmware-областях)

У вас в таком коде


socket.on('data', (data: Buffer) => {
  if (data[0] === this.OPCODE.SHORT_TEXT_MESSAGE) { // Обрабатываем в данном примере только короткие сообщения
    const meta = this.decryptMessage(data);
    const message = this.unmasked(meta.mask, meta.data);
    this.connections.forEach(socket => {
      this.sendShortMessage(message, socket);
    });
  }
});

не учитывается фрагментация когда буфер полученный в обработчике 'data'-события может быть длиной вплоть до одного байта

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

Вы серьезно? Знаете что я делаю когда мне нужно отобразить сложные формы как в вашем случае когда у нас "в общей сложности более 80 разных полей"?
Я не использую никакие стейт-менеджеры или библиотеки для форм я просто использую обычный реакт и в каждом обработчике поля onChange пишу примерно такое


<input onChange={(e)=>{
   AppState.form.someField = e.target.value;
   //валидация или другая логика
   actualize() //однострочный хелпер который вызывает ReactDOM.render(<App/>, el)
}}/>

Увидев вызов ReactDOM.render(<App/>), вы наверное подумаете "о боги, это же перерисовка всего приложения при вводе каждой буквы!". Поэтому позвольте мне напомнить что такое "перерисовка" в терминах реакта. В реакте "перерисовка" это просто рекурсивное сравнение двух деревьев js-объектов чтобы изменить в html/dom только то что отличается. Сравнение очень быстрое — никаких медленных обращений к дом-элементам, алгоритм линейный (просто спуск по дереву объектов и сравнение свойств с соответствующим объектом от предыдущего вызова перерисовки), сам javascript умеет компилироваться в ассеблер и его скорость на уровне с++ или даже быстрее (https://www.youtube.com/watch?v=UJPdhx5zTaw). В общем за 1мс можно "перерисовать" или точнее сравнить дерево из 50к объектов. И на этом фоне ваши желания оптимизировать вызов "перерисовки" отдельных компонентов для формы из всего лишь 100 полей выглядят забавно

При этом архитектуру нужно было заложить с запасом прочности, в расчете на будущее расширение

И поэтому вы выбрали микросервисы? Вы не видите в этом противоречие? Микросервисы это изоляция по функционалу и по данным (каждый микросервис должен хранить данные в своей базе данных иначе считается что это не настоящие микросервисы). Ок, вы построили архитектуру и разбили по микросервисам — например платежами/переводами занимается один сервис а данными юзеров занимается второй сервис. Правильно? А потом на следующий день прилетает задача — вот мы хотим добавить программу лояльности и начислять юзеру какие-то баллы за переводы. И теперь для реализации этой задачи микросервису переводов нужно общаться с микросервисом который хранит данные юзеров. А теперь вопрос — как вы будете решать race-conditions и атомарное выполнение этой бизнес-логики? Речь не только про потерю связи, логику retry-ев на транспортном уровне (https://habr.com/ru/company/yandex/blog/442762) а про более фундаментальную проблему консистентной обработки данных и serializable уровня изоляции транзакций — https://www.youtube.com/watch?v=5ZjhNTM8XU8
В общем микросервисы можно применять только когда проект уже устоялся и не планирует расширяться, иначе добавление нового функционала имеет тенденцию увеличивать связность данных а это в свою очередь требует атомарного выполнения бизнес-логики которая обращается к разным микросервисам и реализации распределенных serializable-транзакций (иначе привет race-conditions и неконсистетность данных и дыры в безопасности)

1
23 ...

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность