Pull to refresh
20
0

Багодел и токсик

Send message

Поставил лайк статье. Но каждый раз наблюдая фразы типа: "Если же архитектура подразумевает сложную логику в базе данных, то на бесшовном обновлении можно поставить крест - сохранение обратной совместимости будет дороже изменений, ради которых происходит обновление." я морщусь, так как, по моему мнению, такое заблуждение исходит исключительно из поверхностного подхода к работе с базами данных. Если вы можете заменить бд на key-value хранилище, то логики в БД у вас действительно нет. Но если не можете, то вы лукавите, и логика в бд у вас есть.

Попробуйте рассмотреть базы данных как отдельный независимый сервис. Причины его отделения обычно технологические. Но всё же это отдельный сервис со своим интерфейсом (внешним контрактом)

Статья понравилась, спасибо!

Хочу отметить, что есть проекты с разной степенью готовности к наличию требований. Очень часто нужно проверить гипотезу или сделать "хоть как-нибудь, лишь бы заработало". Прежде чем утверждать что требования нужны, стоит всегда задавать вопрос а зачем? Стоит ли время потраченное на описание и соблюдение требований меньше чем цена положительного эффекта от этих требований?

Есть классный принцип DDD. У него есть 2 варианта расшифровки:

Давай давай деплой и в этом случае требований нет, есть бизнес необходимость или научный интерес.

Предметно-ориентированное проектирование и в этом случае часто требования описываются уже при описании общего языка.

К чему это я. Мне не понравилась фраза "Требования служат фундаментом для каждого проекта и необходимы для глубокого понимания командой всех аспектов предстоящей работы.". По моему мнению фундамент для каждого проекта это экономическая выгода или "научный" интерес. А понимание аспектов предстоящей работы основывается на глубоком вникании в суть предстоящей работы и начинается с того, что язык общения всех членов команды сводится к единому и понятному набору слов и понятий.

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

В DDD есть понятие агрегата. По моему мнению именно разделение кода на агрегаты ведёт к корректному соблюдению DRY.

SOLID буква I - Interface Segregation обозначает, как мне кажется, в том числе и по зоне применимости. Мне понравился пример со скоростями. Забираю себе закладки как хороший пример для объяснения буквы I :)

Говоря про zap стоит раскрыть тему sampling-а

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

Вот что я придумал, мы:
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()
	}
}
Про 100 потоков — организовать сложно, но вот с 2мя тест есть, как раз его написал по исходному замечанию. И он показывает, что обычные мьютексы быстрее на четверть, чем мой. + мой жрёт доп память на каждую итерацию примерно 200 байт.
Да увы, я как почитал конд тоже думал получится приспособить, но нет путей. Жалко.
Бенчмарки 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(), а этого я без дополнительного потока не смогу сделать.

Как-то так.
Добавил бенчмарки:
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().

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

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

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

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

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Registered
Activity

Specialization

Specialist
From 2,000,000 ₽