Как стать автором
Обновить
8
Карма
0
Рейтинг

Пишу код

  • Подписчики 1
  • Подписки

Брокер очередей Capella Queue

Про облака. Если бы я делал для себя, то, наверное, я бы воспользовался облачными и очередями и БД и вообще всей инфраструктурой, лишь бы денег хватило. Облака рулят. Однако многие организации не готовы сделать шаг в сторону облаков, например в том самом крупном интернет магазине были свои ДЦ и возможности получить сервера в другом месте не было.

Про s3. Пробовали. S3 предоставлялся во внутренних ДЦ. С помощью чего он был сделан не знаю. Он не прошел тесты моей команды и мы отказались реализовывать на нем. Другие команды соглашались, привело к потере чувствительных данных, после того, как предоставляемый s3 «сломался». Думаю такой проблемы с облачным s3 не было бы. S3 хорош, что бы выдавать картинки и другой контент, для быстрой записи лучше использовать другие инструменты. Для решения задачи с заказами мы использовали несколько десятков независимых кластеров PG. Решение получилось очень монструозным, но несколько раз спасало, когда возникали разного рода ЧП. Именно тогда у меня возникла идея написать этот брокер.

На вопрос про связанность. Когда нарушена связанность (например 10 кластеров и все потеряли связь друг с другом), сообщение должно быть сохранено в несколько кластеров (например в 2 из 10). Когда связь наладится, сообщения, за счёт обработчика копирования, будут скопированы на остальные 8 кластеров. Да, нет защиты от того, что можно отправить сообщения с разным содержимым, но одним уникальным ключом.

«сколько в граммах» увы коммерческая тайна, и хоть я уже не работаю в этом интернет магазине, раскрывать её не буду. (Порядок от 1 до 100 в сек). Да это не нагрузка, но с учётом требования пусть всё горит (и такое было), но заказ сохрани даже эта нагрузка становится ощутимой.

Брокер очередей Capella Queue

Спасибо за замечания по оформлению, согласен с ними, я обязательно добавлю диаграммы и картинки в следующих статьях.
Про то, что я сделал — продукт действительно в начале пути.
Претензий к Кафке нет. Кафка — прекрасный брокер, у нас вся отчётность сидела на ней и НАТСе. Проблема в том, что для сохранения заказа требования были таковы, что нужно было иметь возможность сохранять заказы на машины, которые временно оторванны от остального ДЦ. В Кафке этого просто невозможно сделать :( (ну или я не умею).

Хабрастыд-2020

Wildberries — это крутой магазин, в котором работает очень много хороших специалистов. И владельцы, как бы спорно они не управляли, управляют очень эффективно. Тем не менее, по моим сведениям, Татьяна описала "оптимизацию", которая коснулась и её отдела, достаточно точно.

А давайте в Go сделаем TryLock(context.Context)

Вот что я придумал, мы:
1. создаём waitgroup
2. лочим мьютекс
3. создаём 1000 рутин в каждой из которых
3.1 лочим мьютекс
3.2 разлочивам мьютекс
3.3 делаем Done в waitgroup
4. Разлочиваем мьютекс тем самым создаём «лавину» — 1000 одновременно ожидающих мьютексов
5. ждём waitgroup он освободится после последнего анлока

Результаты:
BenchmarkN0T_RWTMutexTryLockUnlock-8 2174 535396 ns/op 36406 B/op 380 allocs/op
BenchmarkN0T_RWMutexLockUnlock-8 2400 447199 ns/op 52 B/op 1 allocs/op
BenchmarkN0T_MutexLockUnlock-8 3020 388154 ns/op 39 B/op 1 allocs/op


Мой мьютекс дольше стандартных на ~ 30 %
А а RW мьютекс дольше простого на ~ 15 %

Код бенча примера
func BenchmarkN0T_RWTMutexTryLockUnlock(b *testing.B) {
	ctx := context.Background()
	mx := RWTMutex{}

	k := 1000

	for i := 0; i < b.N; i++ {
		var wg sync.WaitGroup
		wg.Add(k)

		mx.TryLock(ctx)
		for j := 0; j < k; j++ {
			go func() {
				mx.TryLock(ctx)

				mx.Unlock()
				wg.Done()
			}()
		}
		mx.Unlock()

		wg.Wait()
	}
}

А давайте в Go сделаем TryLock(context.Context)

Про 100 потоков — организовать сложно, но вот с 2мя тест есть, как раз его написал по исходному замечанию. И он показывает, что обычные мьютексы быстрее на четверть, чем мой. + мой жрёт доп память на каждую итерацию примерно 200 байт.

А давайте в Go сделаем TryLock(context.Context)

Да увы, я как почитал конд тоже думал получится приспособить, но нет путей. Жалко.

А давайте в Go сделаем TryLock(context.Context)

Бенчмарки 2х видов

Для незаблокированного мьютекса, тут просто заблокировать и разблокировать когда нет параллельного потока, то есть случай, когда по коду пойдёт быстрый случай:
func BenchmarkRWTMutexTryLockUnlock(b *testing.B) {
	ctx := context.Background()
	mx := RWTMutex{}

	for i := 0; i < b.N; i++ {
		mx.TryLock(ctx)
		mx.Unlock()
	}
}


Для заблокированного мьютекса, тут требуется организовать 2 потока и сделать примерно следующее: блокируем мьютекс, затем создаём рутину (~ 250 нс на моей машине) в которой мьютекс разблокируем, и снова блокируем мьютекс (тут мы начинаем ожидать той самой разблокировки в параллельном потоке) и потом разблокируем мьютекс для следующей итерации.
Получается:
func BenchmarkDT_RWTMutexTryLockUnlock(b *testing.B) {
	ctx := context.Background()
	mx := RWTMutex{}

	for i := 0; i < b.N; i++ {
		mx.TryLock(ctx)

		go func() {
			mx.Unlock()
		}()

		mx.TryLock(ctx)
		mx.Unlock()
	}
}


По поводу sync.Cond. Я потоки лочу через select { case } это позволяет мне их разблокировать несколькими независимыми событиями. Если я буду блочить через Cond.Wait() то мне придётся разлочивать Cond на основании <-ctx.Done(), а этого я без дополнительного потока не смогу сделать.

Как-то так.

А давайте в Go сделаем TryLock(context.Context)

Добавил бенчмарки:
BenchmarkDT_RWTMutexTryLockUnlock-8 1911730 625 ns/op 192 B/op 1 allocs/op
BenchmarkDT_RWMutexLockUnlock-8 2423566 494 ns/op 0 B/op 0 allocs/op
BenchmarkDT_MutexLockUnlock-8 2577855 466 ns/op 0 B/op 0 allocs/op


Потребляется память за счёт того, что пересоздаётся канал.

Оценил Cond. Реализацию с ним плохо представляю без создания доп потока (а это 4 кб) так, что бы корректно реагировать на ctx.Done().

Если я не прав прошу меня попправить.

А давайте в Go сделаем TryLock(context.Context)

Спасибо, что прочитали и вникли в тему.

По поводу cond, я как то упустил из виду. Бегло прочитав я согласен, что вероятно он подходит больше. Я думаю я перепишу с его использованием или использованием того, что под ним лежит. Но пока по изучаю.

Да бенчи проверяют только быстрый путь. Дополню вместе с переходом на cond.

И видимо нужно добавить еще пару веселых фишек типа поднятие уровня блокировки RLock -> Lock и понижение, и некую приоритезацию для блокировки. Они не дороги в разработке и по идее по ресурсам тоже.

Информация

В рейтинге
Не участвует
Откуда
Москва, Москва и Московская обл., Россия
Зарегистрирован
Активность