gomempro
Расширение пакета github.com/bradfitz/gomemcache/memcache, позволяет помещать в мемкеш структуры
Предыстория
Работая с языком Perl, я привык, что там можно помещать в мемкеш структуру любой сложности (массив, хеш с многими подуровнями и т.д.). И затем получить её обратно в том же виде, даже не задумываясь о скрытых под капотом механизмах. Ну а если копнуть глубже, можно и чуть поковырять этот механизм: выбрать или написать свой сериализатор, позволяющий, к примеру, дополнительно сжимать данные.
Поэтому, я был удивлён, что аналогичный пакет для go позволяет помещать в мемкеш только строки (вернее, массивы байтов). И я написал небольшое расширение, дополняющее пакет memcached методами для работы с объектами (структурами произвольного типа).
Не смотря на простоту, тут используются многие фишки go: и встраивание структуры, и горутины с каналами, и работа с пустым интерфейсом.
Пример использования
// подключаемся к мемкешу. синтаксис полностью аналогичен пакету memcache
mc := mempro.New("127.0.0.1:11211")
// тестовая структура
type Ex struct {
Num int
Str string
}
x := Ex{Num: 123, Str: "abc"}
// помещаем в мемкеш
err := mc.SetStruct("key_one", x)
// то же самое, только задаём ещё время жизни - 5 минут
err := mc.SetStruct("key_one", x, 5*60)
// вытаскиваем из мемкеша
var y Ex
err = mc.GetStruct("key_one", &y)
fmt.Println(y) // {123 abc}
// мультигет
var z Ex
var w SomeOtherType // структуры могут быть разные в одном мультигете
// понадобится карта, ключами которой будут имена ключей мемкеша
// а значениями - пустые структуры, которые будут заполнены данными
list := make(map[string]any)
list["key_one"] = &z
list["key_two"] = &w
// передаём карту в метод GetMultiStruct, который заполнит поля структур данными
err = mc.GetMultiStruct(list)
fmt.Println(list["key_one"]) // &{123 abc}
// если хотим получить не ссылку на структуру, а её саму, тогда так:
v, ok := list["key_one"].(*Ex)
fmt.Println(*v) // {123 abc}
// также можно пользоваться всеми методами memcache:
new_val, err := mc.Increment("key_three", 1)
Бенчмарки
Находятся вместе с тестами в mempro_test.go.
Сравнивал три метода. Первый - BenchmarkGetMulti, который с помощью GetMultiStruct получает за раз 100 ключей из мемкеша. Второй, BenchmarkGetSingle - те же 100 ключей, но в цикле через единичный гет. Третий, самый интересный: версия GetMultiStruct в которой дешифровка данных в структуры идёт не параллельно в горутинах, а в один поток (BenchmarkGetMultiSimple). Это позволяет сильно упростить код. Результаты:
goos: linux
goarch: amd64
pkg: github.com/vklimenkov/gomempro
cpu: 11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz
BenchmarkGetMulti-8 4173 270738 ns/op 69922 B/op 1620 allocs/op
BenchmarkGetSingle-8 919 1353884 ns/op 53439 B/op 1797 allocs/op
BenchmarkGetMultiSimple-8 4104 247973 ns/op 63158 B/op 1516 allocs/op
Ожидаемый результат - единичный get сильно проигрывает мульти-версии, и по скорости и по работе с памятью. А вот распараллеливание дешифровки практически ничего не даёт. Видимо, потенциальный выигрыш нивелируется усложнением кода и вводом дполнительных действий. Так что можно обойтись простой версией метода, BenchmarkGetMultiSimple
Лицензия
Свободное распространение