Напишите потокобезопасный кеш с TTL и размером 👇
📦 Задание
Реализуйте ExpiringCache<K, V> — thread-safe кеш с автоматическим удалением устаревших записей.
public class ExpiringCache<K, V> {
public void put(K key, V value, Duration ttl) { }
public Optional<V> get(K key) { }
public void remove(K key) { }
public int size() { }
}📋 Требования
1. Функциональность
put() — добавить элемент с TTL (time-to-live)
get() — получить элемент, вернуть Optional.empty() если истёк
remove() — удалить элемент
size() — количество валидных (не истёкших) элементов
2. Потокобезопасность
→ Все операции должны быть thread-safe
→ Минимизировать блокировки (не использовать synchronized на весь объект)
→ Одновременное чтение не должно блокироваться
— Производительность
→ get() должен быть O(1) в среднем случае
→ Автоматическая очистка истёкших записей (passive + active eviction)
→ Не создавать отдельный поток для каждого элемента
3. Ограничения
→ Максимальный размер кеша — 1000 элементов
→ При превышении удалять самые старые записи (LRU)
→ Graceful shutdown при закрытии кеша
Бонус: Добавить метрики (cache hits/misses)
Ставьте → 🔥, если нравится формат. Если нет → 🤔
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10👍2❤1
Напишите упрощённую версию HashMap с нуля 👇
📦 Задание
Реализуйте SimpleHashMap<K, V> — собственную реализацию хеш-таблицы без использования готовых Map.
📋 Требования
1. Основная функциональность
→ put() — добавить/обновить пару ключ-значение
→ get() — получить значение по ключу (null если нет)
→ remove() — удалить элемент, вернуть старое значение
→ containsKey() — проверка наличия ключа
→ size() — текущее количество элементов
→ keySet() — множество всех ключей
2. Внутренняя структура
→ Использовать массив buckets (корзин)
→ Collision resolution через связный список (chaining)
→ Начальная ёмкость — 16 элементов
→ Load factor — 0.75
3. Обязательные фичи
→ Автоматическое расширение (resize) при превышении load factor
→ Корректная работа с null ключами
→ Правильный расчёт hashCode (учитывать equals/hashCode contract)
→ Обработка коллизий
Ставьте → 🔥, если нравится формат. Если нет → 🤔
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍2❤1
Напишите метод для production-кода 👇
📦 Задание
Реализуйте debouncer, который откладывает выполнение действия пока события продолжают поступать:
public class Debouncer {
public Debouncer(long delayMs) {
// Ваш код здесь
}
public void call(Runnable action) {
// Ваш код здесь
// Если вызов повторяется до истечения delayMs
// - отменить предыдущий и запланировать новый
// Выполнить только когда delayMs прошло без новых вызовов
}
}🔹 Требования
— Выполнять action только если delayMs прошло без новых вызовов
— Каждый новый call() отменяет предыдущий таймер
— Использовать ScheduledExecutorService
— Потокобезопасность
🔹 Пример:
Debouncer debouncer = new Debouncer(300); // 300ms
debouncer.call(() -> search(query)); // откладываем
debouncer.call(() -> search(query)); // отменяем предыдущий
debouncer.call(() -> search(query)); // отменяем предыдущий
// Через 300ms выполнится только последний
Ставьте → 🔥, если нравится формат. Если нет → 🤔
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍3👏1
Задача из реального интервью в Google 👇
📦 Задание
Вы аналитик Google Ads. Нужно найти все объявления, которые:
— Имеют статус 'active'
— Показов больше 500,000
— Обновлены в 2024 году
Таблица google_ads на картинке. Напишите SQL-запрос для фильтрации.
Ставьте → 🔥, если нравится формат. Если нет → 🤔
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20👍2👏1
Напишите production-ready Spring компонент 👇
📦 Задание
Реализуйте кастомную аннотацию @RateLimit, которая ограничивает количество вызовов метода с использованием Spring AOP.
🔹 Требования
— Использовать Spring AOP
— Потокобезопасность
— Кэш лимитов
— Учитывать имя метода + параметры
— Custom exception при превышении
Ставьте → 🔥, если нравится формат. Если нет → 🤔
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16👏2❤1👍1
Задача из реального интервью в Google 👇
📦 Задание
Google хочет проанализировать самые популярные категории поиска для оптимизации результатов.
Таблицы на картинке. Напишите запрос для подсчета общего количества поисков в каждой категории по месяцам за 2024 год.
Ставьте → 🔥, если нравится формат. Если нет → 🤔
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍3🤔3❤1
Ревью и рефактор логики для production-кода 👇
📦 Задание
Команда написала логику для отправки письма после регистрации пользователя. На проде иногда возникает ситуация, что письма приходят, а юзера в БД нет. Найдите проблему и исправьте:
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final ApplicationEventPublisher eventPublisher;
@Transactional
public void register(UserDto dto) {
User user = new User(dto.email());
userRepository.save(user);
eventPublisher.publishEvent(new UserRegisteredEvent(user));
}
}
@Component
@RequiredArgsConstructor
public class EmailListener {
private final EmailSender emailSender;
private final SomeOtherService someOtherService;
@EventListener
public void onUserRegistered(UserRegisteredEvent event) {
emailSender.sendWelcome(event.user().getEmail());
someOtherService.doSomething();
}
}
🔹 Задачи
— Объяснить, при каком сценарии письмо уйдёт, а пользователь не сохранится
— Исправить код, чтобы событие обрабатывалось только после сохранения юзера
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥21👍5❤4🤔1🌚1
Классическая ловушка в многопоточке👇
📦 Задание
Написали сервис для аудит-логирования действий пользователей. В проде периодически в лог пишется чужой userId — данные одного юзера попадают в запись другого. Найдите баг и исправьте:
@Component
public class UserContext {
private static final ThreadLocal<String> currentUserId = new ThreadLocal<>();
public static void set(String userId) { currentUserId.set(userId); }
public static String get() { return currentUserId.get(); }
public static void clear() { currentUserId.remove(); }
}
@Service
@RequiredArgsConstructor
public class OrderService {
private final AuditLogger auditLogger;
public CompletableFuture<Order> createOrder(String userId, OrderDto dto) {
UserContext.set(userId);
return CompletableFuture.supplyAsync(() -> {
Order order = buildOrder(dto);
auditLogger.log("Order created by: " + UserContext.get());
return order;
});
}
}
🔹 Задачи
— Объяснить, почему UserContext.get() внутри supplyAsync может вернуть чужой userId или null
— Исправить так, чтобы контекст корректно передавался в асинхронный поток
— Бонус: объяснить, почему ThreadLocal вообще опасен с пулами потоков типа ForkJoinPool
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥19👍4❤1🤔1
Код компилируется, тесты зелёные, в проде всё ломается 👇
📦 Задание — code review
Сервис обрабатывает заявки на вывод средств. Требования простые: нельзя выводить больше, чем есть на балансе, и нельзя создавать две заявки одновременно.
@Service
@RequiredArgsConstructor
public class WithdrawalService {
private final AccountRepository accountRepository;
private final WithdrawalRepository withdrawalRepository;
public void requestWithdrawal(Long userId, BigDecimal amount) {
Account account = accountRepository.findByUserId(userId)
.orElseThrow();
if (account.getBalance().compareTo(amount) < 0) {
throw new InsufficientFundsException();
}
boolean hasPending = withdrawalRepository
.existsByUserIdAndStatus(userId, Status.PENDING);
if (hasPending) {
throw new WithdrawalAlreadyPendingException();
}
account.setBalance(account.getBalance().subtract(amount));
accountRepository.save(account);
withdrawalRepository.save(
new Withdrawal(userId, amount, Status.PENDING)
);
}
}
🔹 Задачи
Два запроса от одного пользователя прилетели одновременно. В итоге — баланс ушёл в минус и создались две заявки.
▪️ Объясни
— Точную последовательность событий при конкурентных запросах.
— Спасёт @Transactional над методом или нет? Почему?
— Как исправить это корректно.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20👍2❤1🤔1
Метрики норм, тесты зелёные, при пике трафика — БД ложится 👇
📦 Задание — code review
Сервис отдаёт профили пользователей. Для ускорения добавили кэш на 5 минут.
@Service
@RequiredArgsConstructor
public class UserProfileService {
private final UserRepository userRepository;
private final RedisTemplate<String, UserProfile> redisTemplate;
private static final Duration TTL = Duration.ofMinutes(5);
public UserProfile getProfile(Long userId) {
String key = "profile:" + userId;
UserProfile cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
return cached;
}
UserProfile profile = userRepository.findById(userId)
.map(UserProfile::from)
.orElseThrow(UserNotFoundException::new);
redisTemplate.opsForValue().set(key, profile, TTL);
return profile;
}
}
▪️ Объясни
— Что именно происходит при истечении TTL под нагрузкой.
— Почему добавление @Cacheable над методом не спасёт ситуацию.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17👍3❤1
Memory leak, который живёт неделями и не воспроизводится локально 👇
📦 Задание — code review
Написали фильтр для аудита запросов. В продакшне через несколько дней — OutOfMemoryError.
@Component
public class AuditFilter implements Filter {
private static final ThreadLocal<AuditContext> CONTEXT =
new ThreadLocal<>();
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain
) throws IOException, ServletException {
AuditContext ctx = new AuditContext(
((HttpServletRequest) request).getRequestURI(),
System.currentTimeMillis()
);
CONTEXT.set(ctx);
try {
chain.doFilter(request, response);
} finally {
auditLog(CONTEXT.get());
}
}
public static AuditContext current() {
return CONTEXT.get();
}
private void auditLog(AuditContext ctx) {
// пишем в БД...
}
}
▪️ Объясни
— Почему объекты в ThreadLocal не собираются GC, даже если запрос завершён.
— Какая одна строчка кода здесь отсутствует, и где именно она должна быть.
* Есть ли риски при использовании ThreadLocal в virtual threads (Project Loom)?
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12👍4
Код чистый, тесты быстрые, на реальных данных — pg_stat_activity в огне 👇
📦 Задание — code review
Ручка возвращает список заказов с информацией о товарах. Работает корректно, но DBA прибежал с графиком: каждый вызов делает тысячи запросов к БД.
@Entity
public class Order {
@Id
private Long id;
private Long userId;
private LocalDateTime createdAt;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "order")
private List<OrderItem> items;
}
@Entity
public class OrderItem {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Order order;
@ManyToOne(fetch = FetchType.LAZY)
private Product product;
private Integer quantity;
}
@RestController
@RequiredArgsConstructor
public class OrderController {
private final OrderRepository orderRepository;
@GetMapping("/orders")
public List<OrderDto> getOrders(@RequestParam Long userId) {
List<Order> orders = orderRepository.findByUserId(userId);
return orders.stream()
.map(order -> new OrderDto(
order.getId(),
order.getItems().stream()
.map(item -> new ItemDto(
item.getProduct().getName(),
item.getQuantity()
))
.toList()
))
.toList();
}
}
▪️ Объясни
— Точную механику N+1: где и сколько раз запросы уходят в БД в этом коде.
— Почему FetchType.EAGER «решит» проблему, но создаст другую.
— Как можно решить проблему.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20❤4👍4
Никаких ошибок в логах. Никаких алертов. Данные просто не сохраняются 👇
📦 Задание — code review
Сервис нотификаций: после оплаты заказа — отправить email и записать событие в БД. Оба действия независимы, сделали асинхронно.
@Service
@RequiredArgsConstructor
public class NotificationService {
private final EmailClient emailClient;
private final EventRepository eventRepository;
private final Executor taskExecutor;
public void notifyOrderPaid(Order order) {
CompletableFuture.runAsync(
() -> emailClient.sendOrderConfirmation(order),
taskExecutor
);
CompletableFuture.runAsync(
() -> {
Event event = Event.orderPaid(order.getId());
eventRepository.save(event);
},
taskExecutor
);
}
}
▪️ Объясни
— Почему исключения из runAsync полностью проглатываются и как это работает внутри.
— Чем отличается поведение runAsync от supplyAsync в контексте обработки ошибок.
— Как переписать код так, чтобы: (1) ошибки логировались, (2) один сбой не блокировал другую задачу, (3) вызывающий код мог знать об итоге.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤3🤔2👍1
Работает у одного, но ломается у другого. В логах каша из чужих данных 👇
📦 Задание — code review
Команда добавила контекст текущего пользователя в сервис через поле. Локально — всё ок.
На проде с несколькими потоками — пользователи видят чужие данные.
@Service
public class ReportService {
private User currentUser;
private ReportFilter activeFilter;
private final ReportRepository reportRepository;
public ReportService(ReportRepository reportRepository) {
this.reportRepository = reportRepository;
}
public void initContext(User user, ReportFilter filter) {
this.currentUser = user;
this.activeFilter = filter;
}
public List<Report> getReports() {
if (currentUser == null) {
throw new IllegalStateException("Context not initialized");
}
return reportRepository.findByUserAndFilter(
currentUser.getId(),
activeFilter
);
}
public ReportSummary getSummary() {
List<Report> reports = getReports();
return ReportSummary.calculate(reports, currentUser);
}
}
▪️ Объясни
— Какой скоуп у бина по умолчанию в Spring и почему это делает поля-состояния опасными.
— Почему synchronized над методами не является правильным решением здесь.
— Как переписать код, чтобы работало.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤3👍2👏1
Метод возвращает устаревшие данные. Иногда и только на нескольких потоках. Воспроизвести локально — нереально 👇
📦 Задание — code review
Команда добавила простой in-memory кэш для тяжёлых вычислений. На одном потоке работает идеально. На проде с нагрузкой — иногда возвращает старый результат или null.
@Component
public class PricingCache {
private final Map<String, BigDecimal> cache = new HashMap<>();
private final PricingEngine pricingEngine;
public PricingCache(PricingEngine pricingEngine) {
this.pricingEngine = pricingEngine;
}
public BigDecimal getPrice(String productId) {
if (cache.containsKey(productId)) {
return cache.get(productId);
}
BigDecimal price = pricingEngine.calculate(productId);
cache.put(productId, price);
return price;
}
public void invalidate(String productId) {
cache.remove(productId);
}
public void invalidateAll() {
cache.clear();
}
}
▪️ Объясни
— В чём проблема в коде
— Как переписать getPrice верно
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍3❤1🤔1
@Transactional стоит, исключение летит. Данные в БД остаются. Почему?
📦 Задание — code review
Команда добавила транзакцию на метод сохранения заказа. При ошибке валидации данные всё равно коммитятся в базу. @Transactional на месте, но не работает.
public class CheckedBusinessException extends Exception {
public CheckedBusinessException(String message) {
super(message);
}
}
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final InventoryService inventoryService;
@Transactional
public void placeOrder(Order order) throws CheckedBusinessException {
orderRepository.save(order);
inventoryService.reserveStock(order);
}
}
@Service
public class InventoryService {
public void reserveStock(Order order) throws CheckedBusinessException {
if (order.getQuantity() > availableStock) {
throw new CheckedBusinessException("Not enough stock");
}
}
}▪️ Почему @Transactional не откатывает изменения при CheckedBusinessException?
▪️ Как добиться rollback в этом коде
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤3👍3
Добавили объект в Set. Он там. Но найти его невозможно. Утечка памяти в проде. Почему?
📦 Задание — code review
Команда хранит активные сессии в HashSet. После смены роли пользователя сессия «пропадает» из множества — contains() возвращает false, remove() не удаляет. Set растёт, память течёт.
public class UserSession {
private Long userId;
private String role;
public UserSession(Long userId, String role) {
this.userId = userId;
this.role = role;
}
// getters, setters
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserSession that = (UserSession) o;
return Objects.equals(userId, that.userId)
&& Objects.equals(role, that.role);
}
@Override
public int hashCode() {
return Objects.hash(userId, role);
}
}
@Service
public class SessionManager {
private final Set<UserSession> activeSessions = new HashSet<>();
public void addSession(UserSession session) {
activeSessions.add(session);
}
public void promoteToAdmin(Long userId) {
activeSessions.stream()
.filter(s -> s.getUserId().equals(userId))
.findFirst()
.ifPresent(s -> s.setRole("ADMIN"));
}
public boolean isActive(UserSession session) {
return activeSessions.contains(session);
}
public void removeSession(UserSession session) {
activeSessions.remove(session);
}
}// Сценарий бага:
sessionManager.addSession(new UserSession(1L, "USER")); // ОК
sessionManager.promoteToAdmin(1L); // меняем роль
sessionManager.isActive(new UserSession(1L, "ADMIN")); // false ?!
// объект внутри Set есть, но достать его нельзя
▪️ Почему после promoteToAdmin() сессия становится невидимой для contains() и remove()?
▪️ Как исправить, не ломая бизнес-логику?
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍3❤1👏1
Напишите защиту от двойного списания 👇
📦 Задание
Клиент при сетевом таймауте повторяет POST /payments и списание проходит дважды. Реализуйте идемпотентность по заголовку Idempotency-Key.
@PostMapping("/payments")
public PaymentResult pay(
@RequestHeader("Idempotency-Key") String key,
@RequestBody PaymentRequest request) {
// Ваша реализация
}📋 Требования
— Повторный запрос с тем же ключом возвращает результат первого вызова и НЕ выполняет списание заново.
— Разные ключи → разные операции.
— Конкурентные запросы с одним ключом (двойной клик / параллельные ретраи) не должны привести к двойному выполнению.
— Ключ хранится с TTL (например, 24 часа).
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍2👏1
Наш подписчик прислал реальную задачу с собеса в Яндекс. Сможете решить? 👇
📦 Задание
Реализуйте банкомат и тесты к нему. Инициализируется набором купюр, умеет выдавать купюры под заданную сумму либо отвечать отказом. При выдаче купюры списываются с баланса.
/**
* Банкомат.
* Инициализируется набором купюр, выдаёт купюры под сумму либо отказывает.
* При выдаче купюры списываются с баланса.
*
* Номиналы:
* - 50, 100, 500, 1000, 5000 ₽
* - 20, 100, 500 EUR
*
* Валюты обрабатываются раздельно, обмен не поддерживается.
* Банкомат может использоваться многопоточно (резервирование выдачи).
* Поддержку многопоточности можно вынести в отдельную итерацию.
*/
public class ATM {
public ??? withdraw(???) {
// TODO
}
}
📋 Требования
— Сами определите, что withdraw принимает и что возвращает.
— Валюты считаются раздельно, обмена нет.
— Сумму, которую нельзя собрать имеющимися купюрами, отклоняем (частичная выдача недопустима).
— Покройте решение тестами.
— Бонус: сделайте безопасным для вызова из нескольких потоков.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14👍4👏1
Внешний API затупил на 30 секунд и наш сервис перестал отвечать целиком 👇
📦 Задание — code review
Ходим во внешний платёжный шлюз через RestTemplate.
@Configuration
public class HttpConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@Service
@RequiredArgsConstructor
public class PaymentGatewayClient {
private final RestTemplate restTemplate;
public GatewayResponse charge(ChargeRequest request) {
return restTemplate.postForObject(
"https://gateway.example.com/charge",
request,
GatewayResponse.class
);
}
}
▪️ Объясни
— Что происходит с пулом потоков Tomcat, когда внешний шлюз отвечает медленно или висит.
— Почему «ложится» всё приложение, хотя тормозит только один внешний вызов.
— Как настроить таймауты правильно (connect / read), зачем connection pool и где здесь место для timeout + circuit breaker / bulkhead.
Ставьте → 🔥, если нравится формат. Если нет → 🌚
#practise
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍2❤1🤔1