Работа мечты

Материал из Artem Aleksashkin's Wiki
Перейти к навигации Перейти к поиску
  • Facebook
  • Amazon
  • Apple
  • Netflix
  • Google
  • Microsoft

Мотивация

Penguin-Falls-Over-Rock.gif

Удача улыбается тому, кто к ней готов.
Характер + воспитание + обстоятельства = воля + мораль + закалка
  • Отрицание
  • Гнев
  • Торг
  • Депрессия
  • Принятие

Dream.jpeg

Nu-davay.jpg

Steps-motivation.jpg

Один работник заходит к хозяину и спрашивает:
– А почему ты платишь мне всего рубль в неделю, в то время как Елисею – пятнадцать? Хозяин смотрит в окно и говорит:
– Слышу, кто-то едет. Нам сена на зиму нужно купить. Выйди-ка, посмотри, не сено ли везут.

Вышел работник.
Зашел и говорит:
– Правда, сено.
– А не знаешь, откуда? Может, с Лукьяновских лугов?
– Не знаю.
– Так сходи и спроси.
Пошел тот.
Снова входит:
– Точно, с Лукьяновских.
– А сено какого укоса – первого или второго?
– Не знаю.
– Так сходи, узнай!
Вышел работник.
Возвращается:
– Хозяин! Первого укоса!
– А не знаешь, почем?
– Не знаю.
– Так сходи, узнай.
Сходил.
Вернулся и говорит:
– Хозяин! По двадцать рублей.
– А дешевле не дают?
– Не знаю.
В этот момент входит Елисей и говорит:
– Хозяин! Мимо везли сено с Лукьяновских лугов первого укоса. Просили по 20 рублей. Сторговались по 17 за воз. Я их загнал во двор, сейчас разгружают.
Хозяин поворачивается к первому:
– Теперь ты понял, почему тебе платят 1 рубль, а Елисею – 15?

Evg-sanya.jpg

Full-stack-developer.jpg

Cat-developer-home.jpg

90ie.jpg

Interview-vs-job.jpg

Compensations

Jobs Websites

Подготовка

Алгоритмы

ООП

  • Инкапсуляция (разделение доступа)
  • Наследование
  • Полиморфизм (полиморфные методы для каждого типа свой метод)

SOLID

  • S - Принцип единственной ответственности (The Single Responsibility Principle)
    • Следование принципу заключается обычно в декомпозиции сложных классов, которые делают сразу много вещей, на простые, отвественность которых очень специализирована.
  • O - Принцип открытости/закрытости (The Open Closed Principle)
    • Программные сущности (классы, модули, функции и т. п.) должны быть открыты для расширения, но закрыты для изменения
  • L - Принцип подстановки Барбары Лисков (The Liskov Substitution Principle)
    • При построении иерархий наследования создаваемые наследники должны корректно реализовывать поведение базового типа. Наследник класса дополняет, но не заменяет поведение базового класса. То есть в любом месте программы замена базового класса на класс‑наследник не должна вызывать проблем.
  • I - Принцип разделения интерфейса (The Interface Segregation Principle)
    • Заключается в создании интерфейсов, которые достаточно специфичны и требуют только необходимый минимум реализаций методов. Избыточные интерфейсы, напротив, могут требовать от реализующего класса создание большого количества методов, причём даже таких, которые не имеют смысла в контексте класса.
  • D - Принцип инверсии зависимостей (The Dependency Inversion Principle)
    • Следование принципу инверсии зависимостей «заставляет» реализовывать высокоуровневые компоненты без встраивания зависимостей от конкретных низкоуровневых классов, что, например, сильно упрощает замену используемых зависимостей как по бизнес‑требованиям, так и для целей тестирования. При этом зависимость формируется не от конкретной реализации, а от абстракции — реализуемого зависимостью интерфейса.

Паттерны проектирования

  • Порождающие паттерны
    • Абстрактная фабрика
    • Одиночка
    • Прототип
    • Строитель
    • Фабричный метод
  • Структурные паттерны
    • Адаптер
    • Декоратор
    • Заместитель
    • Компоновщик
    • Мост
    • Приспособленец
    • Фасад
  • Паттерны поведения
    • Интерпретатор
    • Итератор
    • Команда
    • Наблюдатель
    • Посетитель
    • Посредник
    • Состояние
    • Стратегия
    • Хранитель
    • Цепочка обязанностей
    • Шаблонный метод

Чистая архитектура

Clean-architecture.png

Domain Driven Design

  • Единый язык (Ubiquitous Language)
  • Домены и субдомены
  • Ubiquitous Language - общий язык с заказчиком
  • Стратегическое моделирование - стратегия управления бизнесом
  • Тактическое моделирование - итеративная разработка ПО
  • Bounded Context - граница предметной области
  • Context Map - как Bounded Context связан с другими

Паттерны микросервисов

  • Паттерны декомпозиции на микросервисы
    • Шаблон «Разбиение по бизнес-возможностям» (Decompose By Business Capability)
    • Шаблон «Разбиение по поддоменам» (Decompose By Subdomain)
  • Паттерны рефакторинга для перехода на микросервисы
    • Шаблон «Душитель» (Strangler)
    • Шаблон «Уровень защиты от повреждений» (Anti-Corruption Layer)
  • Паттерны управления данными в микросервисной архитектуре
    • Шаблон «База данных на сервис» (Database Per Service)
    • Шаблон «API-композиция» (API Composition)
    • Шаблон «Разделение команд и запросов» (Command Query Responsibility Segregation, CQRS)
    • Шаблон «Поиск событий» (Event Sourcing)
    • Шаблон «Сага» (Saga)
  • Паттерны коммуникации микросервисов
    • Шаблон «API-шлюз» (API Gateway)
    • Шаблон «Бэкенды для фронтендов» (Backends for Frontends, BFF)
  • Паттерны построения пользовательского интерфейса
    • Шаблон «Сборка пользовательского интерфейса на стороне клиента» (Client-Side UI Composition)
    • Шаблон «Сборка фрагментов страниц на стороне сервера» (Server-Side Page Fragment Composition)
  • Паттерны обнаружения сервисов в микросервисной архитектуре
    • Шаблон «Обнаружение сервисов на стороне клиента» (Client-Side Service Discovery)
    • Шаблон «Обнаружение сервисов на стороне сервера» (Server-Side Service Discovery)
  • Паттерны развертывания микросервисов
    • Шаблон «Экземпляр сервиса на хост» (Service Instance Per Host)
    • Шаблон «Сине-зеленое развертывание» (Blue-Green Deployment)
  • Паттерны повышения отказоустойчивости
    • Шаблон «Автоматический выключатель» (Circuit Breaker)
    • Шаблон «Переборка» (Bulkhead)
  • Паттерны мониторинга микросервисов
    • Шаблон «Агрегация логов» (Log Aggregation)
    • Шаблон «Распределенная трассировка» (Distributed Tracing)
    • Шаблон «Проверки здоровья» (Health Check)
  • Прочие паттерны проектирования микросервисов
    • Шаблон «Посредник» («Посол», Ambassador)
    • Шаблон «Коляска» («Прицеп», Sidecar)
    • Шаблон «Тестирование контрактов, ориентированных на потребителя» (Consumer-Driven Contract Testing)
    • Шаблон «Внешняя конфигурация» (External Configuration)

Дженерики

ACID

  • Atomicity — Атомарность
  • Consistency — Согласованность
  • Isolation — Изолированность
  • Durability — Долговечность

Нормальные формы

Уровни изоляции транзакций

SELECT @@GLOBAL.transaction_isolation, @@GLOBAL.transaction_read_only;
SELECT @@SESSION.transaction_isolation, @@SESSION.transaction_read_only;
Уровень изоляции Черновое чтение Невоспроизводимое чтение Фантомное чтение Блокировка чтения
READ UNCOMMITTED Yes Yes Yes No
READ COMMITTED No Yes Yes No
REPEATABLE READ No No Yes No
SERIALIZABLE No No No Yes
DROP TABLE IF EXISTS `money_transactions`;
CREATE TABLE `money_transactions` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `user_from_id` bigint(20) unsigned DEFAULT NULL,
  `user_to_id` bigint(20) unsigned DEFAULT NULL,
  `volume` decimal(19,6) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;
DROP TABLE IF EXISTS `money_accounts`;
CREATE TABLE `money_accounts` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) unsigned DEFAULT NULL,
  `volume` decimal(19,6) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY (`user_id`)
) ENGINE=InnoDB;
INSERT INTO `money_transactions` VALUES (DEFAULT, NOW(), NULL, 1, 10000.0);
INSERT INTO `money_transactions` VALUES (DEFAULT, NOW(), NULL, 2, 4300.0);
INSERT INTO `money_accounts` SELECT NULL AS `id`, T.`user_to_id` AS `user_id`, SUM(T.`volume`) AS `volume` FROM `money_transactions` AS T GROUP BY T.`user_to_id` ON DUPLICATE KEY UPDATE `volume` = VALUES(`volume`);


SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;

READ UNCOMMITTED

Суть в том, что незакрепленные транзакции видны в другой открытой транзакции

SET @user_id = 1;
SET @user_to_id = 2;
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
INSERT INTO `money_transactions` VALUES (DEFAULT, NOW(), @user_id, @user_to_id, 3000.0);
INSERT INTO `money_transactions` VALUES (DEFAULT, NOW(), @user_to_id, @user_id, -3000.0);
INSERT INTO `money_accounts` SELECT NULL AS `id`, T.`user_to_id` AS `user_id`, SUM(T.`volume`) AS `volume` FROM `money_transactions` AS T GROUP BY T.`user_to_id` ON DUPLICATE KEY UPDATE `volume` = VALUES(`volume`);
SELECT * FROM `money_transactions`;
SELECT * FROM `money_accounts`;
ROLLBACK;
SET @user_id = 2;
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;




SELECT * FROM `money_transactions`;
SELECT * FROM `money_accounts`;
ROLLBACK;

 > SELECT * FROM `money_transactions`;
+----+---------------------+--------------+------------+--------------+
| id | created_at          | user_from_id | user_to_id | volume       |
+----+---------------------+--------------+------------+--------------+
|  1 | 2024-02-12 13:34:24 |         NULL |          1 | 10000.000000 |
|  2 | 2024-02-12 13:34:24 |         NULL |          2 |  4300.000000 |
|  5 | 2024-02-12 14:48:29 |            1 |          2 |  3000.000000 |
|  6 | 2024-02-12 14:48:29 |            2 |          1 | -3000.000000 |
+----+---------------------+--------------+------------+--------------+
4 rows in set (0.00 sec)

 > SELECT * FROM `money_accounts`;
+----+---------+-------------+
| id | user_id | volume      |
+----+---------+-------------+
|  1 |       1 | 7000.000000 |
|  2 |       2 | 7300.000000 |
+----+---------+-------------+
2 rows in set (0.00 sec)
 > SELECT * FROM `money_transactions`;
+----+---------------------+--------------+------------+--------------+
| id | created_at          | user_from_id | user_to_id | volume       |
+----+---------------------+--------------+------------+--------------+
|  1 | 2024-02-12 13:34:24 |         NULL |          1 | 10000.000000 |
|  2 | 2024-02-12 13:34:24 |         NULL |          2 |  4300.000000 |
|  5 | 2024-02-12 14:48:29 |            1 |          2 |  3000.000000 |
|  6 | 2024-02-12 14:48:29 |            2 |          1 | -3000.000000 |
+----+---------------------+--------------+------------+--------------+
4 rows in set (0.00 sec)

 > SELECT * FROM `money_accounts`;
+----+---------+-------------+
| id | user_id | volume      |
+----+---------+-------------+
|  1 |       1 | 7000.000000 |
|  2 |       2 | 7300.000000 |
+----+---------+-------------+
2 rows in set (0.00 sec)

READ COMMITTED

Суть в том, что если в первой транзакции был коммит - вы увидите из нее изменения, хотя находитесь в транзакции.

SET @user_id = 1;
SET @user_to_id = 2;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
INSERT INTO `money_transactions` VALUES (DEFAULT, NOW(), @user_id, @user_to_id, 3000.0);
INSERT INTO `money_transactions` VALUES (DEFAULT, NOW(), @user_to_id, @user_id, -3000.0);
INSERT INTO `money_accounts` SELECT NULL AS `id`, T.`user_to_id` AS `user_id`, SUM(T.`volume`) AS `volume` FROM `money_transactions` AS T GROUP BY T.`user_to_id` ON DUPLICATE KEY UPDATE `volume` = VALUES(`volume`);
SELECT * FROM `money_transactions`;
SELECT * FROM `money_accounts`;
COMMIT;
SET @user_id = 2;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;








SELECT * FROM `money_transactions`;
SELECT * FROM `money_accounts`;
ROLLBACK;

 > SELECT * FROM `money_transactions`;
+----+---------------------+--------------+------------+--------------+
| id | created_at          | user_from_id | user_to_id | volume       |
+----+---------------------+--------------+------------+--------------+
|  1 | 2024-02-12 13:34:24 |         NULL |          1 | 10000.000000 |
|  2 | 2024-02-12 13:34:24 |         NULL |          2 |  4300.000000 |
|  7 | 2024-02-12 15:13:40 |            1 |          2 |  3000.000000 |
|  8 | 2024-02-12 15:13:40 |            2 |          1 | -3000.000000 |
+----+---------------------+--------------+------------+--------------+
4 rows in set (0.00 sec)

 > SELECT * FROM `money_accounts`;
+----+---------+-------------+
| id | user_id | volume      |
+----+---------+-------------+
|  1 |       1 | 7000.000000 |
|  2 |       2 | 7300.000000 |
+----+---------+-------------+
2 rows in set (0.00 sec)
 > SELECT * FROM `money_transactions`;
+----+---------------------+--------------+------------+--------------+
| id | created_at          | user_from_id | user_to_id | volume       |
+----+---------------------+--------------+------------+--------------+
|  1 | 2024-02-12 13:34:24 |         NULL |          1 | 10000.000000 |
|  2 | 2024-02-12 13:34:24 |         NULL |          2 |  4300.000000 |
|  7 | 2024-02-12 15:13:40 |            1 |          2 |  3000.000000 |
|  8 | 2024-02-12 15:13:40 |            2 |          1 | -3000.000000 |
+----+---------------------+--------------+------------+--------------+
4 rows in set (0.00 sec)

 > SELECT * FROM `money_accounts`;
+----+---------+-------------+
| id | user_id | volume      |
+----+---------+-------------+
|  1 |       1 | 7000.000000 |
|  2 |       2 | 7300.000000 |
+----+---------+-------------+
2 rows in set (0.00 sec)

REPEATABLE READ

В MySQL этот уровень по-умолчанию. Суть в том, что возможно чтение фантомных строк - закрепленных другими транзакциями. Тест показал, что транзакции не изолированы

SET @user_id = 1;
SET @user_to_id = 2;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
INSERT INTO `money_transactions` VALUES (DEFAULT, NOW(), @user_id, @user_to_id, 3000.0);
INSERT INTO `money_transactions` VALUES (DEFAULT, NOW(), @user_to_id, @user_id, -3000.0);
INSERT INTO `money_accounts` SELECT NULL AS `id`, T.`user_to_id` AS `user_id`, SUM(T.`volume`) AS `volume` FROM `money_transactions` AS T GROUP BY T.`user_to_id` ON DUPLICATE KEY UPDATE `volume` = VALUES(`volume`);
SELECT * FROM `money_transactions`;
SELECT * FROM `money_accounts`;
COMMIT;
SET @user_id = 2;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;








SELECT * FROM `money_transactions`;
SELECT * FROM `money_accounts`;
ROLLBACK;

 > SELECT * FROM `money_transactions`;
+----+---------------------+--------------+------------+--------------+
| id | created_at          | user_from_id | user_to_id | volume       |
+----+---------------------+--------------+------------+--------------+
|  1 | 2024-02-12 15:27:39 |         NULL |          1 | 10000.000000 |
|  2 | 2024-02-12 15:27:39 |         NULL |          2 |  4300.000000 |
|  3 | 2024-02-12 15:28:22 |            1 |          2 |  3000.000000 |
|  4 | 2024-02-12 15:28:22 |            2 |          1 | -3000.000000 |
+----+---------------------+--------------+------------+--------------+
4 rows in set (0.00 sec)

 > SELECT * FROM `money_accounts`;
+----+---------+-------------+
| id | user_id | volume      |
+----+---------+-------------+
|  1 |       1 | 7000.000000 |
|  2 |       2 | 7300.000000 |
+----+---------+-------------+
2 rows in set (0.00 sec)
 > SELECT * FROM `money_transactions`;
+----+---------------------+--------------+------------+--------------+
| id | created_at          | user_from_id | user_to_id | volume       |
+----+---------------------+--------------+------------+--------------+
|  1 | 2024-02-12 15:27:39 |         NULL |          1 | 10000.000000 |
|  2 | 2024-02-12 15:27:39 |         NULL |          2 |  4300.000000 |
|  3 | 2024-02-12 15:28:22 |            1 |          2 |  3000.000000 |
|  4 | 2024-02-12 15:28:22 |            2 |          1 | -3000.000000 |
+----+---------------------+--------------+------------+--------------+
4 rows in set (0.00 sec)

 > SELECT * FROM `money_accounts`;
+----+---------+-------------+
| id | user_id | volume      |
+----+---------+-------------+
|  1 |       1 | 7000.000000 |
|  2 |       2 | 7300.000000 |
+----+---------+-------------+
2 rows in set (0.00 sec)

SERIALIZABLE

Суть в том, что вставка в левой сессии заблокируется, т.к. было чтение из правой сессии. Блокируется чтение - поэтому вы не сможете получить данные.

SET @user_id = 1;
SET @user_to_id = 2;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;


INSERT INTO `money_transactions` VALUES (DEFAULT, NOW(), @user_id, @user_to_id, 3000.0);
INSERT INTO `money_transactions` VALUES (DEFAULT, NOW(), @user_to_id, @user_id, -3000.0);
INSERT INTO `money_accounts` SELECT NULL AS `id`, T.`user_to_id` AS `user_id`, SUM(T.`volume`) AS `volume` FROM `money_transactions` AS T GROUP BY T.`user_to_id` ON DUPLICATE KEY UPDATE `volume` = VALUES(`volume`);
SELECT * FROM `money_transactions`;
SELECT * FROM `money_accounts`;
COMMIT;
SET @user_id = 2;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;

SELECT * FROM `money_transactions`;
SELECT * FROM `money_accounts`;
ROLLBACK;

Алгоритмы

  • Не забываем про рекурсию

Сложность алгоритмов

PHP

JavaScript

TypeScript

Angular

Python

Golang

SQL

Linux Boot

Поведенческие вопросы

Решение задач

Дизайн систем

Схема ответа

  • Сбор функциональных требований (что должно быть)
  • Сбор нефункциональных требований (как оно должно быть) + некая вводная
  • Расчет ресурсов RPS write, RPS read, потоки данных, объемы хранилищ
  • Проектирование API (основные методы, схема)
  • Проектирование архитектуры (сервисы, блоки, базы данных, очереди, кэши)
  • Проектирование БД (таблицы в бд сервисов)
  • Оценка отказоустойчивости и масштабируемости системы
  • Расчет железа и сети
  • Сценарии отказов
  • Логгирование
  • Мониторинги


Ссылки

Время отклика систем

Latency.png

Latency Comparison Numbers
--------------------------
L1 cache reference                           0.5 ns
Branch mispredict                            5   ns
L2 cache reference                           7   ns                      14x L1 cache
Mutex lock/unlock                           25   ns
Main memory reference                      100   ns                      20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy            10,000   ns       10 us
Send 1 KB bytes over 1 Gbps network     10,000   ns       10 us
Read 4 KB randomly from SSD*           150,000   ns      150 us          ~1GB/sec SSD
Read 1 MB sequentially from memory     250,000   ns      250 us
Round trip within same datacenter      500,000   ns      500 us
Read 1 MB sequentially from SSD*     1,000,000   ns    1,000 us    1 ms  ~1GB/sec SSD, 4X memory
HDD seek                            10,000,000   ns   10,000 us   10 ms  20x datacenter roundtrip
Read 1 MB sequentially from 1 Gbps  10,000,000   ns   10,000 us   10 ms  40x memory, 10X SSD
Read 1 MB sequentially from HDD     30,000,000   ns   30,000 us   30 ms 120x memory, 30X SSD
Send packet CA->Netherlands->CA    150,000,000   ns  150,000 us  150 ms

Notes
-----
1 ns = 10^-9 seconds
1 us = 10^-6 seconds = 1,000 ns
1 ms = 10^-3 seconds = 1,000 us = 1,000,000 ns
  • https://ping-admin.ru/free_ping/
  • До МСК
    • Европа < 50
    • До Урала < 50
    • Владивосток 100
    • США-Канада 150-250
    • Южная Америка 200-300
    • Азия 200-300
    • Австралия 300

Расположение датацентров в мире

Не функциональные характеристики систем

Availability / Доступность

Доступность % Недоступность в год Недоступность в месяц Недоступность в неделю
90% (1 девяток) 36.5 дней 72 часов 16.8 часов
99% (2 девяток) 3.65 дней 7.20 часов 1.68 часов
99.5% (2 девяток) 1.83 дней 3.60 часов 50.4 минут
99.9% (3 девяток) 8.76 часов 43.8 минут 10.1 минут
99.99% (4 девяток) 52.56 минут 4.32 минут 1.01 минут
99.999% (5 девяток) 5.26 минут 25.9 секунд 6.05 секунд
99.9999% (6 девяток) 31.5 секунд 2.59 секунд 0.605 секунд
99.99999% (7 девяток) 3.15 секунд 0.259 секунд 0.0605 секунд

Reliability / Надежность

Среднее время между отказами (MTBF)

Среднее время отказа (MTTF)

Среднее время восстановления(MTTR)

Scalability / Масштабируемость

  • Вертикальное масштабирование - увеличение мощности узлов (CPU(Frequency, # of cores), MEM(Volume, Frequency), HDD->SSD->NVMe)
  • Горизонтальное масштабирование - увеличение количества узлов

Maintainability / Поддерживаемость

  • Главный параметр - MTTR
  • Легкость кода и прозрачность разработки

Fault Tolerance / Устойчивость к сбоям

  • Отказоустойчивость - способность системы обслуживать запросы даже при отказе ее частей.
  • Можно достичь шардингом и репликацией

Сколько нужно серверов?

Блоки систем

  • DNS
  • Load balancers - Балансировщики
  • Databases - Базы данных
  • KV Storage - Хранилища Ключ Значение
  • CDN - Сеть доставки контента
  • Sequencer - генератор идентификаторов
  • Мониторинг
  • Распределенный кэш
  • Распределенная очередь сообщений
  • Издатель-подписчик
  • Ограничитель нагрузки
  • Файловое хранилище
  • Распределенный поиск
  • Распределенное логгирование
  • Распределенное задачи по расписанию
  • Шардированные счетчики.

Mock интервью

Interview tools

Демотиваторы

Qs-vs-real.png

Nizkoopl-programmist.jpg

Chatgpt-meme.jpeg

Nothing-to-eat.jpg

Suk-sidish.gif

Moskvich-bez-raboty.jpg

Digital-zavod.jpg

Pravki-po-makety.gif

Son-of-owner.jpg

Eboshit.jpeg