Add shopfront task
This commit is contained in:
parent
d65fd9bcf1
commit
f51848efa4
6 changed files with 172 additions and 1 deletions
|
@ -1,7 +1,7 @@
|
||||||
FROM golang:1.17
|
FROM golang:1.17
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
rsync libssl-dev postgresql sudo \
|
rsync libssl-dev postgresql sudo redis-server \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.44.0
|
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.44.0
|
||||||
|
|
21
shopfront/README.md
Normal file
21
shopfront/README.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# shopfront
|
||||||
|
|
||||||
|
В этой задаче вам нужно реализовать хранилище счётчиков посещений поверх redis.
|
||||||
|
|
||||||
|
- Метод `RecordView` запоминает, что пользователь посетил страницу `item`-а.
|
||||||
|
- Метод `GetItems` загружает счётчики для пачки `item` ов. В поле `item[i].Viewed` должен
|
||||||
|
быть записан флаг, означающий что пользователь посетил `i`-ый `item`.
|
||||||
|
|
||||||
|
В этой задаче есть benchmark-и. Чтобы пройти его, ваше решение должно использовать [pipelining](https://redis.io/docs/manual/pipelining/).
|
||||||
|
|
||||||
|
## Запуск тестов на linux
|
||||||
|
|
||||||
|
Для работы тестов на ubuntu нужно установить пакет `redis-server`.
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install redis-server
|
||||||
|
```
|
||||||
|
|
||||||
|
Если вы работаете на другом дистрибутиве linux, воспользуйтесь своим пакетным менеджером.
|
||||||
|
|
||||||
|
Тесты сами запускают `redis` в начале, и останавливают его в конце.
|
19
shopfront/model.go
Normal file
19
shopfront/model.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package shopfront
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type (
|
||||||
|
ItemID int64
|
||||||
|
UserID int64
|
||||||
|
)
|
||||||
|
|
||||||
|
type Item struct {
|
||||||
|
ViewCount int
|
||||||
|
Viewed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Counters interface {
|
||||||
|
GetItems(ctx context.Context, ids []ItemID, userID UserID) ([]Item, error)
|
||||||
|
|
||||||
|
RecordView(ctx context.Context, id ItemID, userID UserID) error
|
||||||
|
}
|
56
shopfront/redis_test.go
Normal file
56
shopfront/redis_test.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package shopfront_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"gitlab.com/slon/shad-go/tools/testtool"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testingTB interface {
|
||||||
|
Logf(format string, args ...interface{})
|
||||||
|
Fatalf(format string, args ...interface{})
|
||||||
|
Errorf(format string, args ...interface{})
|
||||||
|
FailNow()
|
||||||
|
Cleanup(func())
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartRedis(tb testingTB) string {
|
||||||
|
if redis, ok := os.LookupEnv("REDIS"); ok {
|
||||||
|
tb.Logf("using external redis server; REDIS=%s", redis)
|
||||||
|
return redis
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err := testtool.GetFreePort()
|
||||||
|
require.NoError(tb, err)
|
||||||
|
|
||||||
|
_, err = exec.LookPath("redis-server")
|
||||||
|
if err != nil {
|
||||||
|
tb.Fatalf("redis-server binary is not found; is redis installed?")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("redis-server", "--port", port, "--save", "", "--appendonly", "no")
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
require.NoError(tb, cmd.Start())
|
||||||
|
|
||||||
|
finished := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
finished <- cmd.Wait()
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-finished:
|
||||||
|
tb.Fatalf("redis server terminated: %v", err)
|
||||||
|
|
||||||
|
case <-time.After(time.Second / 2):
|
||||||
|
}
|
||||||
|
|
||||||
|
tb.Cleanup(func() {
|
||||||
|
_ = cmd.Process.Kill()
|
||||||
|
})
|
||||||
|
|
||||||
|
return "localhost:" + port
|
||||||
|
}
|
9
shopfront/shopfront.go
Normal file
9
shopfront/shopfront.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
//go:build !solution
|
||||||
|
|
||||||
|
package shopfront
|
||||||
|
|
||||||
|
import "github.com/go-redis/redis/v8"
|
||||||
|
|
||||||
|
func New(rdb *redis.Client) Counters {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
66
shopfront/shopfront_test.go
Normal file
66
shopfront/shopfront_test.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package shopfront_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"gitlab.com/slon/shad-go/shopfront"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShopfront(t *testing.T) {
|
||||||
|
rdb := redis.NewClient(&redis.Options{
|
||||||
|
Addr: StartRedis(t),
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
c := shopfront.New(rdb)
|
||||||
|
|
||||||
|
items, err := c.GetItems(ctx, []shopfront.ItemID{1, 2, 3, 4}, 42)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, items, []shopfront.Item{
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NoError(t, c.RecordView(ctx, 3, 42))
|
||||||
|
require.NoError(t, c.RecordView(ctx, 2, 42))
|
||||||
|
|
||||||
|
require.NoError(t, c.RecordView(ctx, 2, 4242))
|
||||||
|
|
||||||
|
items, err = c.GetItems(ctx, []shopfront.ItemID{1, 2, 3, 4}, 42)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, items, []shopfront.Item{
|
||||||
|
{},
|
||||||
|
{ViewCount: 2, Viewed: true},
|
||||||
|
{ViewCount: 1, Viewed: true},
|
||||||
|
{},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkShopfront(b *testing.B) {
|
||||||
|
const nItems = 1024
|
||||||
|
|
||||||
|
rdb := redis.NewClient(&redis.Options{
|
||||||
|
Addr: StartRedis(b),
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
c := shopfront.New(rdb)
|
||||||
|
|
||||||
|
var ids []shopfront.ItemID
|
||||||
|
for i := 0; i < nItems; i++ {
|
||||||
|
ids = append(ids, shopfront.ItemID(i))
|
||||||
|
require.NoError(b, c.RecordView(ctx, shopfront.ItemID(i), 42))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err := c.GetItems(ctx, ids, 42)
|
||||||
|
require.NoError(b, err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue