Задать вопрос
Местоположение
Япония, Осака, Осака

Наибольший вклад в теги

Все теги (10)

Лучшие ответы пользователя

Все ответы (14)
  • Ошибка Inappropriate blocking method call при http запросе в Kotlin. В чем проблема?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    Если по каким-то причинам нет возможности заменить блокирующий код неблокирующим аналогом (например, драйвер БД, для которого асинхронной версии просто не успели ещё написать), то можно использовать такое решение:
    1. Создаёте отдельный тредпул
    2. Все вызовы блокирующего кода оборачиваете в блок
    suspendCoroutine { continuation ->
        //
    }

    3. Внутри этого блока создаёте Runnable, который и будет выполнять ваш блокирующий код. После выполнения блокирующего кода вызываете либо continuation.resume(result), либо continuation.resumeWithException(e)
    4. Скармливаете полученный Runnable тредпулу.

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

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

    Из очевидных минусов:
    • количество потоков больше, чем ядер CPU
    • количество одновременно выполняемых блокирующих методов = количеству потоков в этом тредпуле
    • следите за тем, что бы этот тредпул был ограничен сверху
    .

    Насколько я помню, еще ~1.5 года назад примерно такая схема применялась в котлиновских обёртках над спринговыми драйверами то ли к монго, то ли к редису (там было 3 версии драйвера - блокирующая, реактивная, и блокирующая, но обёрнутая в отдельный тредпул, как я описал). Как сейчас - не знаю.
    Ответ написан
    1 комментарий
  • Java junior, серверное ПО, какие задачи решать, чтоб понять устройство системы?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    Если уже есть небольшой опыт работы со спрингом, то советую посмотреть "Spring-потрошитель" (2 части) от Евгения Борисова на ютубе. Он хорошо объяснил как спринг устроен изнутри. Магии много, на разобраться можно.

    После этого можно начинать читать документацию по интересующим модулям спринга. Она обычно очень подробная и понятная. Вот например документация по Spring Jpa.

    После этого попробуйте с нуля написать какое-нибудь простое приложение на спринге не используя туториалы, quick-старты и spring initializr, только maven/gradle (смотря что у вас в команде используется) и документация. Скажем, 3 пользовательских роли (админ, пользователь, аноним), 3 эндпоинта: 1 доступен для всех, 1 доступен админу и пользователю, 1 доступен только админу. Для начала пользователей можно захардкодить. Потом прикрутить постгрес для их хранения, потом прикрутить эластик для логов и т.п.
    После этого добавить несколько тестов, причём как тестов, которые напрямую дёргают методы сервисов и репозиториев, так и тесты, которые эмулируют пользователя: отправляют http-запросы на вход в систему, а потом дёргают эндпоинты и проверяют, что ограничения эндпоинтов по ролям работают правильно.

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

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

    Понимание рекурсии и базовых алгоритмов полезно, но в корпоративном сегменте это будет встречаться не часто: бизнес процессы в большинстве случаев будут расписаны либо бизнес-аналитиком, либо самим заказчиком, а большинство структур данных и алгоритмов работы с ними уже доступны в стандартной библиотеке, Apache Commons Lang 3 и Google Guava.
    Ответ написан
    Комментировать
  • Как оптимизировать код Java при работе со строчками?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    Про кеширование
    Если у вас condition бОльшую часть времени имеет малый набор возможных значений, то кешированием в данном случае будет создание хешмапы, где ключ - condition, значение - condition + ",". Можете даже использовать какой-нибудь LRU-кеш, что бы редко используемые ключи постепенно удалялись из кеша и не занимали память.

    Но если честно, в данном случае операция condition + "," настолько незначительна по вычислительной сложности, что хоть какое-то изменение вы заметите только если у вас этот участок кода вызывается в горячем цикле и генерирует столько мусора, что паузы GC перевешивают дополнительные вычисления хеша, ветвления и добавление новых данных в кеш. Но даже в этом случае нужно профилировать, например с помощью JMH и VisualVM.

    Теперь по поводу StringBuilder.
    Сразу скажу, что у меня нет под рукой IDE для Java, что бы проверить то, что будет написано дальше, все выводы сделаны только глядя на исходный код классов String и StringBuilder. Итак, поехали:
    • Код condition + "," будет преобразован компилятором в
      new StringBuilder().append(condition).append(",").toString()
      Это сразу +2 созданных объекта: StringBuilder и новый String, который будет создан в результате вызова метода StringBuilder.toString()

    • a = a.replace("^Var1:", newString); внутри себя тоже создаёт StringBuilder, у которого потом вызывается метод toString() - еще +2 объекта

    • a = a + System.lineSeparator(); опять же будет преобразован в StringBuilder + toString()


    Итого, у вас создаётся минимум 6 объектов.

    Если переменная condition может иметь произвольное значение (т.е. операции с ней нет смысла кешировать, так как всё-равно придётся каждый раз вычислять новое значение), то можете попробовать воспользоваться методом condition.concat(String str) - будете создавать только 1 объект вместо 2. Будет ли это быстрее? Не знаю, надо профилировать.
    Далее, после входа в блок if создаём новый StringBuilder вместимостью a.length() + condition.length() + 2. Если не указать длину строки, то вместимость у StringBuilder будет либо 16 символов, либо равной длине значения в переменной a (в зависимости от того, какой конструктор будет использован). И при конкатенации с новыми строками скорее всего возникнет необходимость увеличивать размер буфера, что приведёт к дополнительному копированию массивов (создать буфер с новым размером, скопировать содержимое старого буфера в новый). Поэтому желательно сразу рассчитать правильную длину результирующей строки. В любом случае, это тоже +1 объект.
    a.replace(CharSequence target, CharSequence replacement)
    можно заменить методом
    StringBuilder.replace(int start, int end, String str)
    (придётся вручную вычислить начало и конец подстроки, которую вы хотите заменить).
    Вместо a = a + System.lineSeparator(); используйте a.append(System.lineSeparator()).
    out.append(a); приведёт к созданию еще 1 объекта - либо вы вручную вызовете a.toString(), либо этот метод будет вызван за вас в методе out.append(CharSequence seq). И скорее всего приведёт еще и к копированию массива символов внутри переменной out, если вы заранее не указали вместимость с запасом.

    Итого, 3 объекта против изначальных 6 с несколькими не сразу очевидными НО. Стоит ли усложнение кода полученной экономии? Не уверен, надо профилировать. Ну и учтите, что производительность самого StringBuilder может быть разной, в зависимости от того, как вы его используете.

    Имхо, то что вы пытаетесь сделать - это экономия на спичках, которая может помочь только если у вас по настоящему высоконагруженный сервис.
    P.S. если вы пишете библиотеку, которая потом будет использоваться другими проектам, то я всеми конечностями за, если вы решили заморочиться с оптимизацией.
    Ответ написан
    1 комментарий
  • Как получить часть документа mongodb?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    Для этой задачи вам надо использовать проекцию. Их в монге несколько видов:

    1. Используется в запросах find, findOne, findOneAndDelete, findOneAndReplace и findOneAndUpdate
    2. Используется для фильтрации элементов массива
    3. Используется как отдельная стадия аггрегации


    1 тип аггрегации позволяет только скрыть лишние элементы из ответа. Если воспользоваться этим типом, то запрос будет выглядеть вот так:
    db.subjects.find(
    	{"ФизМат.Педагогические науки.B009": {$exists: true}}, // Из всех документов (если у вас их несколько, а не один огромный документ), выбрать те, у которых существует поле "ФизМат.Педагогические науки.B009"
    	{
    		_id: 1, // У найденного документа вернуть только поле _id
    		"ФизМат.Педагогические науки.B009": 1 // и поле "ФизМат.Педагогические науки.B009"
    	}
    )


    Ответ на такой запрос:
    {
      "_id": ObjectId("600fa6f3101fa920a8575e6f"),
      "ФизМат": {
        "Педагогические науки": {
          "B009": {
            "code": "B009",
            "name": "Подготовка учителей математики",
            "max": "140",
            "min": " 107",
            "minWithQuota": " 95"
          }
        }
      }
    }


    т.е. будут скрыты все поля, кроме _id и ФизМат.Педагогические науки.B009, никаких дополнительных преобразований над документом проводиться не будет.

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

    3 тип - как стадия аггрегации. Аггрегация в монге - довольно мощный инструмент, который позволяет группировать результаты, преобразовывать их, делать многоуровневый поиск и т.п.
    В данном случае сам запрос будет выглядеть совершенно по другому:
    db.subjects.aggregate(
    [
      { 
        $match: { // Эта стадия ищет документы, подходящие под условие
          "ФизМат.Педагогические науки.B009": {$exists: true} // В документе есть поле "ФизМат.Педагогические науки.B009"
        } 
      },
      { 
        $project: { // Стадия проекции
          _id: 1, // Вернуть id документа
          value: "$ФизМат.Педагогические науки.B009" // Вернуть embedded документ в поле value
        }
      },
    ])


    Ответ в таком случае будет выглядеть вот так:
    {
      "_id": ObjectId("600fa6f3101fa920a8575e6f"),
      "value": {
        "code": "B009",
        "name": "Подготовка учителей математики",
        "max": "140",
        "min": " 107",
        "minWithQuota": " 95"
      }
    }
    Ответ написан
    1 комментарий
  • Как сделать запрос к mongodb для фильтрации по типу поля?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    В данном случае надо использовать оператор $not для отрицания равенства. Пример данных:
    db.test.insertMany([
      {
        "field1": "abc",
        "field2": [
          {"embeddedField": "a"},
          {"embeddedField": "b"},
          {"embeddedField": "c"}
        ]
      },
      {
        "field1": "xyz",
        "field2": {
          "embeddedField1": "x",
          "embeddedField2": "y",
          "embeddedField3": "z"
        }
      }
    ])


    Запрос:
    db.test.find({"field2": {$not: {$type: "array"}}})

    Результат:
    {
      "_id": ObjectId("..."),
      "field1": "xyz",
      "field2": {
        "embeddedField1": "a",
        "embeddedField2": "b",
        "embeddedField3": "c"
      }
    }
    Ответ написан
    Комментировать