You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Рано или поздно все крупные IT-компании создают свои дата-центры. Коля только устроился в такую компанию и еще не успел во всем разобраться. В его компании есть $N$ дата-цетров,в каждом дата-центре установлено $M$ серверов.
Из-за большой нагрузки серверы могут выключаться. Из-за спешки при постройке дата-центров включить только один сервер не получается, поэтому приходится перезагружать весь дата-центр. У каждого дата-центра есть два неотрицательных целочисленных параметра: $R_i$ — число перезапусков $i$-го дата-центра и $A_i$ — число рабочих (не выключенных) серверов на текущий момент в $i$-м дата-центре.
Коля получил задачу по сбору некоторых метрик, которые в будущем позволят улучшить работу дата-центов. Для этого Коля собрал данные о $Q$ событиях, произошедших за текущий день. Коля справился с этой задачей, но просит помочь и проверить свои результаты.
Формат ввода
В первой строке входных данных записано 3 положительных целых числа $n$, $m$, $q$ ($1 \leq q \leq 105$, $1 \leq n \cdot m \leq 106$) — число дата-центров, число серверов в каждом из дата-центров и число событий соответственно.
В последующих $q$ строках записаны события, которые могут иметь один из следующих видов:
RESET$i$ — был перезагружен $i$-й дата-центр ($1 \leq i \leq n$)
DISABLE$i$$j$ — в $i$-м дата-центре был выключен $j$-й сервер ($1 \leq i \leq n$, $1 \leq j \leq m$)
GETMAX — получить номер дата-центра с наибольшим произведением $R_i \cdot A_i$
GETMIN — получить номер дата-центра с наименьшим произведением $R_i \cdot A_i$
Формат вывода
На каждый запрос вида GETMIN или GETMAX выведите единственное положительное целое число — номер дата-центра, подходящий под условие. В случае неоднозначности ответа выведите номер наименьшего из дата-центров.
Рассмотрим древовидную сеть хостов для обработки запросов. Запрос будет обрабатываться следующим образом:
Запрос приходит на корневой хост (пусть он называется balancer.test.yandex.ru);
хост формирует подзапросы на хосты-потомки (не более одного запроса в потомок);
хост ждет получения всех ответов;
хост формирует свой ответ и возвращает его;
Все потомки обрабатывают подзапросы по такой же схеме. Все хосты логируют события ввиде следующих записей:
datetime – время наступления события;
request_id – id запроса;
parent_request_id – id родительского запроса (для корневого бэкенда NULL);
host – имя бэкенда, на котором возникло событие;
type – тип события (список указан ниже);
data – описание события.
Допустимые типы событий:
RequestReceived – на бэкенд поступил новый запрос (поле data пустое);
RequestSent – на бэкенд-потомок был отправлен подзапрос (в поле data записывается имя бэкенда-потомка);
ResponseSent – бэкенд отправил ответ родителю (data пустое);
ResponseReceived – бэкенд получил ответ от потомка (в поле data записываются имя бэкенда-потомка и статус – OK или ERROR –, разделенные символом табуляции).
Все записи собираются в единую базу данных.
Хосты не идеальны, поэтому им требуется время на пересылку запросов и получение ответов. Определим время выполение запроса к корневому хосту, как сумму разниц datetime между всеми соответствующими парами событий RequestSent/RequestReceived и ResponseSent/ResponseReceived, которые возникли при обработке запроса. Найдите эту величину, усредненную по запросам на корневой хост.
Формат ввода
Используется БД postgresql 10.6.1 x64.
Система перед проверкой создает таблицу с событиями следующим запросом:
CREATETABLErequests (
datetime TIMESTAMP,
request_id UUID,
parent_request_id UUID,
host TEXT,
type TEXT,
data TEXT
);
После таблица заполняется тестовыми данными.
Формат вывода
Напишите SELECT выражение, которое вернет таблицу из одной строки с колонкой avg_network_time_ms типа numeric, в которую будет записана средняя сетевая задержка в миллисекундах.
Внимание! Текст выражения подставится в систему как подзапрос, поэтому завершать выражение точкой с запятой не надо (в противном случае вы получите ошибку Presentation Error).
Примечания
Для таблицы requests с таким содержимым (здесь для компактности пишем числа вместо UUID’а и миллисекунды в datetime, в проверочной таблице будут UUID’ы и timestamp’ы):
datetime
request_id
parent_request_id
host
type
data
.000
0
NULL
balancer.test.yandex.ru
RequestReceived
.100
0
NULL
balancer.test.yandex.ru
RequestSent
backend1.ru
.101
0
NULL
balancer.test.yandex.ru
RequestSent
backend2.ru
.150
1
0
backend1.ru
RequestReceived
.200
2
0
backend2.ru
RequestReceived
.155
1
0
backend1.ru
RequestSent
backend3.ru
.210
2
0
backend2.ru
ResponseSent
.200
3
1
backend3.ru
RequestReceived
.220
3
1
backend3.ru
ResponseSent
.260
1
0
backend1.ru
ResponseReceived
backend3.ru OK
.300
1
0
backend1.ru
ResponseSent
.310
0
NULL
balancer.test.yandex.ru
ResponseReceived
backend1.ru OK
.250
0
NULL
balancer.test.yandex.ru
ResponseReceived
backend2.ru OK
.400
0
NULL
balancer.test.yandex.ru
ResponseSent
.500
4
NULL
balancer.test.yandex.ru
RequestReceived
.505
4
NULL
balancer.test.yandex.ru
RequestSent
backend1.ru
.510
5
4
backend1.ru
RequestReceived
.700
5
4
backend1.ru
ResponseSent
.710
4
NULL
balancer.test.yandex.ru
ResponseReceived
backend1.ru ERROR
.715
4
NULL
balancer.test.yandex.ru
ResponseSent
запрос участника должен возвращать следующий результат:
avg_network_time_ms
149.5
Тут два корневых запроса. Выпишем времена, которые прошли между отправкой запроса/ответа и его получением.
Запрос с id 0:
balancer.test.yandex.ru -> backend1.ru – $50$ мс (от $.100$ до $.150$)
balancer.test.yandex.ru -> backend2.ru – $99$ мс (от $.101$ до $.200$)
backend1.ru -> backend3.ru – $45$ мс (от $.155$ до $.200$)
backend2.ru -> balancer.test.yandex.ru – $40$ мс (от $.210$ до $.250$)
backend3.ru -> backend1.ru – $40$ мс (от $.220$ до $.260$)
backend1.ru -> balancer.test.yandex.ru – $10$ мс (от $.300$ до $.310$)
Суммарно это $50+99+45+40+40+10=284$ мс.
Запрос с id 4:
balancer.test.yandex.ru -> backend1.ru – $5$ мс (от $.505$ до $.510$)
backend1.ru -> balancer.test.yandex.ru – $10$ мс (от $.700$ до $.710$)
Суммарно это $5+10=15$ мс.
Итого, ответ $(284+15) / 2=149.5$
SQL
SELECT CAST(
(
(SELECTsum(
1000* EXTRACT (EPOCH FROMrr.datetime) -1000* EXTRACT (EPOCH FROMrs.datetime)
) / (SELECTcount(DISTINCT request_id)
FROM requests
WHERE parent_request_id IS NULL)
FROM requests rs
INNER JOIN requests rr ONrs.request_id=rr.parent_request_idANDrr.host=rs.dataANDrs.type='RequestSent'ANDrr.type='RequestReceived'
) +
(SELECTsum(1000* EXTRACT (EPOCH FROMrr.datetime) -1000* EXTRACT (EPOCH FROMrs.datetime))/
(SELECTcount(DISTINCT request_id)
FROM requests
WHERE parent_request_id IS NULL
)
FROM requests rs
INNER JOIN requests rr ONrr.request_id=rs.parent_request_idANDrr.datalike concat(rs.host, '%')
ANDrs.type='ResponseSent'ANDrr.type='ResponseReceived'
)
) ASNUMERIC
) AS avg_network_time_ms
Сервис подписки
Необъяснимая аномалия! На серверах Яндекс Маркета отказывает оборудование: ломаются жесткие диски, плавится оперативная память, выходит из строя система охлаждения. Системные администраторы локализовали проблему — причиной поломок оказалась используемая база данных. Руководители приняли решение срочно вывести из эксплуатации упомянутую базу данных и заменить ее самописной. Вам нужно как можно скорее предоставить MVP, который поддерживает:
частичное обновление товарных предложений в базе данных
уведомление сервисов-подписчиков при обновлении данных
Товарное предложение в базе описывается следующей JSON схемой:
частичное обновление товарных предложений в базе данных
уведомление сервисов-подписчиков при обновлении данных
Товарное предложение в базе описывается следующей JSON схемой:
{
"$id": "offer.schema.json",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Offer identifier, only numerical symbols are allowed"
},
"price": {
"type": "integer",
"description": "Offer price, value in range from 0 to 10̂9"
},
"stock_count": {
"type": "integer",
"description": "Items left on stocks, value in range from 0 to 10̂9"
},
"partner_content": {
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Offer title filled in by the partner"
},
"description": {
"type": "string",
"description": "Offer description filled in by the partner"
}
}
}
},
"required": [
"id"
]
}
При межсервисном взаимодействии к товарному предложению добавляется контекст, который содержит идентификатор для сквозной трассировки, его схема:
Сервис, который отправляет запрос на обновление товарного предложения, обязательно заполняет идентификатор оффера (поле offer.id) и идентификатор для трассировки (поле trace_id). Все остальные поля в запросе опциональны. В таком случае при применении обновления будет происходить слияние полей. Например, в базе у оффера заполнены поля price=9990, и приходит обновление stock_count=100, тогда в базе будут сохранены оба поля. Гарантируется, что все входящие запросы валидны и соответствуют схеме. Так как это прототип, удаление товаров из базы и очищение полей было решено не поддерживать. Помимо хранения товарных предложений в базе, в сервисе необходима функция доставки обновлений в сервисы-подписчиков. Одна подписка включает в себя два набора полей: trigger и shipment, не обязательно листовых. Когда изменяется любое trigger поле или поле, вложенное в trigger поле, подписчику отправляется сообщение. В сообщении находятся все shipment и trigger поля этого подписчика, а также идентификаторы оффера и трассировки из запроса, который привел к этому сообщению. Инициализация поля также считается за его изменении и создает сообщение об обновлении.
Формат ввода
Первая строка входных данных содержит два целых числа n и m ( 1 ≤ n ≤ 5 0 , 1 ≤ m ≤ 1 0 , 0 0 0 ) — количество сервисов подписчиков и количество запросов на обновления. Следующие n строк содержат описания сервисов подписчиков: i -я строка содержит описание i -го подписчика. В начале строки задается a i и b i — количество trigger и shipment полей соответственно. Далее a i trigger полей, и b i shipment полей. Следующие m строк содержат запросы на обновление, каждая строка — это валидный json, удовлетворяющий схеме m e s s a g e . s c h e m a . j s o n .
Формат вывода
На каждое событие обновления выведите k j сообщений в формате m e s s a g e . s c h e m a . j s o n , где k j — это количество сервисов-подписчиков, которым данное событие интересно. Сообщения должны идти в том же порядке, что и обновления, которые привели к ним. Сообщения в рамках одного обновления должны быть отсортированы по порядковому номеру подписчика.
importjsonclassUser:
def__init__(self, triggers: set, shipments: set):
self.triggers=triggersself.shipments=shipmentsclassOffer:
_offer: dict= {'price': None, 'stock_count': None, 'partner_content': {}}
_isChange: set# helpersdefupdateOffer(self, upgrade_offer: dict):
self._isChange=set()
price=upgrade_offer.get('price') # int | nonestock_count=upgrade_offer.get('stock_count') # int | nonepartner_content=upgrade_offer.get('partner_content') # dict | noneifpriceisnotNoneandself._offer['price'] !=price:
self._offer['price'] =priceself._isChange.add('price')
ifstock_countisnotNoneandself._offer['stock_count'] !=stock_count:
self._offer['stock_count'] =stock_countself._isChange.add('stock_count')
ifpartner_contentisnotNone:
forkeyin ['title', 'description']:
value=partner_content.get(key)
ifvalueisnotNoneandself._offer['partner_content'].get(key) !=value:
self._offer['partner_content'][key] =valueself._isChange.add('partner_content')
defgetOffer(self, trace_id: str, offer_id: str, users):
result= {'id': offer_id}
foruserinusers:
ifself._isChange.intersection(user.triggers):
forfieldinuser.triggers.union(user.shipments):
value=self._offer.get(field)
ifvalueisnotNone:
result[field] =valueprint(json.dumps({'trace_id': trace_id, 'offer': result}))
defmain():
n, m= [int(i) foriininput().split()]
users= [] # все пользователи их может быть от 1 до 50offers= {} # все предложения их может быть от 1 до 10000 foriinrange(n):
# входные данные a - количество trigger полей, b количество shipment полей, services список полей # конкретного пользователяa, b, *services=input().split()
# создаю экземпляр класса User и сую его в список users, класс User имеет 2 поля triggers и shipmentsusers.append(User(set(services[0:int(a)]), set(services[int(a):-1])))
foriinrange(m):
# входные данные, которые преобразуются в jsondata=json.loads(input())
# если оффер не найден в offers, то мы создаем новый экземпляр класса Offers и добавляем его в offersoffer_id=data['offer']['id']
ifoffers.get(offer_id) isNone:
offers[offer_id] =Offer()
# получаем оффер из offersoffer=offers[offer_id]
# обновляем поляoffer.updateOffer(data['offer'])
# уведомляем всем пользователей чью тригер поля были обновленыoffer.getOffer(data['trace_id'], offer_id, users)
if__name__=='__main__':
main()