Решение на HTTP сваляч от Андрея Костов

Обратно към всички решения

Към профила на Андрея Костов

Резултати

  • 3 точки от тестове
  • 0 бонус точки
  • 3 точки общо
  • 4 успешни тест(а)
  • 9 неуспешни тест(а)

Код

package main
import "container/list"
type Request interface {
// Връща идентификатор за заявката. Ако две заявки имат еднакви идентификатори
// то те са "равни".
ID() string
// Блокира докато изпълнява заявката.
// Връща резултата или грешка ако изпълнението е неуспешно.
// Резултата и грешката не трябва да бъдат подавани на SetResult
// за текущата заявка - те са запазват вътрешно преди да бъдат върнати.
Run() (result interface{}, err error)
// Връща дали заявката е кешируерма.
// Метода има неопределено поведение ако бъде извикан преди `Run`.
Cacheable() bool
// Задава резултата на заявката.
// Не трябва да се извиква за заявки, за които е бил извикан `Run`.
SetResult(result interface{}, err error)
}
type Requester interface {
// Добавя заявка за изпълнение и я изпълнява, ако това е необходимо, при първа възможност.
AddRequest(request Request)
// Спира 'Заявчика'. Това означава че изчаква всички вече започнали заявки да завършат
// и извиква `SetResult` на тези заявки които вече са били добавени, но "равни" на тях вече са били изпълнявание.
// Нови заявки не трябва да бъдат започвани през това време, нито ако вече започнати равни на тях да бъдат добавяни за извиквани на `SetResult`.
Stop()
}
type block struct{}
type entry struct {
requestId string
result interface{}
err error
}
type circularBuffer struct {
capacity int
container list.List
lock chan block
}
func (cb *circularBuffer) lockBuffer() {
<-cb.lock
}
func (cb *circularBuffer) unlockBuffer() {
cb.lock <- block{}
}
func (cb *circularBuffer) Add(e entry) {
cb.lockBuffer()
defer cb.unlockBuffer()
if cb.container.Len() >= cb.capacity {
cb.container.Remove(cb.container.Front())
}
cb.container.PushBack(e)
}
func (cb *circularBuffer) Get(requestId string) (entry, bool) {
cb.lockBuffer()
defer cb.unlockBuffer()
for e := cb.container.Front(); e != nil; e = e.Next() {
entry, _ := e.Value.(entry)
if requestId == entry.requestId {
return entry, true
}
}
return entry{}, false
}
type RequesterImpl struct {
cache circularBuffer
bottleneck chan (block)
stopped bool
}
func (r RequesterImpl) AddRequest(request Request) {
if r.stopped {
return
}
r.bottleneck <- block{}
if cached, ok := r.cache.Get(request.ID()); ok {
request.SetResult(cached.result, cached.err)
return
}
result, err := request.Run()
if request.Cacheable() {
r.cache.Add(entry{request.ID(), result, err})
}
<-r.bottleneck
}
func (r RequesterImpl) Stop() {
close(r.bottleneck)
r.stopped = true
}
func NewRequester(cacheSize int, throttleSize int) Requester {
requester := &RequesterImpl{}
requester.cache = circularBuffer{cacheSize, *list.New(), make(chan block, 1)}
requester.bottleneck = make(chan (block), throttleSize)
requester.cache.lock <- block{}
requester.stopped = false
return requester
}

Лог от изпълнението

PASS
ok  	_/tmp/d20160101-5892-1x4em0f	0.003s
PASS
ok  	_/tmp/d20160101-5892-1x4em0f	0.003s
--- FAIL: TestNonCacheableRequestsFast (0.00s)
	solution_test.go:391: second requst has been started :(
--- FAIL: TestNonCacheableRequestsBeingWaitedByStop (0.05s)
	solution_test.go:531: Stop shouldn't have finished before non-cacheable
FAIL
exit status 1
FAIL	_/tmp/d20160101-5892-1x4em0f	0.053s
panic: test timed out after 1s

goroutine 23 [running]:
testing.startAlarm.func1()
	/usr/local/go/src/testing/testing.go:703 +0x132
created by time.goFunc
	/usr/local/go/src/time/sleep.go:129 +0x3a

goroutine 1 [chan receive]:
testing.RunTests(0x5e7f20, 0x676e20, 0xd, 0xd, 0x1)
	/usr/local/go/src/testing/testing.go:562 +0x8ad
testing.(*M).Run(0xc82003fef8, 0xc820062510)
	/usr/local/go/src/testing/testing.go:494 +0x70
main.main()
	_/tmp/d20160101-5892-1x4em0f/_test/_testmain.go:78 +0x116

goroutine 20 [chan receive]:
_/tmp/d20160101-5892-1x4em0f.TestAddRequestRunsOnlyFirstRequest(0xc820098120)
	/tmp/d20160101-5892-1x4em0f/solution_test.go:152 +0x642
testing.tRunner(0xc820098120, 0x676e68)
	/usr/local/go/src/testing/testing.go:456 +0x98
created by testing.RunTests
	/usr/local/go/src/testing/testing.go:561 +0x86d
exit status 2
FAIL	_/tmp/d20160101-5892-1x4em0f	1.005s
PASS
ok  	_/tmp/d20160101-5892-1x4em0f	0.003s
panic: send on closed channel

goroutine 3 [running]:
_/tmp/d20160101-5892-1x4em0f.RequesterImpl.AddRequest(0x2, 0xc820066390, 0xc820066390, 0x0, 0x0, 0x0, 0x0, 0xc820060300, 0xc820060360, 0x0, ...)
	/tmp/d20160101-5892-1x4em0f/solution.go:93 +0x96
_/tmp/d20160101-5892-1x4em0f.(*RequesterImpl).AddRequest(0xc8200840f0, 0x7f2358393620, 0xc8200663f0)
	<autogenerated>:1 +0xaa
created by _/tmp/d20160101-5892-1x4em0f.TestNoRunsAfterStop
	/tmp/d20160101-5892-1x4em0f/solution_test.go:229 +0x64c

goroutine 1 [chan receive]:
testing.RunTests(0x5e7f20, 0x676e20, 0xd, 0xd, 0x1)
	/usr/local/go/src/testing/testing.go:562 +0x8ad
testing.(*M).Run(0xc82003fef8, 0xc820062510)
	/usr/local/go/src/testing/testing.go:494 +0x70
main.main()
	_/tmp/d20160101-5892-1x4em0f/_test/_testmain.go:78 +0x116

goroutine 20 [sleep]:
time.Sleep(0x1312d00)
	/usr/local/go/src/runtime/time.go:59 +0xf9
_/tmp/d20160101-5892-1x4em0f.TestNoRunsAfterStop(0xc8200a2000)
	/tmp/d20160101-5892-1x4em0f/solution_test.go:232 +0x68a
testing.tRunner(0xc8200a2000, 0x676e98)
	/usr/local/go/src/testing/testing.go:456 +0x98
created by testing.RunTests
	/usr/local/go/src/testing/testing.go:561 +0x86d
exit status 2
FAIL	_/tmp/d20160101-5892-1x4em0f	0.027s
panic: test timed out after 1s

goroutine 3 [running]:
testing.startAlarm.func1()
	/usr/local/go/src/testing/testing.go:703 +0x132
created by time.goFunc
	/usr/local/go/src/time/sleep.go:129 +0x3a

goroutine 1 [chan receive]:
testing.RunTests(0x5e7f20, 0x676e20, 0xd, 0xd, 0x1)
	/usr/local/go/src/testing/testing.go:562 +0x8ad
testing.(*M).Run(0xc82003fef8, 0xc820062510)
	/usr/local/go/src/testing/testing.go:494 +0x70
main.main()
	_/tmp/d20160101-5892-1x4em0f/_test/_testmain.go:78 +0x116

goroutine 20 [chan receive]:
_/tmp/d20160101-5892-1x4em0f.TestCacheSize(0xc8200a0000)
	/tmp/d20160101-5892-1x4em0f/solution_test.go:293 +0xaa0
testing.tRunner(0xc8200a0000, 0x676eb0)
	/usr/local/go/src/testing/testing.go:456 +0x98
created by testing.RunTests
	/usr/local/go/src/testing/testing.go:561 +0x86d
exit status 2
FAIL	_/tmp/d20160101-5892-1x4em0f	1.005s
PASS
ok  	_/tmp/d20160101-5892-1x4em0f	0.043s
--- FAIL: TestNonCacheableRequestsFast (0.00s)
	solution_test.go:391: second requst has been started :(
FAIL
exit status 1
FAIL	_/tmp/d20160101-5892-1x4em0f	0.003s
panic: send on closed channel

goroutine 22 [running]:
_/tmp/d20160101-5892-1x4em0f.RequesterImpl.AddRequest(0x1, 0xc820066390, 0xc820066390, 0x0, 0x0, 0x0, 0x0, 0xc820060240, 0xc8200602a0, 0x0, ...)
	/tmp/d20160101-5892-1x4em0f/solution.go:93 +0x96
_/tmp/d20160101-5892-1x4em0f.(*RequesterImpl).AddRequest(0xc820084190, 0x7f6710d40550, 0xc8200663f0)
	<autogenerated>:1 +0xaa
created by _/tmp/d20160101-5892-1x4em0f.TestStopWithQueue
	/tmp/d20160101-5892-1x4em0f/solution_test.go:441 +0x86e

goroutine 1 [chan receive]:
testing.RunTests(0x5e7f20, 0x676e20, 0xd, 0xd, 0x1)
	/usr/local/go/src/testing/testing.go:562 +0x8ad
testing.(*M).Run(0xc82003fef8, 0xc820062510)
	/usr/local/go/src/testing/testing.go:494 +0x70
main.main()
	_/tmp/d20160101-5892-1x4em0f/_test/_testmain.go:78 +0x116

goroutine 20 [runnable]:
time.Sleep(0x1312d00)
	/usr/local/go/src/runtime/time.go:57 +0xb8
_/tmp/d20160101-5892-1x4em0f.TestStopWithQueue(0xc8200a2000)
	/tmp/d20160101-5892-1x4em0f/solution_test.go:450 +0x966
testing.tRunner(0xc8200a2000, 0x676ef8)
	/usr/local/go/src/testing/testing.go:456 +0x98
created by testing.RunTests
	/usr/local/go/src/testing/testing.go:561 +0x86d

goroutine 21 [chan receive]:
_/tmp/d20160101-5892-1x4em0f.TestStopWithQueue.func1(0x0, 0x0, 0x0, 0x0)
	/tmp/d20160101-5892-1x4em0f/solution_test.go:410 +0x68
_/tmp/d20160101-5892-1x4em0f.(*request).Run(0xc8200663c0, 0x0, 0x0, 0x0, 0x0)
	/tmp/d20160101-5892-1x4em0f/solution_test.go:37 +0xa5
_/tmp/d20160101-5892-1x4em0f.RequesterImpl.AddRequest(0x1, 0xc820066390, 0xc820066390, 0x0, 0x0, 0x0, 0x0, 0xc820060240, 0xc8200602a0, 0x0, ...)
	/tmp/d20160101-5892-1x4em0f/solution.go:100 +0x199
_/tmp/d20160101-5892-1x4em0f.(*RequesterImpl).AddRequest(0xc820084190, 0x7f6710d40550, 0xc8200663c0)
	<autogenerated>:1 +0xaa
created by _/tmp/d20160101-5892-1x4em0f.TestStopWithQueue
	/tmp/d20160101-5892-1x4em0f/solution_test.go:439 +0x7d3

goroutine 23 [runnable]:
_/tmp/d20160101-5892-1x4em0f.RequesterImpl.AddRequest(0x1, 0xc820066390, 0xc820066390, 0x0, 0x0, 0x0, 0x0, 0xc820060240, 0xc8200602a0, 0x0, ...)
	/tmp/d20160101-5892-1x4em0f/solution.go:93 +0x96
_/tmp/d20160101-5892-1x4em0f.(*RequesterImpl).AddRequest(0xc820084190, 0x7f6710d40550, 0xc820066420)
	<autogenerated>:1 +0xaa
created by _/tmp/d20160101-5892-1x4em0f.TestStopWithQueue
	/tmp/d20160101-5892-1x4em0f/solution_test.go:442 +0x8e3
exit status 2
FAIL	_/tmp/d20160101-5892-1x4em0f	0.027s
--- FAIL: TestStopWaits (0.00s)
	solution_test.go:482: Stop finished before Ran was
FAIL
exit status 1
FAIL	_/tmp/d20160101-5892-1x4em0f	0.003s
--- FAIL: TestNonCacheableRequestsBeingWaitedByStop (0.05s)
	solution_test.go:531: Stop shouldn't have finished before non-cacheable
FAIL
exit status 1
FAIL	_/tmp/d20160101-5892-1x4em0f	0.053s
panic: send on closed channel

goroutine 18 [running]:
_/tmp/d20160101-5892-1x4em0f.RequesterImpl.AddRequest(0x3e8, 0xc82000a480, 0xc82000a480, 0x0, 0x0, 0x0, 0x0, 0xc82001e2a0, 0xc82001e300, 0x0, ...)
	/tmp/d20160101-5892-1x4em0f/solution.go:93 +0x96
_/tmp/d20160101-5892-1x4em0f.(*RequesterImpl).AddRequest(0xc82001a140, 0x7f3f78946578, 0xc82000a570)
	<autogenerated>:1 +0xaa
created by _/tmp/d20160101-5892-1x4em0f.TestStopWithQueueFromForum
	/tmp/d20160101-5892-1x4em0f/solution_test.go:617 +0xd91

goroutine 1 [chan receive]:
testing.RunTests(0x5e7f20, 0x676e20, 0xd, 0xd, 0x1)
	/usr/local/go/src/testing/testing.go:562 +0x8ad
testing.(*M).Run(0xc82003fef8, 0xc820010650)
	/usr/local/go/src/testing/testing.go:494 +0x70
main.main()
	_/tmp/d20160101-5892-1x4em0f/_test/_testmain.go:78 +0x116

goroutine 6 [sleep]:
time.Sleep(0x1312d00)
	/usr/local/go/src/runtime/time.go:59 +0xf9
_/tmp/d20160101-5892-1x4em0f.TestStopWithQueueFromForum(0xc82008e000)
	/tmp/d20160101-5892-1x4em0f/solution_test.go:625 +0xe14
testing.tRunner(0xc82008e000, 0x676f40)
	/usr/local/go/src/testing/testing.go:456 +0x98
created by testing.RunTests
	/usr/local/go/src/testing/testing.go:561 +0x86d

goroutine 7 [chan receive]:
_/tmp/d20160101-5892-1x4em0f.TestStopWithQueueFromForum.func1(0x0, 0x0, 0x0, 0x0)
	/tmp/d20160101-5892-1x4em0f/solution_test.go:556 +0x71
_/tmp/d20160101-5892-1x4em0f.(*request).Run(0xc82000a4b0, 0x0, 0x0, 0x0, 0x0)
	/tmp/d20160101-5892-1x4em0f/solution_test.go:37 +0xa5
_/tmp/d20160101-5892-1x4em0f.RequesterImpl.AddRequest(0x3e8, 0xc82000a480, 0xc82000a480, 0x0, 0x0, 0x0, 0x0, 0xc82001e2a0, 0xc82001e300, 0x0, ...)
	/tmp/d20160101-5892-1x4em0f/solution.go:100 +0x199
_/tmp/d20160101-5892-1x4em0f.(*RequesterImpl).AddRequest(0xc82001a140, 0x7f3f78946578, 0xc82000a4b0)
	<autogenerated>:1 +0xaa
created by _/tmp/d20160101-5892-1x4em0f.TestStopWithQueueFromForum
	/tmp/d20160101-5892-1x4em0f/solution_test.go:612 +0xbe3

goroutine 8 [chan receive]:
_/tmp/d20160101-5892-1x4em0f.TestStopWithQueueFromForum.func3(0x0, 0x0, 0x0, 0x0)
	/tmp/d20160101-5892-1x4em0f/solution_test.go:584 +0x71
_/tmp/d20160101-5892-1x4em0f.(*request).Run(0xc82000a540, 0x0, 0x0, 0x0, 0x0)
	/tmp/d20160101-5892-1x4em0f/solution_test.go:37 +0xa5
_/tmp/d20160101-5892-1x4em0f.RequesterImpl.AddRequest(0x3e8, 0xc82000a480, 0xc82000a480, 0x0, 0x0, 0x0, 0x0, 0xc82001e2a0, 0xc82001e300, 0x0, ...)
	/tmp/d20160101-5892-1x4em0f/solution.go:100 +0x199
_/tmp/d20160101-5892-1x4em0f.(*RequesterImpl).AddRequest(0xc82001a140, 0x7f3f78946578, 0xc82000a540)
	<autogenerated>:1 +0xaa
created by _/tmp/d20160101-5892-1x4em0f.TestStopWithQueueFromForum
	/tmp/d20160101-5892-1x4em0f/solution_test.go:613 +0xc5b

goroutine 17 [runnable]:
_/tmp/d20160101-5892-1x4em0f.RequesterImpl.AddRequest(0x3e8, 0xc82000a480, 0xc82000a480, 0x0, 0x0, 0x0, 0x0, 0xc82001e2a0, 0xc82001e300, 0x0, ...)
	/tmp/d20160101-5892-1x4em0f/solution.go:93 +0x96
_/tmp/d20160101-5892-1x4em0f.(*RequesterImpl).AddRequest(0xc82001a140, 0x7f3f78946578, 0xc82000a4e0)
	<autogenerated>:1 +0xaa
created by _/tmp/d20160101-5892-1x4em0f.TestStopWithQueueFromForum
	/tmp/d20160101-5892-1x4em0f/solution_test.go:616 +0xd1c
exit status 2
FAIL	_/tmp/d20160101-5892-1x4em0f	0.026s

История (1 версия и 1 коментар)

Андрея обнови решението на 31.12.2015 19:23 (преди над 2 години)

+package main
+
+import "container/list"
+
+type Request interface {
+ // Връща идентификатор за заявката. Ако две заявки имат еднакви идентификатори
+ // то те са "равни".
+ ID() string
+
+ // Блокира докато изпълнява заявката.
+ // Връща резултата или грешка ако изпълнението е неуспешно.
+ // Резултата и грешката не трябва да бъдат подавани на SetResult
+ // за текущата заявка - те са запазват вътрешно преди да бъдат върнати.
+ Run() (result interface{}, err error)
+
+ // Връща дали заявката е кешируерма.
+ // Метода има неопределено поведение ако бъде извикан преди `Run`.
+ Cacheable() bool
+
+ // Задава резултата на заявката.
+ // Не трябва да се извиква за заявки, за които е бил извикан `Run`.
+ SetResult(result interface{}, err error)
+}
+
+type Requester interface {
+ // Добавя заявка за изпълнение и я изпълнява, ако това е необходимо, при първа възможност.
+ AddRequest(request Request)
+
+ // Спира 'Заявчика'. Това означава че изчаква всички вече започнали заявки да завършат
+ // и извиква `SetResult` на тези заявки които вече са били добавени, но "равни" на тях вече са били изпълнявание.
+ // Нови заявки не трябва да бъдат започвани през това време, нито ако вече започнати равни на тях да бъдат добавяни за извиквани на `SetResult`.
+ Stop()
+}
+
+type block struct{}
+
+type entry struct {
+ requestId string
+ result interface{}
+ err error
+}
+
+type circularBuffer struct {
+ capacity int
+ container list.List
+ lock chan block
+}
+
+func (cb *circularBuffer) lockBuffer() {
+ <-cb.lock
+}
+
+func (cb *circularBuffer) unlockBuffer() {
+ cb.lock <- block{}
+}
+
+func (cb *circularBuffer) Add(e entry) {
+ cb.lockBuffer()
+ defer cb.unlockBuffer()
+
+ if cb.container.Len() >= cb.capacity {
+ cb.container.Remove(cb.container.Front())
+ }
+
+ cb.container.PushBack(e)
+}
+
+func (cb *circularBuffer) Get(requestId string) (entry, bool) {
+ cb.lockBuffer()
+ defer cb.unlockBuffer()
+
+ for e := cb.container.Front(); e != nil; e = e.Next() {
+ entry, _ := e.Value.(entry)
+ if requestId == entry.requestId {
+ return entry, true
+ }
+ }
+
+ return entry{}, false
+}
+
+type RequesterImpl struct {
+ cache circularBuffer
+ bottleneck chan (block)
+ stopped bool
+}
+
+func (r RequesterImpl) AddRequest(request Request) {
+ if r.stopped {
+ return
+ }
+
+ r.bottleneck <- block{}
+
+ if cached, ok := r.cache.Get(request.ID()); ok {
+ request.SetResult(cached.result, cached.err)
+ return
+ }
+
+ result, err := request.Run()
+ if request.Cacheable() {
+ r.cache.Add(entry{request.ID(), result, err})
+ }
+
+ <-r.bottleneck
+}
+
+func (r RequesterImpl) Stop() {
+ close(r.bottleneck)
+ r.stopped = true
+}
+
+func NewRequester(cacheSize int, throttleSize int) Requester {
+ requester := &RequesterImpl{}
+ requester.cache = circularBuffer{cacheSize, *list.New(), make(chan block, 1)}
+ requester.bottleneck = make(chan (block), throttleSize)
+ requester.cache.lock <- block{}
+ requester.stopped = false
+
+ return requester
+}