• Как правильно использовать Attach to process в intellij idea?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    Если вы запускаете Jetty не изнутри IDEA, а как отдельное приложение, а потом подключаетесь к нему, то в первую очередь проверьте, что вы включили Remote Debug в Jetty.
    Далее, убедитесь, что на сервере и в идее у вас один и тот же код плагина. Сильно упрощая, при remote debug'е, IDE сообщает серверу - останови выполнение, когда достигнешь строки N в классе X. Соответственно, если на сервере крутится один код, а в IDE открыт другой код, то класса X на сервере может не быть, либо строка N может быть no-op (пустая строка или открывающая/закрывающая скобка) из-за того, что какой-то метод был изменён или где-то изменён комментарий, из-за чего строки съехали по сравнению с кодом на сервере.
    Ответ написан
  • Как оптимизировать код 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 комментарий
  • Как выставить параметр по умолчанию?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    В данный момент такая возможность отсутствует, но в джире MongoDB несколько лет назад было небольшое обсуждение о такой возможности.
    Пока такая возможность не появится, лучшее, что можно сделать - проставлять значение по умолчанию в коде, который создаёт/сохраняет новый документ в БД.
    Ответ написан
    Комментировать
  • Как сделать запрос к 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"
      }
    }
    Ответ написан
    Комментировать
  • Как получить часть документа 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 комментарий
  • Как указать несколько пар условий в запросе по списку?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    Про хешированные индексы
    Судя по документации, MongoDB не поддерживает хешированные индексы для массивов.
    Хешированные индексы нужны для того, что бы более равномерно распределить данные, которые расположены близко к друг другу. Их часто применяют при шардировании коллекций (когда одна коллекция разбита на несколько частей, которые хранятся на разных серверах). В таком случае, запросы на выбор этих данных не будут идти всегда на один и тот же сервер. Но если у вас запросы вида поле1 > значение1 && поле1 < значение2, то индекс использоваться не будет (потому что в таком случае монге надо будет сгенерировать все возможные промежуточные значения и посчитать для них хеши, что невозможно).

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

    Про индексы в целом
    В целом, задача индекса в том, что бы после первичного поиска по нему осталось как можно меньше документов для последующего перебора, поэтому рекомендуется строить индекс по полям, которые у вас гарантированно участвуют в каждом запросе.
    Скажем, у вас в коллекции проиндексированно только поле username и вы выполняете свой запрос вида
    $or: [
    ["username" : "user1", tags: "tag4"],
    ["username" : "user2", tags: "tag1"],
    ["username" : "user2", tags: "tag3"],
    ]

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

    Получается, лучше делать составной индекс username + tags?
    Не всегда. При использовании в индексе поля типа Array, на каждый элемент массива будет создана отдельная запись в индексе.
    Если у вас мало документов, у которых поле username имеет одинаковое значение, но при этом в массиве tags может быть много значений, и значения внутри одного массива могут повторяться, то использование составного индекса приведёт к большему количеству одинаковых записей в индексе, чем если бы вы использовали индексацию только по полю username.

    Отсюда же вытекает другое ограничение: в составном индексе только одно из полей может быть массивом, иначе в индексе на каждый документ пришлось бы создавать N ・ M записей, где N и M - размеры массивов, которые участвуют в построении индекса.

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

    Отдельная коллекция с хешами
    Транзакционность в MongoDB есть только на уровне отдельного документа. А это значит, что вам придётся самому следить за консистентностью данных в обеих коллекциях, что может обернуться той ещё головной болью: что вы будете делать, если документ был отредактирован, но информация в связанной коллекции по каким-то причинам не была обновлена (например, прервалась связь с БД или приложение упало из-за нехватки памяти)? Поэтому лично я в общем случае такой подход не рекомендовал бы.

    Так что же делать?
    Самое простое и эффективное, что вы можете сделать - поднять монгу локально, сгенерировать в ней реалистичные данные, поиграться с разными настройками индексов и замерить скорость выполнения запросов.
    Дополнительно можете воспользоваться методом explain, что бы посмотреть статистику выполнения запроса.
    Ответ написан
    Комментировать
  • System.ObjectDisposedException: "Доступ к закрытому файлу невозможен." не понятно, что не так?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    Согласно документации, метод Close у StreamWriter самостоятельно закрывает нижележащий поток (в вашем случае это FileStream). И при закрытии нижележащего потока StreamWriter пытается записать в него всё, что, возможно, успело накопиться в буфере,но еще не было записано (строка
    System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
    в вашем стектрейсе).
    Но так как файловый поток уже закрыт, вызов метода Flush приводит к ошибке.

    Совет на будущее - закрывайте потоки в порядке обратном тому, в котором вы их открывали. Т.е. если вы их открывали в порядке A -> B(A) -> C(B), то закрывать их надо в порядке C, B, A.
    По крайней мере в Java, закрытие уже закрытого потока является по сути no-op (если поток уже закрыт, то делается return из метода Close). И подозреваю, что в C# сделано так же. А значит и ошибок таких не будет.
    Ответ написан
    1 комментарий
  • Ошибка 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.
    Ответ написан
    Комментировать
  • Почему многие крупные сайты тормозят по самые помидоры?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    Добавлю к вышесказанному: это крупные ритейлеры, которые конкурируют между собой в первую очередь товарами, ценами, и сервисом. Вот серьёзно, кто навсегда перейдёт от мвидео к эльдорадо (или наоборот), только из-за того, что у одного из них сайт на 2 секунды быстрее загружается?

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

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

    А в случае с Амазоном - у них тем более нет конкурентов. И пока они будут поддерживать сайт на достаточно приемлемом уровне, никуда пользователи от них не уйдут (я про торговлю, а не про AWS)
    Ответ написан
    3 комментария
  • Как работает поиск в массиве mongoDB через Spring Data?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    Ответ зависит от того, какой именно запрос сгенерирует Spring Data для метода countByActiveStatesIn(Set states);.

    Судя по таблице №16 раздела 15.3 документации Spring Data MongoDB, должен сгенерироваться запрос
    db.collectionName.count({ activeStates: { $in: ["logicConnectionLost"] }})

    который посчитает все документы, поле activeStates которых содержит хотя бы один элемент из переданного списка.

    Какой именно запрос был сгенерирован, можно проверить выставив уровень логирования в DEBUG:
    logging.level.org.springframework.data.mongodb.core.MongoTemplate=DEBUG
    
    #Если используете реактивный MongoTemplate:
    logging.level.org.springframework.data.mongodb.core.ReactiveMongoTemplate=DEBUG
    Ответ написан
    Комментировать
  • Java как сделать чтобы каждый символ + 1?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    Если я правильно понял, то вам нужно увеличивать yy на 1 каждый раз, когда меняется pp? Т.е. банально надо посчитать сколько раз изменилась переменная pp.
    В таком случае можно поступить вот так: оборачиваете обе переменные в класс, и работаете с ними только через геттеры и сеттер. Прямой сеттер есть только для pp, внутри него происходит обновление yy по нужным вам правилам.
    Ответ написан
    Комментировать
  • Как вызвать printf в Webassembly?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    Функция printf пишет в стандартный stdout, который в браузере не доступен.
    На данный момент в Wasm единственный способ общаться с внешним миром (писать в консоль, читать файлы, делать запросы по сети и т.п.) - это дёргать Web API, которое предоставляет браузер.
    В вашем случае скорее всего подойдёт console.log
    Ответ написан
    Комментировать
  • Как исправить "cannot infer an appropriate lifetime for lifetime parameter"?

    @Asapin
    В свободное время ковыряюсь с Rust и Wasm
    У самого опыта работы с растом не так много, но насколько я понимаю, ситуация получается следующая:
    1. Метод fn get_file(&self) возвращает Box
    2. Так как у возвращаемого типа Box нет явного ограничения лайфтайма, то согласно RFC 599, ему присваивается лайфтайм 'static
    3. Но при этом имплементация трейта File для структуры F ограничена лайфтаймом 'a, отсюда и конфликт.

    Возможное решение:
    Объект, который должен шериться между другими объектами (в моём примере это WrappedKey) заворачиваем сначала в Box, что бы переместить его из стека в кучу, а затем в Rc, что бы объект не умирал пока на него есть хоть одна ссылка.
    Когда надо создать копию ссылки на объект (что бы пошарить этот объект), просто вызываете clone() у Rc.

    Если объект надо шарить между разными потоками, то вместо Rcиспользуйте Arc(Atomic RC).
    Ответ написан
    Комментировать