AssertJ Shorts №4 - Recursive Comparison и Factories
Как верно заметили в комментариях, я не рассказал еще про некоторые важные фишки AssertJ, такие как
Иногда обычный
Он позволяет сравнивать объекты рекурсивно, т.е. по всем вложенным полям, даже если это разные экземпляры одного класса.
Без
Кроме рекурсивного сравнения,
Еще более интересная возможность - сравнивать списки объектов через
Некоторые поля нужно сравнивать по особым правилам, например,
Здесь
Если одни и те же настройки сравнения используются несколько раз, их можно вытащить в отдельную конфигурацию и использовать несколько раз:
И кратко про
Чтобы сохранить тип и использовать нужные ассёрты, можно использовать
А теперь с
Как верно заметили в комментариях, я не рассказал еще про некоторые важные фишки AssertJ, такие как
usingRecursiveComparison и InstanceOfAssertFactory. Спасибо, что заметили, исправляюсь!Иногда обычный
assertThat(actual).isEqualTo(expected) не работает — например, если сравниваемые объекты содержат вложенные поля, коллекции или даже циклические ссылки. В таких случаях на помощь приходит usingRecursiveComparison().Он позволяет сравнивать объекты рекурсивно, т.е. по всем вложенным полям, даже если это разные экземпляры одного класса.
record Address(String city, String zip) {}
record User(String name, Address address) {}
@Test
void recursive_comparison_simple() {
User actual = new User("Alice", new Address("London", "12345"));
User expected = new User("Alice", new Address("London", "12345"));
assertThat(actual)
.usingRecursiveComparison()
.isEqualTo(expected);
}
Без
usingRecursiveComparsion тест бы упал, так как объекты Address разные. Кроме рекурсивного сравнения,
usingRecursiveComparison позволяет исключать некоторые поля из сравнения. Особенно полезно, например, для полей типа createdAt и updatedAt, значения которых точно не совпадают у разных объектов:
record User(String name, int age) {}
@Test
void ignore_field() {
User actual = new User("Alice", 30);
User expected = new User("Alice", 99); // возраст не важен
assertThat(actual)
.usingRecursiveComparison()
.ignoringFields("age")
.isEqualTo(expected);
}
Еще более интересная возможность - сравнивать списки объектов через
usingRecursiveFieldByFieldElementComparator:
List<User> actual = List.of(new User("Alice", new Address("London", "12345")));
List<User> expected = List.of(new User("Alice", new Address("London", "12345")));
assertThat(actual)
.usingRecursiveFieldByFieldElementComparator()
.containsExactlyElementsOf(expected);
Некоторые поля нужно сравнивать по особым правилам, например,
BigDecimal с учетом или без учета точности. Здесь помогут withComparatorForFields и withEqualsForType:
record Price(BigDecimal amount) {}
@Test
void with_comparator_for_field() {
Price actual = new Price(new BigDecimal("10.005"));
Price expected = new Price(new BigDecimal("10.004"));
Comparator<BigDecimal> roundedComparator =
Comparator.comparing(bd -> bd.setScale(2, RoundingMode.HALF_UP));
assertThat(actual)
.usingRecursiveComparison()
.withComparatorForFields(roundedComparator, "amount")
.isEqualTo(expected);
}
Здесь
amount сравнивается уже после округления до двух знаков после запятой. Если одни и те же настройки сравнения используются несколько раз, их можно вытащить в отдельную конфигурацию и использовать несколько раз:
RecursiveComparisonConfiguration config = RecursiveComparisonConfiguration.builder()
.withIgnoredFields("id", "timestamp")
.withIgnoreAllActualNullFields(true)
.build();
assertThat(actual)
.usingRecursiveComparison(config)
.isEqualTo(expected);
И кратко про
InstanceOfAssertFactory - совсем забыл, насколько крутая это фича. В обычных ассёртах мы можем легко проверять свойства объектов, но при использовании методов вроде extracting() или filteredOn() тип выводится как Object, и AssertJ не знает, какие методы доступны у результата.Чтобы сохранить тип и использовать нужные ассёрты, можно использовать
InstanceOfAssertFactory. Без InstanceOfAssertFactory:
List<Person> people = List.of(new Employee("Alice", "HR"), new Manager("Bob", 5));
assertThat(people)
.filteredOn(p -> p instanceof Manager)
.first() // тип Object
.extracting("teamSize") // работает, но не типобезопасно
.isEqualTo(5);
А теперь с
InstanceOfAssertFactory:❤2
InstanceOfAssertFactory<Manager, ObjectAssert<Manager>> managerAssert =
new InstanceOfAssertFactory<>(Manager.class, Assertions::assertThat);
assertThat(people)
.filteredOn(p -> p instanceof Manager)
.first(managerAssert) // теперь тип — Manager
.extracting(Manager::getTeamSize)
.isEqualTo(5);
Если ты часто используешь типы вроде String, Integer, Map, List, LocalDateTime и т.д., то нет необходимости создавать InstanceOfAssertFactory вручную — AssertJ уже поставляет готовые!
import static org.assertj.core.api.InstanceOfAssertFactories.*;
@Test
void use_predefined_instance_of_assert_factory() {
record Event(String type, Map<String, Object> payload) {}
var events = List.of(
new Event("click", Map.of("x", 10, "y", 20)),
new Event("submit", Map.of("formId", "abc123"))
);
assertThat(events)
.element(0)
.extracting(Event::payload, MAP) // используем готовую фабрику
.containsEntry("x", 10);
}
Спасибо большое за комментарии, пишите еще!
🔥10👍3
Awaility Shorts
Мне кажется, про AssertJ я рассказал все, что знал и использовал сам каждый день. Перейдем к следующей библиотеке, которая мне также помогает каждый день - Awaitility. Если есть необходимость проверить результаты работы, которая происходит в фоне, то Awaitility здесь просто незаменима. Естественно, всегда есть возможность сделать
Например, здесь тест ждет до трех секунд, пока значение
Условие можно передать и лямбдой:
DSL Awaitility включает еще несколько очень полезных методов, например,
Иногда проверяемый метод может бросать исключения до момента полной инициализации. Эти исключения можно игнорировать через
Также можно игнорировать исключения только определенных типов:
И что я люблю больше всего, так это интеграцию с AssertJ через
Используете ли вы библиотеку Awaility в своей работе?
Мне кажется, про AssertJ я рассказал все, что знал и использовал сам каждый день. Перейдем к следующей библиотеке, которая мне также помогает каждый день - Awaitility. Если есть необходимость проверить результаты работы, которая происходит в фоне, то Awaitility здесь просто незаменима. Естественно, всегда есть возможность сделать
Thread.sleep(1000) в цикле, но Awaitility предлагает гораздо более удобный DSL. Например, здесь тест ждет до трех секунд, пока значение
flag сменится на true. Если за три секунду этого не произойдет - тест упадет.
import static org.awaitility.Awaitility.await;
import static java.util.concurrent.TimeUnit.SECONDS;
@Test
void shouldWaitUntilValueIsSet() {
AtomicBoolean flag = new AtomicBoolean(false);
Executors.newSingleThreadScheduledExecutor()
.schedule(() -> flag.set(true), 1, SECONDS);
await().atMost(3, SECONDS)
.untilTrue(flag);
}
Условие можно передать и лямбдой:
await().atMost(2, SECONDS)
.until(() -> repository.findAll().size() == 3);
DSL Awaitility включает еще несколько очень полезных методов, например,
pollInterval - как часто проверять:
await()
.pollInterval(200, MILLISECONDS)
.atMost(5, SECONDS)
.until(() -> repo.isInitialized());
atLeast - минимальное время ожидания, даже если условие будет выполнено сразу, то тест все равно подождет указанное в atLeast время:
await()
.atLeast(1, SECONDS) // в любом случае ждем одну секунду
.atMost(5, SECONDS)
.until(() -> cache.isWarm());
Иногда проверяемый метод может бросать исключения до момента полной инициализации. Эти исключения можно игнорировать через
ignoreException:
await()
.ignoreExceptions()
.atMost(2, SECONDS)
.until(() -> service.getStatus().equals("READY"));
Также можно игнорировать исключения только определенных типов:
await()
.ignoreException(IllegalStateException.class)
.until(() -> client.getConnection().isAlive());
И что я люблю больше всего, так это интеграцию с AssertJ через
untilAsserted:
await()
.atMost(3, SECONDS)
.untilAsserted(() -> {
assertThat(store.getData()).isNotEmpty();
assertThat(store.getData()).containsExactly("hello");
});
Используете ли вы библиотеку Awaility в своей работе?
🔥7👍4
Про AssertJ поговорили, про Mockito тоже поговорили, даже Awaitility затронули. Значит, пришло время поговорить про WireMock!
WireMock — это инструмент, который поднимает локальный HTTP-сервер прямо внутри теста (или отдельным процессом) и позволяет:
• эмулировать внешние API;
• контролировать ответы (и их задержки);
• проверять, что запросы отправлялись с правильными параметрами;
• работать без реального интернета.
Минимальный пример без расширений для JUnit и другой автоматизации:
Существенную часть здесь занимает настройка самого WireMock-а, которую можно существенно упростить, если добавить расширение для JUnit:
Мне очень нравятся библиотеки, в которых API читаемый. Мне кажется, WireMock в этом плане - одна из лучших библиотек, настраивать сам WireMock одно удовольствие. Настраивать ответы, которые WireMock выдает уже не так просто, но API довольно выразительный и старается помочь изо всех сил.
Самый простой вариант - отвечать заранее заготовленным текстом и статусом:
Отвечать ошибкой:
Отвечать с задержкой:
И, наконец, динамическая генерация ответа на основе данных из запроса:
Кроме настройки запросов, WireMock позволяет проверять, какой именно запрос пришел (вдруг, не все параметры указаны). Самые простые варианты:
Как и в случае с Mockito, можно проверять, сколько было запросов:
Можно проверить наличие заголовка, например, что указан правильный content type:
WireMock — это инструмент, который поднимает локальный HTTP-сервер прямо внутри теста (или отдельным процессом) и позволяет:
• эмулировать внешние API;
• контролировать ответы (и их задержки);
• проверять, что запросы отправлялись с правильными параметрами;
• работать без реального интернета.
Минимальный пример без расширений для JUnit и другой автоматизации:
import com.github.tomakehurst.wiremock.WireMockServer;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
public class WireMockExampleTest {
private WireMockServer wireMockServer;
@BeforeEach
void setup() {
wireMockServer = new WireMockServer(8089);
wireMockServer.start();
configureFor("localhost", 8089);
stubFor(get(urlEqualTo("/hello"))
.willReturn(aResponse()
.withStatus(200)
.withBody("Hello from WireMock!")));
}
@AfterEach
void tearDown() {
wireMockServer.stop();
}
@Test
void shouldReturnStubbedResponse() {
String response = new RestTemplate()
.getForObject("http://localhost:8089/hello", String.class);
assertThat(response).isEqualTo("Hello from WireMock!");
}
}
Существенную часть здесь занимает настройка самого WireMock-а, которую можно существенно упростить, если добавить расширение для JUnit:
public class WireMockJUnit5Test {
// вот здесь расширение и регистрируется
@RegisterExtension
static WireMockExtension wireMock = WireMockExtension.newInstance()
.options(wireMockConfig().port(8089))
.build();
// обычный тест
@Test
void shouldReturnStubbedResponse() {
wireMock.stubFor(get(urlEqualTo("/hello"))
.willReturn(aResponse()
.withStatus(200)
.withBody("Hello JUnit 5 + WireMock!")));
String response = new RestTemplate()
.getForObject("http://localhost:8089/hello", String.class);
assertThat(response).isEqualTo("Hello JUnit 5 + WireMock!");
wireMock.verify(getRequestedFor(urlEqualTo("/hello")));
}
}
Мне очень нравятся библиотеки, в которых API читаемый. Мне кажется, WireMock в этом плане - одна из лучших библиотек, настраивать сам WireMock одно удовольствие. Настраивать ответы, которые WireMock выдает уже не так просто, но API довольно выразительный и старается помочь изо всех сил.
Самый простой вариант - отвечать заранее заготовленным текстом и статусом:
stubFor(get(urlEqualTo("/hello"))
.willReturn(aResponse()
.withStatus(200)
.withBody("Hello from WireMock!")));
Отвечать ошибкой:
stubFor(get("/error")
.willReturn(serverError())); // статус 500
Отвечать с задержкой:
stubFor(get("/slow")
.willReturn(aResponse()
.withFixedDelay(2000) // задержка 2 секунды
.withStatus(200)
.withBody("Delayed!")));
И, наконец, динамическая генерация ответа на основе данных из запроса:
stubFor(get(urlMatching("/user/.*"))
.willReturn(aResponse()
.withStatus(200)
.withTransformers("response-template")
.withBody("Hello {{request.path.[1]}}!")));
Кроме настройки запросов, WireMock позволяет проверять, какой именно запрос пришел (вдруг, не все параметры указаны). Самые простые варианты:
// проверим, что пришел GET запрос
verify(getRequestedFor(urlEqualTo("/hello")));
// проверим, что пришел POST запрос
verify(postRequestedFor(urlEqualTo("/api/data")));
Как и в случае с Mockito, можно проверять, сколько было запросов:
verify(2, getRequestedFor(urlEqualTo("/hello")));
Можно проверить наличие заголовка, например, что указан правильный content type:
verify(postRequestedFor(urlEqualTo("/submit"))
.withHeader("Content-Type", equalTo("application/json")));
🔥8👍3
Или, что в теле запроса есть JSON в определенном формате:
Аналогично, можно проверять и GET запросы на наличие параметров:
Какими еще возможностями WireMock вы пользуетесь?
verify(postRequestedFor(urlEqualTo("/submit"))
.withRequestBody(matchingJsonPath("$.user.id", equalTo("123"))));
Аналогично, можно проверять и GET запросы на наличие параметров:
verify(getRequestedFor(urlPathEqualTo("/list"))
.withQueryParam("page", equalTo("3")));
Какими еще возможностями WireMock вы пользуетесь?
🔥8👍4
Test Containers Shorts
Test Containers — это библиотека для запуска Docker-контейнеров прямо из тестов и сегодня будет короткая заметка об этой библиотеке.
Для примера запустим PostgreSQL прямо из теста (пока без интеграции с JUnit):
Для JUnit есть расширение, которое автоматически запускает и останавливает контейнеры до и после выполнения теста:
Test Containers довольно удобно интегрируются со спринговыми тестами через
А вы используете Test Containers в своей работе?
Test Containers — это библиотека для запуска Docker-контейнеров прямо из тестов и сегодня будет короткая заметка об этой библиотеке.
Для примера запустим PostgreSQL прямо из теста (пока без интеграции с JUnit):
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import static org.assertj.core.api.Assertions.assertThat;
class PostgresTest {
// Поднимаем контейнер PostgreSQL
static final PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:15")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
static {
postgres.start();
}
@Test
void testDatabaseConnection() throws Exception {
var connection = postgres.createConnection("");
var result = connection.createStatement().executeQuery("SELECT 1");
assertThat(result.next()).isTrue();
assertThat(result.getInt(1)).isEqualTo(1);
}
}
Для JUnit есть расширение, которое автоматически запускает и останавливает контейнеры до и после выполнения теста:
@Testcontainers
class JUnit5Test {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");
@Test
void testIsRunning() {
assertThat(postgres.isRunning()).isTrue();
}
}
Test Containers довольно удобно интегрируются со спринговыми тестами через
@DynamicPropertySource:
@SpringBootTest
@Testcontainers
class UserRepositoryTest {
@Container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:15")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@DynamicPropertySource
static void overrideProps(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired
private UserRepository userRepository;
@Test
void testSaveAndFindUser() {
User user = new User(null, "Alice");
userRepository.save(user);
var users = userRepository.findAll();
assertThat(users).hasSize(1);
assertThat(users.get(0).getName()).isEqualTo("Alice");
}
}
А вы используете Test Containers в своей работе?
👍10🔥6
Media is too big
VIEW IN TELEGRAM
Hibernate vs jOOQ vs SQL - Engineering Trade-offs
Hibernate долго был для меня инструментом по умолчанию в Java-разработке — быстрый, удобный, “магический”. Но со временем я стал всё чаще замечать его ограничения и переходить к jOOQ или plain SQL.
В этом видео мой взгляд на ORM глазами инженера, который работал с реальными системами: где Hibernate помогает, а где начинает усложнять жизнь.
Hibernate долго был для меня инструментом по умолчанию в Java-разработке — быстрый, удобный, “магический”. Но со временем я стал всё чаще замечать его ограничения и переходить к jOOQ или plain SQL.
В этом видео мой взгляд на ORM глазами инженера, который работал с реальными системами: где Hibernate помогает, а где начинает усложнять жизнь.
🔥14❤2
Media is too big
VIEW IN TELEGRAM
Hibernate - Спасение или проблема?
Hibernate — это мощный ORM, который идеально раскрывается в бизнес-приложениях: CRUD, сложные доменные модели, связи между сущностями и быстрый старт разработки без постоянного написания SQL.
В этом видео разбираем:
— где Hibernate реально помогает
— почему он так хорош для CRUD и агрегатов
— как работает lazy loading и где он может «стрельнуть в ногу»
— когда JPQL уже не хватает и приходится идти в native SQL
— и где проходит граница между удобством ORM и контролем над базой
А как ты используешь Hibernate в своих проектах?
Hibernate — это мощный ORM, который идеально раскрывается в бизнес-приложениях: CRUD, сложные доменные модели, связи между сущностями и быстрый старт разработки без постоянного написания SQL.
В этом видео разбираем:
— где Hibernate реально помогает
— почему он так хорош для CRUD и агрегатов
— как работает lazy loading и где он может «стрельнуть в ногу»
— когда JPQL уже не хватает и приходится идти в native SQL
— и где проходит граница между удобством ORM и контролем над базой
А как ты используешь Hibernate в своих проектах?
🔥7
Media is too big
VIEW IN TELEGRAM
Почему я всё реже выбираю Hibernate
В этом видео расскажу, где ORM создаёт больше проблем, чем решает, когда стоит использовать чистый SQL и почему jOOQ стал моим инструментом по умолчанию для работы с базой данных в Java.
В этом видео расскажу, где ORM создаёт больше проблем, чем решает, когда стоит использовать чистый SQL и почему jOOQ стал моим инструментом по умолчанию для работы с базой данных в Java.
👍8❤7
Media is too big
VIEW IN TELEGRAM
MCP сервер для Codex/Claude Code на Spring Boot
Создаём MCP Server на Spring Boot и подключаем его к Codex! В этом видео AI получит доступ к коллекции винила: поиск, просмотр и добавление пластинок через MCP. 🚀💿
Создаём MCP Server на Spring Boot и подключаем его к Codex! В этом видео AI получит доступ к коллекции винила: поиск, просмотр и добавление пластинок через MCP. 🚀💿
🔥12❤2👍2
Media is too big
VIEW IN TELEGRAM
MCP клиент на Spring Boot - подключаемся к Ollama и MCP Server
В этом видео собираем MCP Client на Spring Boot и подключаем его к локально запущенному Ollama и MCP серверу на Spring Boot.
MCP Server предоставляет инструменты для управления коллекцией виниловых пластинок и его мы уже делали в прошлом видео.
Как сейчас модно говорить - без маркетинговой шелухи, четко и по делу, только Spring Boot, только стартеры и минимум кода.
В этом видео собираем MCP Client на Spring Boot и подключаем его к локально запущенному Ollama и MCP серверу на Spring Boot.
MCP Server предоставляет инструменты для управления коллекцией виниловых пластинок и его мы уже делали в прошлом видео.
Как сейчас модно говорить - без маркетинговой шелухи, четко и по делу, только Spring Boot, только стартеры и минимум кода.
🔥8❤3
Media is too big
VIEW IN TELEGRAM
AI-агент теперь ПОМНИТ всё! Добавляем память в Spring AI
В прошлом видео мы научили AI работать с MCP Server и коллекцией виниловых пластинок. Но была одна проблема — после каждого сообщения агент забывал весь предыдущий разговор.
В этом видео исправим это с помощью ChatMemory из Spring AI. Теперь AI будет помнить историю диалога, учитывать контекст и отвечать намного естественнее.
В прошлом видео мы научили AI работать с MCP Server и коллекцией виниловых пластинок. Но была одна проблема — после каждого сообщения агент забывал весь предыдущий разговор.
В этом видео исправим это с помощью ChatMemory из Spring AI. Теперь AI будет помнить историю диалога, учитывать контекст и отвечать намного естественнее.
❤5👍5😁1
Media is too big
VIEW IN TELEGRAM
Микросервисы? Или лучше модульный монолит? | Engineering Trade-offs
Все слышали, что будущее за микросервисами. Но почему тогда Amazon, Shopify и другие компании всё чаще говорят о модульных монолитах?
В этом выпуске разбираемся:
* почему микросервисы вообще появились;
* какую цену приходится платить за распределённую систему;
* почему микросервисы часто решают организационные, а не технические проблемы;
* что такое модульный монолит и зачем нужен Spring Modulith;
* как AI меняет требования к архитектуре.
В инженерии редко бывают универсально правильные решения. Есть только trade-offs.
Все слышали, что будущее за микросервисами. Но почему тогда Amazon, Shopify и другие компании всё чаще говорят о модульных монолитах?
В этом выпуске разбираемся:
* почему микросервисы вообще появились;
* какую цену приходится платить за распределённую систему;
* почему микросервисы часто решают организационные, а не технические проблемы;
* что такое модульный монолит и зачем нужен Spring Modulith;
* как AI меняет требования к архитектуре.
В инженерии редко бывают универсально правильные решения. Есть только trade-offs.
❤11🔥3👍1
Media is too big
VIEW IN TELEGRAM
Spring Modulith - модульный монолит на Spring Boot
В этом видео разбираем Spring Modulith — новый подход к построению модульных монолитов в Spring Boot.
Вы узнаете:
• зачем вообще нужен Spring Modulith;
• как разделить приложение на независимые модули;
• как автоматически проверять архитектурные границы;
В этом видео разбираем Spring Modulith — новый подход к построению модульных монолитов в Spring Boot.
Вы узнаете:
• зачем вообще нужен Spring Modulith;
• как разделить приложение на независимые модули;
• как автоматически проверять архитектурные границы;
❤8🔥3👍2
Media is too big
VIEW IN TELEGRAM
Распиливаем монолит на микросервисы со Spring Modulith
В этом выпуске превращаем модульный монолит в полноценную микросервисную архитектуру. Покажу, какие изменения действительно нужны, как использовать Kafka и HTTP Interface Clients, и как здесь помогает Spring Modulith
В этом выпуске превращаем модульный монолит в полноценную микросервисную архитектуру. Покажу, какие изменения действительно нужны, как использовать Kafka и HTTP Interface Clients, и как здесь помогает Spring Modulith
👍7❤4
Media is too big
VIEW IN TELEGRAM
SQL vs NoSQL: История борьбы и сотрудничества - Engineering Trade-offs
В начале 2010-х все были уверены, что SQL скоро исчезнет, а будущее за NoSQL. Но прошло 15 лет, и PostgreSQL по-прежнему остается одной из самых популярных баз данных. Что пошло не так?
В этом выпуске Engineering Trade-offs разбираемся, почему появился NoSQL, какие реальные проблемы он решил, почему этого оказалось недостаточно и как SQL и NoSQL в итоге изменили друг друга.
В начале 2010-х все были уверены, что SQL скоро исчезнет, а будущее за NoSQL. Но прошло 15 лет, и PostgreSQL по-прежнему остается одной из самых популярных баз данных. Что пошло не так?
В этом выпуске Engineering Trade-offs разбираемся, почему появился NoSQL, какие реальные проблемы он решил, почему этого оказалось недостаточно и как SQL и NoSQL в итоге изменили друг друга.
👍9
Media is too big
VIEW IN TELEGRAM
Как разбить большую таблицу на партиции в PostgreSQL
Рассказываю о том, как работать с партициями в PostgreSQL. Несколько примеров:
1. Создаем таблицу с несколькими партициями сразу.
2. Мигрируем существующие данные в таблицу с партициями.
Пишите в комментариях, какой подход при миграции использовали вы.
Рассказываю о том, как работать с партициями в PostgreSQL. Несколько примеров:
1. Создаем таблицу с несколькими партициями сразу.
2. Мигрируем существующие данные в таблицу с партициями.
Пишите в комментариях, какой подход при миграции использовали вы.
🔥9
Media is too big
VIEW IN TELEGRAM
MongoDB Sharding: как разделить миллион документов между серверами
Разбираемся, как работает MongoDB Sharding: поднимаем sharded cluster, распределяем миллион документов между серверами, смотрим на chunks, balancer и разбираемся, как MongoDB выбирает нужный shard для запроса
Разбираемся, как работает MongoDB Sharding: поднимаем sharded cluster, распределяем миллион документов между серверами, смотрим на chunks, balancer и разбираемся, как MongoDB выбирает нужный shard для запроса
❤5👍2