Pull to refresh
89
0
Сергей Шашков@ShashkovS

Менеджер продукта, методист, разработчик

Send message

У меня не супер-сложная задача на большую C-кодовую базу.
Новая версия пока на все мои запросы через полторы минуты выдаёт «не шмогла ничего сделать за отведённое время».
Предыдущая делала правки.
Так что пока непонятное сравнение для моего проекта.

Зато диверсификация. Нет завязки на конкретную платформу, хоть и разработка с поддержкой дороже.

Маркетинг. Если данные есть, то в день рожденья можно предложить всем контактам купить в подарок премиум.

А-ха-ха-ха. Они что, думают, что компании гоняют в контейнерах print('Hello, world!')?

Примерно любой мало-мальский бекенд — от десятка до нескольких десятков библиотек. У каждой зашиты ограничения на версии, и иногда это python<=3.10.
И вот ты уже не можешь просто перещёлкнуть версии, тебе нужно бампить версии библиотек. А там начинаются потери обратной совмести. Обновил условный bcrypt, и вот твой бекенд падает, когда пользователь вводит длинный пароль. Потому, что в старой версии просто брались первые 72 символа пароля, а в новой он кидает тебе exception, который ты не ждал.

И это если вообще получилось разрешить зависимости, и код запустился.

Или был у меня бекенд на Sanic ещё на Python 3.9. Памяти жрёт мало, работает быстро.
Обновляю на Python 3.12, нужно обновить Sanic. Обновил Sanic, тесты проходят. Пускаем в прод.
Смотрю графички: а он жрёт в два раза больше оперативки, причём по ней очень скачет, и в два раза больше нагрузка на CPU. То есть все тесты прошли, но по факту стало гораздо хуже.

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

Ну, может и мудак, но я очень одобряю идею не писать make_u32_from_two_u16 вместо (a << 16) + b, в том числе по причине, описанной в его комментарии.

Могу сказать как краевед, который считал эти копейки много лет (и как математик).
Ни в коем случае нельзя считать проценты на остаток добавляя какую-то дополнительную точность (условно 3 знака после запятой): всё разъедется через некоторое время с вероятностью 100%.

Правильное вычисление выглядит так:
Нужно каждый раз считать полную сумму с нужной итоговой разрядностью «как бы заново» и вычитать предыдущее значение.

Условно, если у нас на вкладе 10.00 рублей и ставка, скажем, 16.5%, и прошло, условно, 17 дней месяца, то должно быть добавлено 10.00 * 16.5 * 17 / 100 / 365 (или 366 в високосный год).
Вот эта штука 10.00 * 16.5 * 17 / 100 / 365, если в результате нужно округлить к целому по математическим правилам, считается точно при помощи несложной арифметики в целых числах.
Получится 0.08 (8 копеек). Значит после 17 дней, у нас 10.08.
После 18 дней слова будет 10.08. А вот уже после 19-го дня будет 10.09 рублей.

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

Код не обфусцирован, просто открывайте и читайте

Уж лучше через связывание в параметр по умолчанию. Читается получше...

Там мультиподпись. Нужно передать "файлик" через всех подписывающих. Там же наверняка "серьёзные" дяти, не через тележечку они его перекидывают? Значит, есть какой-то программный интерфейс для передачи его по кругу. И на этот интерфейс можно совершить атаку.
Может и не веб, конечно, но веб просится первым.

Насколько я понимаю, устроено всё так.
Там холодные кошельки с мультиподписью.
У каждого из N человек есть девайсина.
На одном из устройств создаётся черновик транзакции.
Дальше файл с черновиком транзакции должен пройти через холодный кошелёк каждого подписанта, и каждый её должен подписать.
В конце файл с подписанной всеми транзакцией переносится на устройтво с доступом к интернету, проверяются подписи, делается транзакция.

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

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

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

Сначала делаем обход в глубину с отметаками времени входа-выхода для дорог одного типа.

Это позволяет за O(1) отвечать на вопрос "является ли эта вершина предком этой", что для нашего графа эквивалентно "если ли из первой ж/д первого типа во вторую".

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

А где хоть какое-то сравнение производительности по сравнению с однопоточным вариантом?

Ну а куда они могут без юр.лица в РФ и без "помощи" визы или мастеркарда деть сунуть рубли?
Хотя бы по ip не банят --- уже хорошо.

Это --- временное ограничения из-за атаки на их инфрастуктуру.

У вас устаревшие данные.

import os
import sqlite3
import time
import multiprocessing
import uuid


def writer_process(proc_id, num_inserts, db_file):
    conn = sqlite3.connect(db_file)
    conn.execute("PRAGMA busy_timeout = 5000")
    conn.execute("PRAGMA synchronous = NORMAL")
    for _ in range(num_inserts):
        with conn:
            conn.execute("INSERT INTO test_data (val) VALUES (?)", (str(uuid.uuid4()),))


def run(num_processes, num_inserts, test_id=1):
    start_time = time.perf_counter()
    db_name = f'db{test_id}.sqlite'
    conn = sqlite3.connect(db_name)
    conn.execute("PRAGMA journal_mode = WAL")
    conn.execute("CREATE TABLE IF NOT EXISTS test_data (id INTEGER PRIMARY KEY, val TEXT)")
    conn.commit()
    conn.close()

    processes = []
    for i in range(num_processes):
        p = multiprocessing.Process(target=writer_process, args=(i, num_inserts, db_name))
        processes.append(p)

    print(f'Тестируем {num_processes} процессов')
    for p in processes:
        p.start()

    for p in processes:
        p.join()

    os.unlink(db_name)

    total_time = time.perf_counter() - start_time
    total_inserts = num_processes * num_inserts
    print(
        f"{num_processes} процессов. Общее время: {total_time:.4f} секунд. Суммарная скорость: {total_inserts / total_time:.2f} транзакций/сек")


if __name__ == "__main__":
    num_inserts = 277200
    for num_processes in range(1, 21):
        run(num_processes, num_inserts // num_processes)

У меня максимум на 4 одновременных процессах --- 70К транзакций в секунду.
Дальше до 20 процессов --- около 30К транзакций в секунду. И это в режиме, когда все процессы только и делают, что пишут в одную общую нагруженную таблицу.

Если вы пишете асинхронный вебсервис, то там будет столько воркеров, сколько у вас физических ядер. То есть до 20 воркеров --- всё ок, это достаточно высокая нагрузка.
Не high load, конечно, но..

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

Про API так вообще очевидно: они для доступа к API используют библиотеку openai, и типичный код выглядит так:

from dotenv import dotenv_values
from openai import OpenAI

config = dotenv_values(".env")

BASE_URL = "https://api.deepseek.com/beta"
MODEL = "deepseek-chat"

client = OpenAI(api_key=config['API_KEY'], base_url=BASE_URL)

response = client.chat.completions.create(
    model="deepseek-chat",
    messages=[
        {"role": "system", "content": "You are a helpful assistant"},
        {"role": "user", "content": "Hello"},
    ],
)

print(response.choices[0].message.content)

То есть их публичные API совместимы с OpenAI'шными. Что просто супер-удобно, ведь имеющийся для OpenAI-код можно легко применить к их модели.

Ой, ещё как считают. У нас есть специальный чатик, куда мы double-дичь с денежками скидываем. Кажется уже пару десятков примеров.

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

Строго говоря x+0.5 не всегда работает правильно.

> round = (x) => Math.floor(x + 0.5);
function round(x)

> x = 0.49999999999999994;
0.49999999999999994
> x
0.49999999999999994
> Math.round(x)
0
> round(x)
1 
1
23 ...

Information

Rating
Does not participate
Location
Россия
Date of birth
Registered
Activity

Specialization

Бэкенд разработчик, Менеджер продукта
Ведущий
Python
Управление проектами
Алгоритмы и структуры данных
Asyncio