From da22edc78db1e7171a0cd19fea30d292b7bb6e83 Mon Sep 17 00:00:00 2001 From: mahtabfarrokh Date: Wed, 10 Apr 2019 17:58:55 +0430 Subject: [PATCH 01/14] finally 28 tests passed :D --- internal/broker/core.go | 202 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 188 insertions(+), 14 deletions(-) diff --git a/internal/broker/core.go b/internal/broker/core.go index f9b0a8b..b5a620f 100644 --- a/internal/broker/core.go +++ b/internal/broker/core.go @@ -2,9 +2,11 @@ package broker import ( "context" + "fmt" + "sync" "time" - "github.com/arcana261/ubroker/pkg/ubroker" + "github.com/mahtabfarrokh/ubroker/pkg/ubroker" "github.com/pkg/errors" ) @@ -13,34 +15,206 @@ import ( // we requeue an unacknowledged/unrequeued message // automatically. func New(ttl time.Duration) ubroker.Broker { - return &core{} + temp := &core{ + closed: false, + brokerChan: make(chan ubroker.Delivery, 1000), + publishedQueue: []item{}, + receivedId: []int{}, + deliveredId: []int{}, + receivedAck: []int{}, + receivedRequeue: []int{}, + lastIdValue: -1, + deliveryStarted: false, + wg: sync.WaitGroup{}, + ttl: ttl, + } + + return temp } +type item struct { + Message ubroker.Message + ID int + Time time.Time + receivedAckChannel chan int +} type core struct { - // TODO: add required fields + closed bool + brokerChan chan ubroker.Delivery + publishedQueue []item + receivedId []int + deliveredId []int + lastIdValue int + receivedAck []int + receivedRequeue []int + wg sync.WaitGroup + mut sync.Mutex + deliveryStarted bool + ttl time.Duration } +func contextProblem(ctx context.Context) bool { + if ctx.Err() == context.Canceled { + return true + } + if ctx.Err() == context.DeadlineExceeded { + return true + } + return false +} func (c *core) Delivery(ctx context.Context) (<-chan ubroker.Delivery, error) { - // TODO:‌ implement me - return nil, errors.Wrap(ubroker.ErrUnimplemented, "method Delivery is not implemented") + + if contextProblem(ctx) { + return nil, ctx.Err() + } + if c.closed { + return nil, errors.Wrap(ubroker.ErrClosed, "closed") + } + c.deliveryStarted = true + //c.wg.Done() + return c.brokerChan, nil + //return nil, errors.Wrap(ubroker.ErrUnimplemented, "method Delivery is not implemented") } func (c *core) Acknowledge(ctx context.Context, id int) error { - // TODO:‌ implement me - return errors.Wrap(ubroker.ErrUnimplemented, "method Acknowledge is not implemented") + if c.closed { + return errors.Wrap(ubroker.ErrClosed, "closed") + } + if contextProblem(ctx) { + return ctx.Err() + } + temp := false + if c.deliveryStarted { + temp = true + } + for _, element := range c.receivedAck { + if element == id { + temp = false + } + } + if !temp { + return errors.Wrap(ubroker.ErrInvalidID, "invalid Id") + } + if c.closed { + return errors.Wrap(ubroker.ErrClosed, "closed") + } + c.mut.Lock() + c.receivedAck = append(c.receivedAck, id) + for i, element := range c.publishedQueue { + if element.ID == id { + c.publishedQueue[i].receivedAckChannel <- id + c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) + } + } + c.mut.Unlock() + //c.wg.Done() + return nil } +func (c *core) DoingReQueue(ctx context.Context, id int) { + c.mut.Lock() + for i, element := range c.publishedQueue { -func (c *core) ReQueue(ctx context.Context, id int) error { - // TODO:‌ implement me - return errors.Wrap(ubroker.ErrUnimplemented, "method ReQueue is not implemented") + if element.ID == id { + c.receivedRequeue = append(c.receivedRequeue, id) + c.receivedAck = append(c.receivedAck, id) + c.lastIdValue += 1 + c.receivedId = append(c.receivedId, c.lastIdValue) + v := ubroker.Delivery{Message: element.Message, ID: c.lastIdValue} + v2 := item{Message: element.Message, ID: c.lastIdValue, Time: time.Now(), receivedAckChannel: make(chan int)} + fmt.Println(len(c.publishedQueue), id, i) + c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) + c.publishedQueue = append(c.publishedQueue, v2) + c.brokerChan <- v + go c.HandelingTTL(ctx, v2) + break + } + + } + c.mut.Unlock() + defer c.wg.Done() } +func (c *core) HandelingTTL(ctx context.Context, element item) { + c.wg.Add(1) + select { + case <-time.After(c.ttl): + go c.DoingReQueue(ctx, element.ID) + case <-element.receivedAckChannel: + c.wg.Done() + return + } +} +func (c *core) ReQueue(ctx context.Context, id int) error { + c.wg.Add(1) + if c.closed { + c.wg.Done() + return errors.Wrap(ubroker.ErrClosed, "closed") + } + if contextProblem(ctx) { + c.wg.Done() + return ctx.Err() + } + temp := false + if c.deliveryStarted { + temp = true + } + for _, element := range c.receivedRequeue { + if element == id { + temp = false + } + } + for _, element := range c.receivedAck { + if element == id { + temp = false + } + } + if !temp { + c.wg.Done() + return errors.Wrap(ubroker.ErrInvalidID, "invalid Id") + } + c.DoingReQueue(ctx, id) + return nil +} +func (c *core) DoingPublish(ctx context.Context, message ubroker.Message) { + c.mut.Lock() + c.lastIdValue += 1 + c.receivedId = append(c.receivedId, c.lastIdValue) + v := ubroker.Delivery{Message: message, ID: c.lastIdValue} + v2 := item{Message: message, ID: c.lastIdValue, Time: time.Now(), receivedAckChannel: make(chan int)} + c.publishedQueue = append(c.publishedQueue, v2) + c.brokerChan <- v + go c.HandelingTTL(ctx, v2) + c.mut.Unlock() + defer c.wg.Done() +} func (c *core) Publish(ctx context.Context, message ubroker.Message) error { - // TODO:‌ implement me - return errors.Wrap(ubroker.ErrUnimplemented, "method Publish is not implemented") + c.wg.Add(1) + + if contextProblem(ctx) { + c.wg.Done() + return ctx.Err() + } + if c.closed { + c.wg.Done() + return errors.Wrap(ubroker.ErrClosed, "closed") + } + if contextProblem(ctx) { + c.wg.Done() + return ctx.Err() + } + c.DoingPublish(ctx, message) + return nil } func (c *core) Close() error { - // TODO:‌ implement me - return errors.Wrap(ubroker.ErrUnimplemented, "method Close is not implemented") + if c.closed { + return nil + } + c.wg.Wait() + close(c.brokerChan) + //close(c.publishedQueue) + c.closed = true + //fmt.Println(e) + return nil + //return nil } From 81583d4e01bcc599276bf319a7d153a71525b008 Mon Sep 17 00:00:00 2001 From: mahtabfarrokh Date: Wed, 10 Apr 2019 18:09:01 +0430 Subject: [PATCH 02/14] change name --- internal/broker/core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/broker/core.go b/internal/broker/core.go index b5a620f..398108b 100644 --- a/internal/broker/core.go +++ b/internal/broker/core.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "github.com/mahtabfarrokh/ubroker/pkg/ubroker" + "github.com/arcana261/ubroker/pkg/ubroker" "github.com/pkg/errors" ) From 97f80dc2434d4a8a7bb9f6a62d04a86a5fe9f0bd Mon Sep 17 00:00:00 2001 From: mahtabfarrokh Date: Wed, 10 Apr 2019 18:27:32 +0430 Subject: [PATCH 03/14] bug fixed --- internal/broker/core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/broker/core.go b/internal/broker/core.go index 398108b..13ca027 100644 --- a/internal/broker/core.go +++ b/internal/broker/core.go @@ -17,7 +17,7 @@ import ( func New(ttl time.Duration) ubroker.Broker { temp := &core{ closed: false, - brokerChan: make(chan ubroker.Delivery, 1000), + brokerChan: make(chan ubroker.Delivery, 2000), publishedQueue: []item{}, receivedId: []int{}, deliveredId: []int{}, From c5d2ad2e62d58265be14d4ec5cf356522f5dba35 Mon Sep 17 00:00:00 2001 From: mahtabfarrokh Date: Wed, 10 Apr 2019 19:43:18 +0430 Subject: [PATCH 04/14] some changes --- internal/broker/core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/broker/core.go b/internal/broker/core.go index 13ca027..24fa22d 100644 --- a/internal/broker/core.go +++ b/internal/broker/core.go @@ -17,7 +17,7 @@ import ( func New(ttl time.Duration) ubroker.Broker { temp := &core{ closed: false, - brokerChan: make(chan ubroker.Delivery, 2000), + brokerChan: make(chan ubroker.Delivery, 4000), publishedQueue: []item{}, receivedId: []int{}, deliveredId: []int{}, From 7664e04fae40c08235ad9103f54b95e59d82e6f0 Mon Sep 17 00:00:00 2001 From: mahtabfarrokh Date: Wed, 10 Apr 2019 19:54:38 +0430 Subject: [PATCH 05/14] some changes --- internal/broker/core.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/broker/core.go b/internal/broker/core.go index 24fa22d..06c3670 100644 --- a/internal/broker/core.go +++ b/internal/broker/core.go @@ -2,7 +2,6 @@ package broker import ( "context" - "fmt" "sync" "time" @@ -17,7 +16,7 @@ import ( func New(ttl time.Duration) ubroker.Broker { temp := &core{ closed: false, - brokerChan: make(chan ubroker.Delivery, 4000), + brokerChan: make(chan ubroker.Delivery, 5000), publishedQueue: []item{}, receivedId: []int{}, deliveredId: []int{}, @@ -121,7 +120,7 @@ func (c *core) DoingReQueue(ctx context.Context, id int) { c.receivedId = append(c.receivedId, c.lastIdValue) v := ubroker.Delivery{Message: element.Message, ID: c.lastIdValue} v2 := item{Message: element.Message, ID: c.lastIdValue, Time: time.Now(), receivedAckChannel: make(chan int)} - fmt.Println(len(c.publishedQueue), id, i) + //fmt.Println(len(c.publishedQueue), id, i) c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) c.publishedQueue = append(c.publishedQueue, v2) c.brokerChan <- v From 17471d4e746a293e3fdfc4db113c819ab9da091b Mon Sep 17 00:00:00 2001 From: mahtabfarrokh Date: Wed, 10 Apr 2019 22:08:30 +0430 Subject: [PATCH 06/14] try to fix bug --- internal/broker/core.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/internal/broker/core.go b/internal/broker/core.go index 06c3670..1780188 100644 --- a/internal/broker/core.go +++ b/internal/broker/core.go @@ -19,7 +19,6 @@ func New(ttl time.Duration) ubroker.Broker { brokerChan: make(chan ubroker.Delivery, 5000), publishedQueue: []item{}, receivedId: []int{}, - deliveredId: []int{}, receivedAck: []int{}, receivedRequeue: []int{}, lastIdValue: -1, @@ -34,7 +33,6 @@ func New(ttl time.Duration) ubroker.Broker { type item struct { Message ubroker.Message ID int - Time time.Time receivedAckChannel chan int } type core struct { @@ -42,7 +40,6 @@ type core struct { brokerChan chan ubroker.Delivery publishedQueue []item receivedId []int - deliveredId []int lastIdValue int receivedAck []int receivedRequeue []int @@ -76,6 +73,7 @@ func (c *core) Delivery(ctx context.Context) (<-chan ubroker.Delivery, error) { } func (c *core) Acknowledge(ctx context.Context, id int) error { + if c.closed { return errors.Wrap(ubroker.ErrClosed, "closed") } @@ -103,6 +101,7 @@ func (c *core) Acknowledge(ctx context.Context, id int) error { if element.ID == id { c.publishedQueue[i].receivedAckChannel <- id c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) + break } } c.mut.Unlock() @@ -119,7 +118,7 @@ func (c *core) DoingReQueue(ctx context.Context, id int) { c.lastIdValue += 1 c.receivedId = append(c.receivedId, c.lastIdValue) v := ubroker.Delivery{Message: element.Message, ID: c.lastIdValue} - v2 := item{Message: element.Message, ID: c.lastIdValue, Time: time.Now(), receivedAckChannel: make(chan int)} + v2 := item{Message: element.Message, ID: c.lastIdValue, receivedAckChannel: make(chan int)} //fmt.Println(len(c.publishedQueue), id, i) c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) c.publishedQueue = append(c.publishedQueue, v2) @@ -179,7 +178,7 @@ func (c *core) DoingPublish(ctx context.Context, message ubroker.Message) { c.lastIdValue += 1 c.receivedId = append(c.receivedId, c.lastIdValue) v := ubroker.Delivery{Message: message, ID: c.lastIdValue} - v2 := item{Message: message, ID: c.lastIdValue, Time: time.Now(), receivedAckChannel: make(chan int)} + v2 := item{Message: message, ID: c.lastIdValue, receivedAckChannel: make(chan int)} c.publishedQueue = append(c.publishedQueue, v2) c.brokerChan <- v go c.HandelingTTL(ctx, v2) From b4f965945196ec5651ee74cfc2704d1649126bf9 Mon Sep 17 00:00:00 2001 From: mahtabfarrokh Date: Fri, 12 Apr 2019 06:40:49 +0430 Subject: [PATCH 07/14] data race bug fixed --- internal/broker/core.go | 99 +++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/internal/broker/core.go b/internal/broker/core.go index 1780188..6ff1a3b 100644 --- a/internal/broker/core.go +++ b/internal/broker/core.go @@ -2,6 +2,7 @@ package broker import ( "context" + "fmt" "sync" "time" @@ -16,7 +17,8 @@ import ( func New(ttl time.Duration) ubroker.Broker { temp := &core{ closed: false, - brokerChan: make(chan ubroker.Delivery, 5000), + brokerChan: make(chan ubroker.Delivery, 1000), + closedChan: make(chan bool, 5000), publishedQueue: []item{}, receivedId: []int{}, receivedAck: []int{}, @@ -38,6 +40,7 @@ type item struct { type core struct { closed bool brokerChan chan ubroker.Delivery + closedChan chan bool publishedQueue []item receivedId []int lastIdValue int @@ -64,7 +67,7 @@ func (c *core) Delivery(ctx context.Context) (<-chan ubroker.Delivery, error) { return nil, ctx.Err() } if c.closed { - return nil, errors.Wrap(ubroker.ErrClosed, "closed") + return nil, ubroker.ErrClosed } c.deliveryStarted = true //c.wg.Done() @@ -73,14 +76,16 @@ func (c *core) Delivery(ctx context.Context) (<-chan ubroker.Delivery, error) { } func (c *core) Acknowledge(ctx context.Context, id int) error { - + fmt.Println("id", id) if c.closed { - return errors.Wrap(ubroker.ErrClosed, "closed") + return ubroker.ErrClosed } if contextProblem(ctx) { return ctx.Err() } temp := false + c.mut.Lock() + fmt.Println("locked in ack") if c.deliveryStarted { temp = true } @@ -89,66 +94,83 @@ func (c *core) Acknowledge(ctx context.Context, id int) error { temp = false } } + if !temp { + fmt.Println("locked in ack released") + c.mut.Unlock() return errors.Wrap(ubroker.ErrInvalidID, "invalid Id") } if c.closed { - return errors.Wrap(ubroker.ErrClosed, "closed") + fmt.Println("locked in ack released") + c.mut.Unlock() + + return ubroker.ErrClosed } - c.mut.Lock() c.receivedAck = append(c.receivedAck, id) for i, element := range c.publishedQueue { if element.ID == id { + fmt.Println("-") c.publishedQueue[i].receivedAckChannel <- id + fmt.Println("--") c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) break } } + fmt.Println("locked in ack released") c.mut.Unlock() + //c.wg.Done() return nil } func (c *core) DoingReQueue(ctx context.Context, id int) { - c.mut.Lock() for i, element := range c.publishedQueue { - if element.ID == id { c.receivedRequeue = append(c.receivedRequeue, id) c.receivedAck = append(c.receivedAck, id) c.lastIdValue += 1 c.receivedId = append(c.receivedId, c.lastIdValue) v := ubroker.Delivery{Message: element.Message, ID: c.lastIdValue} - v2 := item{Message: element.Message, ID: c.lastIdValue, receivedAckChannel: make(chan int)} + v2 := item{Message: element.Message, ID: c.lastIdValue, receivedAckChannel: make(chan int, 10)} //fmt.Println(len(c.publishedQueue), id, i) c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) c.publishedQueue = append(c.publishedQueue, v2) c.brokerChan <- v + fmt.Println("locked released doing requeue") + c.mut.Unlock() go c.HandelingTTL(ctx, v2) break } } - c.mut.Unlock() - defer c.wg.Done() + } func (c *core) HandelingTTL(ctx context.Context, element item) { - c.wg.Add(1) + fmt.Println("ha?") select { case <-time.After(c.ttl): - go c.DoingReQueue(ctx, element.ID) + fmt.Println("inja? ") + c.mut.Lock() + fmt.Println("locked in handeling ttl ") + c.DoingReQueue(ctx, element.ID) + return case <-element.receivedAckChannel: - c.wg.Done() + fmt.Println("what ?") + return + case <-c.closedChan: return } } func (c *core) ReQueue(ctx context.Context, id int) error { - c.wg.Add(1) + c.mut.Lock() + fmt.Println("locked here requqeue!") if c.closed { - c.wg.Done() - return errors.Wrap(ubroker.ErrClosed, "closed") + fmt.Println("locked requeue released") + c.mut.Unlock() + return ubroker.ErrClosed } if contextProblem(ctx) { - c.wg.Done() + fmt.Println("locked requeue released") + c.mut.Unlock() return ctx.Err() } temp := false @@ -166,41 +188,38 @@ func (c *core) ReQueue(ctx context.Context, id int) error { } } if !temp { - c.wg.Done() + fmt.Println("locked requeue released") + c.mut.Unlock() + return errors.Wrap(ubroker.ErrInvalidID, "invalid Id") } - c.DoingReQueue(ctx, id) return nil } func (c *core) DoingPublish(ctx context.Context, message ubroker.Message) { - c.mut.Lock() c.lastIdValue += 1 c.receivedId = append(c.receivedId, c.lastIdValue) v := ubroker.Delivery{Message: message, ID: c.lastIdValue} - v2 := item{Message: message, ID: c.lastIdValue, receivedAckChannel: make(chan int)} + v2 := item{Message: message, ID: c.lastIdValue, receivedAckChannel: make(chan int, 10)} c.publishedQueue = append(c.publishedQueue, v2) c.brokerChan <- v - go c.HandelingTTL(ctx, v2) + fmt.Println("locked doing publish released") c.mut.Unlock() - defer c.wg.Done() + + c.HandelingTTL(ctx, v2) + //defer c.wg.Done() } func (c *core) Publish(ctx context.Context, message ubroker.Message) error { - c.wg.Add(1) - if contextProblem(ctx) { - c.wg.Done() return ctx.Err() } + c.mut.Lock() + fmt.Println("locked publish") if c.closed { - c.wg.Done() - return errors.Wrap(ubroker.ErrClosed, "closed") + c.mut.Unlock() + return ubroker.ErrClosed } - if contextProblem(ctx) { - c.wg.Done() - return ctx.Err() - } - c.DoingPublish(ctx, message) + go c.DoingPublish(ctx, message) return nil } @@ -208,11 +227,15 @@ func (c *core) Close() error { if c.closed { return nil } - c.wg.Wait() + //c.wg.Wait() + for i := 0; i < 4000; i++ { + c.closedChan <- true + } + fmt.Println("here wait") + c.mut.Lock() + fmt.Println("closed") close(c.brokerChan) - //close(c.publishedQueue) c.closed = true - //fmt.Println(e) + c.mut.Unlock() return nil - //return nil } From 25491b33676da19bff2c8037ecb045b6ffc35160 Mon Sep 17 00:00:00 2001 From: mahtabfarrokh Date: Fri, 12 Apr 2019 06:49:32 +0430 Subject: [PATCH 08/14] go -race bug fixed --- internal/broker/core.go | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/internal/broker/core.go b/internal/broker/core.go index 6ff1a3b..3d4c98e 100644 --- a/internal/broker/core.go +++ b/internal/broker/core.go @@ -2,7 +2,6 @@ package broker import ( "context" - "fmt" "sync" "time" @@ -69,23 +68,25 @@ func (c *core) Delivery(ctx context.Context) (<-chan ubroker.Delivery, error) { if c.closed { return nil, ubroker.ErrClosed } + c.mut.Lock() c.deliveryStarted = true - //c.wg.Done() + c.mut.Unlock() return c.brokerChan, nil //return nil, errors.Wrap(ubroker.ErrUnimplemented, "method Delivery is not implemented") } func (c *core) Acknowledge(ctx context.Context, id int) error { - fmt.Println("id", id) + c.mut.Lock() if c.closed { + c.mut.Unlock() return ubroker.ErrClosed } if contextProblem(ctx) { + c.mut.Unlock() return ctx.Err() } temp := false - c.mut.Lock() - fmt.Println("locked in ack") + if c.deliveryStarted { temp = true } @@ -96,12 +97,10 @@ func (c *core) Acknowledge(ctx context.Context, id int) error { } if !temp { - fmt.Println("locked in ack released") c.mut.Unlock() return errors.Wrap(ubroker.ErrInvalidID, "invalid Id") } if c.closed { - fmt.Println("locked in ack released") c.mut.Unlock() return ubroker.ErrClosed @@ -109,14 +108,11 @@ func (c *core) Acknowledge(ctx context.Context, id int) error { c.receivedAck = append(c.receivedAck, id) for i, element := range c.publishedQueue { if element.ID == id { - fmt.Println("-") c.publishedQueue[i].receivedAckChannel <- id - fmt.Println("--") c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) break } } - fmt.Println("locked in ack released") c.mut.Unlock() //c.wg.Done() @@ -135,7 +131,6 @@ func (c *core) DoingReQueue(ctx context.Context, id int) { c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) c.publishedQueue = append(c.publishedQueue, v2) c.brokerChan <- v - fmt.Println("locked released doing requeue") c.mut.Unlock() go c.HandelingTTL(ctx, v2) break @@ -145,16 +140,12 @@ func (c *core) DoingReQueue(ctx context.Context, id int) { } func (c *core) HandelingTTL(ctx context.Context, element item) { - fmt.Println("ha?") select { case <-time.After(c.ttl): - fmt.Println("inja? ") c.mut.Lock() - fmt.Println("locked in handeling ttl ") c.DoingReQueue(ctx, element.ID) return case <-element.receivedAckChannel: - fmt.Println("what ?") return case <-c.closedChan: return @@ -162,14 +153,11 @@ func (c *core) HandelingTTL(ctx context.Context, element item) { } func (c *core) ReQueue(ctx context.Context, id int) error { c.mut.Lock() - fmt.Println("locked here requqeue!") if c.closed { - fmt.Println("locked requeue released") c.mut.Unlock() return ubroker.ErrClosed } if contextProblem(ctx) { - fmt.Println("locked requeue released") c.mut.Unlock() return ctx.Err() } @@ -188,7 +176,6 @@ func (c *core) ReQueue(ctx context.Context, id int) error { } } if !temp { - fmt.Println("locked requeue released") c.mut.Unlock() return errors.Wrap(ubroker.ErrInvalidID, "invalid Id") @@ -203,7 +190,6 @@ func (c *core) DoingPublish(ctx context.Context, message ubroker.Message) { v2 := item{Message: message, ID: c.lastIdValue, receivedAckChannel: make(chan int, 10)} c.publishedQueue = append(c.publishedQueue, v2) c.brokerChan <- v - fmt.Println("locked doing publish released") c.mut.Unlock() c.HandelingTTL(ctx, v2) @@ -214,7 +200,6 @@ func (c *core) Publish(ctx context.Context, message ubroker.Message) error { return ctx.Err() } c.mut.Lock() - fmt.Println("locked publish") if c.closed { c.mut.Unlock() return ubroker.ErrClosed @@ -231,9 +216,7 @@ func (c *core) Close() error { for i := 0; i < 4000; i++ { c.closedChan <- true } - fmt.Println("here wait") c.mut.Lock() - fmt.Println("closed") close(c.brokerChan) c.closed = true c.mut.Unlock() From c8adf38427edf0a6d3ee519c697fdf333f764094 Mon Sep 17 00:00:00 2001 From: mahtabfarrokh Date: Thu, 23 May 2019 18:05:01 +0430 Subject: [PATCH 09/14] GRPC Done. --- cmd/ubroker/main.go | 4 +- internal/broker/core.go | 307 ++++++++++++++++++----------------- internal/broker/core_test.go | 18 +- internal/server/http.go | 4 +- internal/server/http_test.go | 4 +- pkg/ubroker/ubroker.go | 1 - 6 files changed, 176 insertions(+), 162 deletions(-) diff --git a/cmd/ubroker/main.go b/cmd/ubroker/main.go index 40e5f9e..9264721 100644 --- a/cmd/ubroker/main.go +++ b/cmd/ubroker/main.go @@ -7,8 +7,8 @@ import ( "os/signal" "time" - "github.com/arcana261/ubroker/internal/broker" - "github.com/arcana261/ubroker/internal/server" + "github.com/mahtabfarrokh/ubroker/internal/broker" + "github.com/mahtabfarrokh/ubroker/internal/server" ) func main() { diff --git a/internal/broker/core.go b/internal/broker/core.go index 3d4c98e..57ebbfa 100644 --- a/internal/broker/core.go +++ b/internal/broker/core.go @@ -5,7 +5,7 @@ import ( "sync" "time" - "github.com/arcana261/ubroker/pkg/ubroker" + "github.com/mahtabfarrokh/ubroker/pkg/ubroker" "github.com/pkg/errors" ) @@ -13,212 +13,217 @@ import ( // with given `ttl`. `ttl` determines time in which // we requeue an unacknowledged/unrequeued message // automatically. + func New(ttl time.Duration) ubroker.Broker { - temp := &core{ - closed: false, - brokerChan: make(chan ubroker.Delivery, 1000), - closedChan: make(chan bool, 5000), - publishedQueue: []item{}, - receivedId: []int{}, - receivedAck: []int{}, - receivedRequeue: []int{}, - lastIdValue: -1, - deliveryStarted: false, - wg: sync.WaitGroup{}, - ttl: ttl, - } - - return temp -} -type item struct { - Message ubroker.Message - ID int - receivedAckChannel chan int + c := &core{ + delFlag: false, + isClosed: false, + delChan: make(chan ubroker.Delivery, 100), + mainQ: make([]messageType, 0), + lastID: 0, + ackedMessageID: make([]int, 0), + ttl: ttl, + } + return c } + type core struct { - closed bool - brokerChan chan ubroker.Delivery - closedChan chan bool - publishedQueue []item - receivedId []int - lastIdValue int - receivedAck []int - receivedRequeue []int - wg sync.WaitGroup - mut sync.Mutex - deliveryStarted bool - ttl time.Duration + sync.Mutex + isClosed bool + delChan chan ubroker.Delivery + lastID int + mainQ []messageType + ttl time.Duration + delFlag bool + ackedMessageID []int } -func contextProblem(ctx context.Context) bool { - if ctx.Err() == context.Canceled { - return true - } - if ctx.Err() == context.DeadlineExceeded { - return true - } - return false +type messageType struct { + msg ubroker.Delivery + ttlTime time.Time } + func (c *core) Delivery(ctx context.Context) (<-chan ubroker.Delivery, error) { - if contextProblem(ctx) { + switch ctx.Err() { + case context.Canceled: + return nil, ctx.Err() + case context.DeadlineExceeded: return nil, ctx.Err() } - if c.closed { + c.Lock() + defer c.Unlock() + if c.isClosed == true { return nil, ubroker.ErrClosed } - c.mut.Lock() - c.deliveryStarted = true - c.mut.Unlock() - return c.brokerChan, nil - //return nil, errors.Wrap(ubroker.ErrUnimplemented, "method Delivery is not implemented") + c.delFlag = true + + return c.delChan, nil } func (c *core) Acknowledge(ctx context.Context, id int) error { - c.mut.Lock() - if c.closed { - c.mut.Unlock() - return ubroker.ErrClosed - } - if contextProblem(ctx) { - c.mut.Unlock() - return ctx.Err() - } - temp := false - if c.deliveryStarted { - temp = true + switch ctx.Err() { + case context.Canceled: + return ctx.Err() + case context.DeadlineExceeded: + return ctx.Err() } - for _, element := range c.receivedAck { - if element == id { - temp = false - } + c.Lock() + defer c.Unlock() + if c.isClosed == true { + return ubroker.ErrClosed } - if !temp { - c.mut.Unlock() - return errors.Wrap(ubroker.ErrInvalidID, "invalid Id") + // check delivery done + if c.delFlag == false { + return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") } - if c.closed { - c.mut.Unlock() - return ubroker.ErrClosed - } - c.receivedAck = append(c.receivedAck, id) - for i, element := range c.publishedQueue { - if element.ID == id { - c.publishedQueue[i].receivedAckChannel <- id - c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) + // published + var indexID = -1 + for index, message := range c.mainQ { + if message.msg.ID == id { + indexID = index break } } - c.mut.Unlock() - //c.wg.Done() - return nil -} -func (c *core) DoingReQueue(ctx context.Context, id int) { - for i, element := range c.publishedQueue { - if element.ID == id { - c.receivedRequeue = append(c.receivedRequeue, id) - c.receivedAck = append(c.receivedAck, id) - c.lastIdValue += 1 - c.receivedId = append(c.receivedId, c.lastIdValue) - v := ubroker.Delivery{Message: element.Message, ID: c.lastIdValue} - v2 := item{Message: element.Message, ID: c.lastIdValue, receivedAckChannel: make(chan int, 10)} - //fmt.Println(len(c.publishedQueue), id, i) - c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) - c.publishedQueue = append(c.publishedQueue, v2) - c.brokerChan <- v - c.mut.Unlock() - go c.HandelingTTL(ctx, v2) + // acked befor + var ackIndex = -1 + for index, ids := range c.ackedMessageID { + if ids == id { + ackIndex = index break } + } + if indexID == -1 { + return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") } -} -func (c *core) HandelingTTL(ctx context.Context, element item) { - select { - case <-time.After(c.ttl): - c.mut.Lock() - c.DoingReQueue(ctx, element.ID) - return - case <-element.receivedAckChannel: - return - case <-c.closedChan: - return + if ackIndex != -1 { + return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") + } + // checked ttl time + if time.Now().Sub(c.mainQ[indexID].ttlTime) > c.ttl { + return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") + } else { + // acked + c.ackedMessageID = append(c.ackedMessageID, id) + return nil } + + return nil } + func (c *core) ReQueue(ctx context.Context, id int) error { - c.mut.Lock() - if c.closed { - c.mut.Unlock() - return ubroker.ErrClosed - } - if contextProblem(ctx) { - c.mut.Unlock() + + //c.wg.Done() + //defer c.wg.Done() + + switch ctx.Err() { + case context.Canceled: + return ctx.Err() + case context.DeadlineExceeded: return ctx.Err() } - temp := false - if c.deliveryStarted { - temp = true + c.Lock() + defer c.Unlock() + if c.isClosed == true { + return ubroker.ErrClosed } - for _, element := range c.receivedRequeue { - if element == id { - temp = false - } + + //check delivery + if c.delFlag == false { + return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") } - for _, element := range c.receivedAck { - if element == id { - temp = false + + // published? + var indexID = -1 + for index, message := range c.mainQ { + if message.msg.ID == id { + indexID = index + break } } - if !temp { - c.mut.Unlock() - return errors.Wrap(ubroker.ErrInvalidID, "invalid Id") + if indexID == -1 { + return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") + } + // check ttl time + if time.Now().Sub(c.mainQ[indexID].ttlTime) > c.ttl { + c.doReQ(c.mainQ[indexID]) + c.mainQ = append(c.mainQ[:indexID], c.mainQ[indexID+1:]...) + return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") + } else { + c.doReQ(c.mainQ[indexID]) + c.mainQ = append(c.mainQ[:indexID], c.mainQ[indexID+1:]...) + return nil + } - c.DoingReQueue(ctx, id) return nil } -func (c *core) DoingPublish(ctx context.Context, message ubroker.Message) { - c.lastIdValue += 1 - c.receivedId = append(c.receivedId, c.lastIdValue) - v := ubroker.Delivery{Message: message, ID: c.lastIdValue} - v2 := item{Message: message, ID: c.lastIdValue, receivedAckChannel: make(chan int, 10)} - c.publishedQueue = append(c.publishedQueue, v2) - c.brokerChan <- v - c.mut.Unlock() - - c.HandelingTTL(ctx, v2) + +func (c *core) doReQ(msg1 messageType) error { + //c.wg.Add(1) //defer c.wg.Done() + c.lastID = c.lastID + 1 + var newMsg ubroker.Delivery + newMsg.Message = msg1.msg.Message + newMsg.ID = c.lastID + var newnewmsg = messageType{} + newnewmsg.msg = newMsg + newnewmsg.ttlTime = time.Now() + + c.mainQ = append(c.mainQ, newnewmsg) + //send message to channel + c.delChan <- newMsg + return nil } + func (c *core) Publish(ctx context.Context, message ubroker.Message) error { - if contextProblem(ctx) { + //c.wg.Add(1) + //defer c.wg.Done() + + switch ctx.Err() { + + case context.Canceled: + return ctx.Err() + case context.DeadlineExceeded: return ctx.Err() } - c.mut.Lock() - if c.closed { - c.mut.Unlock() + c.Lock() + defer c.Unlock() + + if c.isClosed == true { return ubroker.ErrClosed } - go c.DoingPublish(ctx, message) + + c.lastID = c.lastID + 1 + var newMsg ubroker.Delivery + newMsg.Message = message + newMsg.ID = c.lastID + //send message to channel + c.delChan <- newMsg + + var newnewmsg = messageType{} + newnewmsg.msg = newMsg + newnewmsg.ttlTime = time.Now() + c.mainQ = append(c.mainQ, newnewmsg) return nil } func (c *core) Close() error { - if c.closed { - return nil - } //c.wg.Wait() - for i := 0; i < 4000; i++ { - c.closedChan <- true + if c.isClosed { + return nil } - c.mut.Lock() - close(c.brokerChan) - c.closed = true - c.mut.Unlock() + c.Lock() + defer c.Unlock() + c.isClosed = true + close(c.delChan) + return nil } diff --git a/internal/broker/core_test.go b/internal/broker/core_test.go index 9d6530d..fe6bdd3 100644 --- a/internal/broker/core_test.go +++ b/internal/broker/core_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" - "github.com/arcana261/ubroker/internal/broker" - "github.com/arcana261/ubroker/pkg/ubroker" + "github.com/mahtabfarrokh/ubroker/internal/broker" + "github.com/mahtabfarrokh/ubroker/pkg/ubroker" "github.com/pkg/errors" "github.com/stretchr/testify/assert" @@ -30,9 +30,7 @@ func TestCoreBrokerTestSuite(t *testing.T) { func BenchmarkPublish(b *testing.B) { broker := broker.New(1 * time.Minute) s := assert.New(b) - b.ResetTimer() - for i := 0; i < b.N; i++ { s.Nil(broker.Publish(context.Background(), ubroker.Message{})) } @@ -161,6 +159,7 @@ func (s *CoreBrokerTestSuite) TestMessageShouldNotBeQueueablePreemptively() { s.prepareTest(1 * time.Second) s.publish("hello") for id := -100; id <= 100; id++ { + s.assertErrorEquals(ubroker.ErrInvalidID, s.broker.ReQueue(context.Background(), id)) } } @@ -193,11 +192,14 @@ func (s *CoreBrokerTestSuite) TestMultipleDeliveriesShouldBeAcknowledgeableIndep } func (s *CoreBrokerTestSuite) TestAcknowledgedMessageShouldNotAppearInDelivery() { + s.prepareTest(1 * time.Second) s.publish("hello") delivery := s.getDelivery(context.Background()) + msg := <-delivery s.Nil(s.broker.Acknowledge(context.Background(), msg.ID)) + s.assertEmpty(delivery) } @@ -214,8 +216,11 @@ func (s *CoreBrokerTestSuite) TestDeliveryShouldBeReQueueable() { func (s *CoreBrokerTestSuite) TestDeliveryShouldNotBeReQueueableTwice() { s.prepareTest(1 * time.Second) + s.publish("hello") + msg := <-s.getDelivery(context.Background()) + s.Nil(s.broker.ReQueue(context.Background(), msg.ID)) s.assertErrorEquals(ubroker.ErrInvalidID, s.broker.ReQueue(context.Background(), msg.ID)) } @@ -292,9 +297,14 @@ func (s *CoreBrokerTestSuite) TestDeliveryShouldFailOnClosedBroker() { func (s *CoreBrokerTestSuite) TestCloseShouldCloseDeliveryChannel() { s.prepareTest(1 * time.Second) + fmt.Println(context.Background()) delivery := s.getDelivery(context.Background()) + fmt.Println(delivery) s.Nil(s.broker.Close()) _, ok := <-delivery + fmt.Println(s) + + fmt.Println(ok) s.False(ok) } diff --git a/internal/server/http.go b/internal/server/http.go index 6dcac4c..c4b75d9 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -17,7 +17,7 @@ import ( "github.com/sirupsen/logrus" - "github.com/arcana261/ubroker/pkg/ubroker" + "github.com/mahtabfarrokh/ubroker/pkg/ubroker" "github.com/pkg/errors" ) @@ -79,7 +79,7 @@ func (s *httpServer) Run() error { ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) defer cancel() - + print(ctx) s.delivery, err = s.broker.Delivery(ctx) if err != nil { return err diff --git a/internal/server/http_test.go b/internal/server/http_test.go index d29d161..b7ce875 100644 --- a/internal/server/http_test.go +++ b/internal/server/http_test.go @@ -9,8 +9,8 @@ import ( "strings" "testing" - "github.com/arcana261/ubroker/internal/server" - "github.com/arcana261/ubroker/pkg/ubroker" + "github.com/mahtabfarrokh/ubroker/internal/server" + "github.com/mahtabfarrokh/ubroker/pkg/ubroker" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) diff --git a/pkg/ubroker/ubroker.go b/pkg/ubroker/ubroker.go index 18268cd..931872f 100644 --- a/pkg/ubroker/ubroker.go +++ b/pkg/ubroker/ubroker.go @@ -93,7 +93,6 @@ type Message struct { type Delivery struct { // Message is the message fetched from queue Message Message `json:"message"` - // ID ID int `json:"id"` } From 480e623d4ddb9d313836e1cae0f81b15b3525941 Mon Sep 17 00:00:00 2001 From: mahtabfarrokh Date: Thu, 23 May 2019 18:12:22 +0430 Subject: [PATCH 10/14] GRPC Done. --- api/ubroker.proto | 51 +++ cmd/ubroker/main.go | 33 +- internal/broker/core.go | 415 ++++++++++++++--------- internal/broker/core_test.go | 150 ++++----- internal/server/grpc.go | 80 +++++ internal/server/grpc_test.go | 382 ++++++++++++++++++++++ internal/server/http.go | 22 +- internal/server/http_test.go | 66 +--- internal/server/moc_broker_test.go | 44 +++ pkg/ubroker/ubroker.go | 23 +- pkg/ubroker/ubroker.pb.go | 506 +++++++++++++++++++++++++++++ 11 files changed, 1457 insertions(+), 315 deletions(-) create mode 100644 api/ubroker.proto create mode 100644 internal/server/grpc.go create mode 100644 internal/server/grpc_test.go create mode 100644 internal/server/moc_broker_test.go create mode 100644 pkg/ubroker/ubroker.pb.go diff --git a/api/ubroker.proto b/api/ubroker.proto new file mode 100644 index 0000000..7b2c23c --- /dev/null +++ b/api/ubroker.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; + +package ubroker; +option go_package = "github.com/arcana261/ubroker/pkg/ubroker"; + +import "google/protobuf/empty.proto"; + +service Broker { + // Fetch should return a single Delivery per FetchRequest. + // Should return: + // Unavailable: If broker has been closed + rpc Fetch(stream FetchRequest) returns (stream Delivery); + + // Acknowledge a message + // Should return: + // OK: on success + // Unavailable: If broker has been closed + // InvalidArgument: If requested ID is invalid + rpc Acknowledge(AcknowledgeRequest) returns (google.protobuf.Empty); + + // ReQueue a message + // OK: on success + // Unavailable: If broker has been closed + // InvalidArgument: If requested ID is invalid + rpc ReQueue(ReQueueRequest) returns (google.protobuf.Empty); + + // Publish message to Queue + // OK: on success + // Unavailable: If broker has been closed + rpc Publish(Message) returns (google.protobuf.Empty); +} + +message Message { + bytes body = 1; +} + +message Delivery { + Message message = 1; + int32 id = 2; +} + +message FetchRequest { +} + +message AcknowledgeRequest { + int32 id = 1; +} + +message ReQueueRequest { + int32 id = 1; +} \ No newline at end of file diff --git a/cmd/ubroker/main.go b/cmd/ubroker/main.go index 9264721..95e9562 100644 --- a/cmd/ubroker/main.go +++ b/cmd/ubroker/main.go @@ -3,12 +3,17 @@ package main import ( "flag" "fmt" + "log" + "net" "os" "os/signal" "time" - "github.com/mahtabfarrokh/ubroker/internal/broker" - "github.com/mahtabfarrokh/ubroker/internal/server" + "github.com/arcana261/ubroker/pkg/ubroker" + "google.golang.org/grpc" + + "github.com/arcana261/ubroker/internal/broker" + "github.com/arcana261/ubroker/internal/server" ) func main() { @@ -19,13 +24,15 @@ func main() { broker := broker.New(time.Duration(*ttlPtr) * time.Millisecond) endpoint := fmt.Sprintf(":%d", *portPtr) - srv := server.NewHTTP(broker, endpoint) + servicer := server.NewGRPC(broker) - if err := srv.Run(); err != nil { - panic(err.Error()) - } + grpcServer := grpc.NewServer() + ubroker.RegisterBrokerServer(grpcServer, servicer) - fmt.Printf("listening on %s\n", endpoint) + listener, err := net.Listen("tcp", endpoint) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } signalChan := make(chan os.Signal, 1) cleanupDone := make(chan struct{}) @@ -35,11 +42,17 @@ func main() { fmt.Printf("\nReceived an interrupt, stopping services...\n\n") close(cleanupDone) }() + + go func() { + if err := grpcServer.Serve(listener); err != nil { + log.Fatalf("failed to serve: %v", err) + } + }() + + fmt.Printf("listening on %s\n", endpoint) <-cleanupDone - if err := srv.Close(); err != nil { - panic(err.Error()) - } + grpcServer.GracefulStop() if err := broker.Close(); err != nil { panic(err.Error()) diff --git a/internal/broker/core.go b/internal/broker/core.go index 57ebbfa..c040e9b 100644 --- a/internal/broker/core.go +++ b/internal/broker/core.go @@ -5,225 +5,338 @@ import ( "sync" "time" - "github.com/mahtabfarrokh/ubroker/pkg/ubroker" "github.com/pkg/errors" + + "github.com/arcana261/ubroker/pkg/ubroker" ) // New creates a new instance of ubroker.Broker // with given `ttl`. `ttl` determines time in which // we requeue an unacknowledged/unrequeued message // automatically. - func New(ttl time.Duration) ubroker.Broker { - - c := &core{ - delFlag: false, - isClosed: false, - delChan: make(chan ubroker.Delivery, 100), - mainQ: make([]messageType, 0), - lastID: 0, - ackedMessageID: make([]int, 0), - ttl: ttl, + broker := &core{ + ttl: ttl, + requests: make(chan interface{}), + deliveryChannel: make(chan *ubroker.Delivery), + closed: make(chan bool, 1), + closing: make(chan bool, 1), + pending: make(map[int32]*ubroker.Message), + messages: []*ubroker.Delivery{{}}, } - return c + + broker.wg.Add(1) + go broker.startDelivery() + + return broker } type core struct { - sync.Mutex - isClosed bool - delChan chan ubroker.Delivery - lastID int - mainQ []messageType - ttl time.Duration - delFlag bool - ackedMessageID []int + nextID int32 + ttl time.Duration + + mutex sync.Mutex + working sync.WaitGroup + wg sync.WaitGroup + + requests chan interface{} + deliveryChannel chan *ubroker.Delivery + closed chan bool + closing chan bool + pending map[int32]*ubroker.Message + messages []*ubroker.Delivery + channel chan *ubroker.Delivery } -type messageType struct { - msg ubroker.Delivery - ttlTime time.Time +type acknowledgeRequest struct { + id int32 + response chan acknowledgeResponse } -func (c *core) Delivery(ctx context.Context) (<-chan ubroker.Delivery, error) { +type acknowledgeResponse struct { + id int32 + err error +} - switch ctx.Err() { - case context.Canceled: - return nil, ctx.Err() - case context.DeadlineExceeded: +type requeueRequest struct { + id int32 + response chan requeueResponse +} + +type requeueResponse struct { + id int32 + err error +} + +type publishRequest struct { + message *ubroker.Message + response chan publishResponse +} + +type publishResponse struct { + err error +} + +func (c *core) Delivery(ctx context.Context) (<-chan *ubroker.Delivery, error) { + if isCanceledContext(ctx) { return nil, ctx.Err() } - c.Lock() - defer c.Unlock() - if c.isClosed == true { + + if !c.startWorking() { return nil, ubroker.ErrClosed } - c.delFlag = true + defer c.working.Done() - return c.delChan, nil + return c.deliveryChannel, nil } -func (c *core) Acknowledge(ctx context.Context, id int) error { - - switch ctx.Err() { - case context.Canceled: - return ctx.Err() - case context.DeadlineExceeded: +func (c *core) Acknowledge(ctx context.Context, id int32) error { + if isCanceledContext(ctx) { return ctx.Err() } - c.Lock() - defer c.Unlock() - if c.isClosed == true { + + if !c.startWorking() { return ubroker.ErrClosed } + defer c.working.Done() - // check delivery done - if c.delFlag == false { - return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") + request := &acknowledgeRequest{ + id: id, + response: make(chan acknowledgeResponse, 1), } - // published - var indexID = -1 - for index, message := range c.mainQ { - if message.msg.ID == id { - indexID = index - break + select { + case <-ctx.Done(): + return ctx.Err() + case c.requests <- request: + select { + case response := <-request.response: + return response.err + case <-ctx.Done(): + return ctx.Err() } } +} - // acked befor - var ackIndex = -1 - for index, ids := range c.ackedMessageID { - if ids == id { - ackIndex = index - break - } +func (c *core) ReQueue(ctx context.Context, id int32) error { + if isCanceledContext(ctx) { + return ctx.Err() } - if indexID == -1 { - return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") + if !c.startWorking() { + return ubroker.ErrClosed } + defer c.working.Done() - if ackIndex != -1 { - return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") - } - // checked ttl time - if time.Now().Sub(c.mainQ[indexID].ttlTime) > c.ttl { - return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") - } else { - // acked - c.ackedMessageID = append(c.ackedMessageID, id) - return nil + request := &requeueRequest{ + id: id, + response: make(chan requeueResponse, 1), } - return nil + select { + case <-ctx.Done(): + return ctx.Err() + case c.requests <- request: + select { + case response := <-request.response: + return response.err + case <-ctx.Done(): + return ctx.Err() + } + } } -func (c *core) ReQueue(ctx context.Context, id int) error { +func (c *core) Publish(ctx context.Context, message *ubroker.Message) error { + if isCanceledContext(ctx) { + return ctx.Err() + } - //c.wg.Done() - //defer c.wg.Done() + if !c.startWorking() { + return ubroker.ErrClosed + } + defer c.working.Done() - switch ctx.Err() { - case context.Canceled: - return ctx.Err() - case context.DeadlineExceeded: - return ctx.Err() + request := &publishRequest{ + message: message, + response: make(chan publishResponse, 1), } - c.Lock() - defer c.Unlock() - if c.isClosed == true { + + select { + case <-ctx.Done(): + return ctx.Err() + case <-c.closed: return ubroker.ErrClosed + case c.requests <- request: + return nil } +} - //check delivery - if c.delFlag == false { - return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") +func (c *core) Close() error { + if !c.startClosing() { + return errors.New("can not close channel, closing in progress") } + c.working.Wait() + close(c.closed) + c.wg.Wait() + close(c.deliveryChannel) + + return nil +} - // published? - var indexID = -1 - for index, message := range c.mainQ { - if message.msg.ID == id { - indexID = index - break +func (c *core) startDelivery() { + defer c.wg.Done() + for { + select { + case <-c.closed: + return + + case request := <-c.requests: + if isAcknowledgeRequest(request) { + c.wg.Add(1) + req, _ := request.(*acknowledgeRequest) + req.response <- c.handleAcknowledge(req) + } else if isRequeueRequest(request) { + c.wg.Add(1) + req, _ := request.(*requeueRequest) + req.response <- c.handleRequeue(req) + } else if isPublishRequest(request) { + c.wg.Add(1) + req, _ := request.(*publishRequest) + req.response <- c.handlePublish(req) + } else { + panic(errors.New("UNKNOWN REQUEST")) + } + + case c.channel <- c.messages[0]: + if c.channel != nil { + c.pending[c.messages[0].Id] = c.messages[0].Message + c.wg.Add(1) + go c.snooze(c.messages[0].Id) + + c.messages = c.messages[1:] + if len(c.messages) == 0 { + c.channel = nil + c.messages = []*ubroker.Delivery{{}} + } + } } } +} + +func (c *core) startWorking() bool { + c.mutex.Lock() + defer c.mutex.Unlock() - if indexID == -1 { - return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") + select { + case <-c.closing: + return false + default: + c.working.Add(1) + return true } - // check ttl time - if time.Now().Sub(c.mainQ[indexID].ttlTime) > c.ttl { - c.doReQ(c.mainQ[indexID]) - c.mainQ = append(c.mainQ[:indexID], c.mainQ[indexID+1:]...) - return errors.Wrap(ubroker.ErrInvalidID, "id is invalid") - } else { - c.doReQ(c.mainQ[indexID]) - c.mainQ = append(c.mainQ[:indexID], c.mainQ[indexID+1:]...) - return nil +} +func (c *core) startClosing() bool { + c.mutex.Lock() + defer c.mutex.Unlock() + + select { + case <-c.closing: + return false + default: + close(c.closing) + return true } - return nil } -func (c *core) doReQ(msg1 messageType) error { - //c.wg.Add(1) - //defer c.wg.Done() - c.lastID = c.lastID + 1 - var newMsg ubroker.Delivery - newMsg.Message = msg1.msg.Message - newMsg.ID = c.lastID - var newnewmsg = messageType{} - newnewmsg.msg = newMsg - newnewmsg.ttlTime = time.Now() - - c.mainQ = append(c.mainQ, newnewmsg) - //send message to channel - c.delChan <- newMsg - return nil +func isCanceledContext(ctx context.Context) bool { + select { + case <-ctx.Done(): + return true + default: + return false + } } -func (c *core) Publish(ctx context.Context, message ubroker.Message) error { - //c.wg.Add(1) - //defer c.wg.Done() +func isAcknowledgeRequest(request interface{}) bool { + _, ok := request.(*acknowledgeRequest) + return ok +} - switch ctx.Err() { +func isRequeueRequest(request interface{}) bool { + _, ok := request.(*requeueRequest) + return ok +} - case context.Canceled: - return ctx.Err() - case context.DeadlineExceeded: - return ctx.Err() +func isPublishRequest(request interface{}) bool { + _, ok := request.(*publishRequest) + return ok +} + +func (c *core) handleAcknowledge(request *acknowledgeRequest) acknowledgeResponse { + defer c.wg.Done() + _, ok := c.pending[request.id] + if !ok { + return acknowledgeResponse{id: request.id, err: ubroker.ErrInvalidID} } - c.Lock() - defer c.Unlock() + delete(c.pending, request.id) + return acknowledgeResponse{id: request.id, err: nil} +} - if c.isClosed == true { - return ubroker.ErrClosed +func (c *core) handleRequeue(request *requeueRequest) requeueResponse { + defer c.wg.Done() + message, ok := c.pending[request.id] + if !ok { + return requeueResponse{id: request.id, err: ubroker.ErrInvalidID} } + delete(c.pending, request.id) + c.wg.Add(1) + c.handlePublish(&publishRequest{ + message: message, + response: make(chan publishResponse, 1), + }) + return requeueResponse{id: request.id, err: nil} +} - c.lastID = c.lastID + 1 - var newMsg ubroker.Delivery - newMsg.Message = message - newMsg.ID = c.lastID - //send message to channel - c.delChan <- newMsg +func (c *core) handlePublish(request *publishRequest) publishResponse { + defer c.wg.Done() - var newnewmsg = messageType{} - newnewmsg.msg = newMsg - newnewmsg.ttlTime = time.Now() - c.mainQ = append(c.mainQ, newnewmsg) - return nil -} + if c.channel == nil { + c.messages = []*ubroker.Delivery{} + c.channel = c.deliveryChannel + } -func (c *core) Close() error { - //c.wg.Wait() - if c.isClosed { - return nil + id := c.nextID + c.nextID++ + newDelivery := ubroker.Delivery{ + Id: id, + Message: request.message, } - c.Lock() - defer c.Unlock() - c.isClosed = true - close(c.delChan) - return nil + c.messages = append(c.messages, &newDelivery) + + return publishResponse{err: nil} +} + +func (c *core) snooze(id int32) { + defer c.wg.Done() + ticker := time.NewTicker(c.ttl) + defer ticker.Stop() + + select { + case <-c.closed: + return + + case <-ticker.C: + request := &requeueRequest{ + id: id, + response: make(chan requeueResponse, 1), + } + select { + case <-c.closed: + return + + case c.requests <- request: + } + } } diff --git a/internal/broker/core_test.go b/internal/broker/core_test.go index fe6bdd3..0c3780b 100644 --- a/internal/broker/core_test.go +++ b/internal/broker/core_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" - "github.com/mahtabfarrokh/ubroker/internal/broker" - "github.com/mahtabfarrokh/ubroker/pkg/ubroker" + "github.com/arcana261/ubroker/internal/broker" + "github.com/arcana261/ubroker/pkg/ubroker" "github.com/pkg/errors" "github.com/stretchr/testify/assert" @@ -30,9 +30,11 @@ func TestCoreBrokerTestSuite(t *testing.T) { func BenchmarkPublish(b *testing.B) { broker := broker.New(1 * time.Minute) s := assert.New(b) + b.ResetTimer() + for i := 0; i < b.N; i++ { - s.Nil(broker.Publish(context.Background(), ubroker.Message{})) + s.Nil(broker.Publish(context.Background(), &ubroker.Message{})) } } @@ -41,7 +43,7 @@ func BenchmarkDelivery(b *testing.B) { s := assert.New(b) for i := 0; i < b.N; i++ { - s.Nil(broker.Publish(context.Background(), ubroker.Message{})) + s.Nil(broker.Publish(context.Background(), &ubroker.Message{})) } delivery, err := broker.Delivery(context.Background()) @@ -59,12 +61,12 @@ func BenchmarkAcknowledge(b *testing.B) { s := assert.New(b) for i := 0; i < b.N; i++ { - s.Nil(broker.Publish(context.Background(), ubroker.Message{})) + s.Nil(broker.Publish(context.Background(), &ubroker.Message{})) } delivery, err := broker.Delivery(context.Background()) s.Nil(err) - deliveries := make([]ubroker.Delivery, 0, b.N) + deliveries := make([]*ubroker.Delivery, 0, b.N) for i := 0; i < b.N; i++ { deliveries = append(deliveries, <-delivery) @@ -73,7 +75,7 @@ func BenchmarkAcknowledge(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - s.Nil(broker.Acknowledge(context.Background(), deliveries[i].ID)) + s.Nil(broker.Acknowledge(context.Background(), deliveries[i].Id)) } } @@ -82,12 +84,12 @@ func BenchmarkReQueue(b *testing.B) { s := assert.New(b) for i := 0; i < b.N; i++ { - s.Nil(broker.Publish(context.Background(), ubroker.Message{})) + s.Nil(broker.Publish(context.Background(), &ubroker.Message{})) } delivery, err := broker.Delivery(context.Background()) s.Nil(err) - deliveries := make([]ubroker.Delivery, 0, b.N) + deliveries := make([]*ubroker.Delivery, 0, b.N) for i := 0; i < b.N; i++ { deliveries = append(deliveries, <-delivery) @@ -96,7 +98,7 @@ func BenchmarkReQueue(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - s.Nil(broker.ReQueue(context.Background(), deliveries[i].ID)) + s.Nil(broker.ReQueue(context.Background(), deliveries[i].Id)) } } @@ -121,7 +123,7 @@ func (s *CoreBrokerTestSuite) TestPublishedMessageShouldAppearInDeliveryOnce() { s.publish("hello1") delivery := s.getDelivery(context.Background()) msg := <-delivery - s.Equal("hello1", msg.Message.Body) + s.Equal("hello1", string(msg.Message.Body)) s.assertEmpty(delivery) } @@ -131,13 +133,13 @@ func (s *CoreBrokerTestSuite) TestPublishShouldPreserveOrder() { s.publish("hello2") s.publish("hello3") delivery := s.getDelivery(context.Background()) - messages := []ubroker.Delivery{<-delivery, <-delivery, <-delivery} - s.NotEqual(messages[0].ID, messages[1].ID) - s.NotEqual(messages[0].ID, messages[2].ID) - s.NotEqual(messages[1].ID, messages[2].ID) - s.Equal("hello1", messages[0].Message.Body) - s.Equal("hello2", messages[1].Message.Body) - s.Equal("hello3", messages[2].Message.Body) + messages := []*ubroker.Delivery{<-delivery, <-delivery, <-delivery} + s.NotEqual(messages[0].Id, messages[1].Id) + s.NotEqual(messages[0].Id, messages[2].Id) + s.NotEqual(messages[1].Id, messages[2].Id) + s.Equal("hello1", string(messages[0].Message.Body)) + s.Equal("hello2", string(messages[1].Message.Body)) + s.Equal("hello3", string(messages[2].Message.Body)) } func (s *CoreBrokerTestSuite) TestDeliveriesShouldBeUnique() { @@ -151,7 +153,8 @@ func (s *CoreBrokerTestSuite) TestMessageShouldNotBeAcknowledgeablePreemptively( s.prepareTest(1 * time.Second) s.publish("hello") for id := -100; id <= 100; id++ { - s.assertErrorEquals(ubroker.ErrInvalidID, s.broker.Acknowledge(context.Background(), id)) + s.assertErrorEquals(ubroker.ErrInvalidID, + s.broker.Acknowledge(context.Background(), int32(id))) } } @@ -159,8 +162,8 @@ func (s *CoreBrokerTestSuite) TestMessageShouldNotBeQueueablePreemptively() { s.prepareTest(1 * time.Second) s.publish("hello") for id := -100; id <= 100; id++ { - - s.assertErrorEquals(ubroker.ErrInvalidID, s.broker.ReQueue(context.Background(), id)) + s.assertErrorEquals(ubroker.ErrInvalidID, + s.broker.ReQueue(context.Background(), int32(id))) } } @@ -168,15 +171,15 @@ func (s *CoreBrokerTestSuite) TestDeliveryShouldBeAcknowledgeable() { s.prepareTest(1 * time.Second) s.publish("hello") msg := <-s.getDelivery(context.Background()) - s.Nil(s.broker.Acknowledge(context.Background(), msg.ID)) + s.Nil(s.broker.Acknowledge(context.Background(), msg.Id)) } func (s *CoreBrokerTestSuite) TestDeliveryShouldNotBeAcknowledgedTwice() { s.prepareTest(1 * time.Second) s.publish("hello") msg := <-s.getDelivery(context.Background()) - s.Nil(s.broker.Acknowledge(context.Background(), msg.ID)) - s.assertErrorEquals(ubroker.ErrInvalidID, s.broker.Acknowledge(context.Background(), msg.ID)) + s.Nil(s.broker.Acknowledge(context.Background(), msg.Id)) + s.assertErrorEquals(ubroker.ErrInvalidID, s.broker.Acknowledge(context.Background(), msg.Id)) } func (s *CoreBrokerTestSuite) TestMultipleDeliveriesShouldBeAcknowledgeableIndependently() { @@ -185,21 +188,18 @@ func (s *CoreBrokerTestSuite) TestMultipleDeliveriesShouldBeAcknowledgeableIndep s.publish("hello2") s.publish("hello3") delivery := s.getDelivery(context.Background()) - messages := []ubroker.Delivery{<-delivery, <-delivery, <-delivery} - s.Nil(s.broker.Acknowledge(context.Background(), messages[0].ID)) - s.Nil(s.broker.Acknowledge(context.Background(), messages[1].ID)) - s.Nil(s.broker.Acknowledge(context.Background(), messages[2].ID)) + messages := []*ubroker.Delivery{<-delivery, <-delivery, <-delivery} + s.Nil(s.broker.Acknowledge(context.Background(), messages[0].Id)) + s.Nil(s.broker.Acknowledge(context.Background(), messages[1].Id)) + s.Nil(s.broker.Acknowledge(context.Background(), messages[2].Id)) } func (s *CoreBrokerTestSuite) TestAcknowledgedMessageShouldNotAppearInDelivery() { - s.prepareTest(1 * time.Second) s.publish("hello") delivery := s.getDelivery(context.Background()) - msg := <-delivery - s.Nil(s.broker.Acknowledge(context.Background(), msg.ID)) - + s.Nil(s.broker.Acknowledge(context.Background(), msg.Id)) s.assertEmpty(delivery) } @@ -208,21 +208,18 @@ func (s *CoreBrokerTestSuite) TestDeliveryShouldBeReQueueable() { s.publish("hello") delivery := s.getDelivery(context.Background()) msg1 := <-delivery - s.Nil(s.broker.ReQueue(context.Background(), msg1.ID)) + s.Nil(s.broker.ReQueue(context.Background(), msg1.Id)) msg2 := <-delivery - s.NotEqual(msg1.ID, msg2.ID) + s.NotEqual(msg1.Id, msg2.Id) s.Equal(msg1.Message.Body, msg2.Message.Body) } func (s *CoreBrokerTestSuite) TestDeliveryShouldNotBeReQueueableTwice() { s.prepareTest(1 * time.Second) - s.publish("hello") - msg := <-s.getDelivery(context.Background()) - - s.Nil(s.broker.ReQueue(context.Background(), msg.ID)) - s.assertErrorEquals(ubroker.ErrInvalidID, s.broker.ReQueue(context.Background(), msg.ID)) + s.Nil(s.broker.ReQueue(context.Background(), msg.Id)) + s.assertErrorEquals(ubroker.ErrInvalidID, s.broker.ReQueue(context.Background(), msg.Id)) } func (s *CoreBrokerTestSuite) TestMultipleDeliveriesShouldBeReQueueableIndependently() { @@ -231,10 +228,10 @@ func (s *CoreBrokerTestSuite) TestMultipleDeliveriesShouldBeReQueueableIndepende s.publish("hello2") s.publish("hello3") delivery := s.getDelivery(context.Background()) - messages := []ubroker.Delivery{<-delivery, <-delivery, <-delivery} - s.Nil(s.broker.ReQueue(context.Background(), messages[0].ID)) - s.Nil(s.broker.ReQueue(context.Background(), messages[1].ID)) - s.Nil(s.broker.ReQueue(context.Background(), messages[2].ID)) + messages := []*ubroker.Delivery{<-delivery, <-delivery, <-delivery} + s.Nil(s.broker.ReQueue(context.Background(), messages[0].Id)) + s.Nil(s.broker.ReQueue(context.Background(), messages[1].Id)) + s.Nil(s.broker.ReQueue(context.Background(), messages[2].Id)) s.Equal(messages[0].Message.Body, (<-delivery).Message.Body) s.Equal(messages[1].Message.Body, (<-delivery).Message.Body) s.Equal(messages[2].Message.Body, (<-delivery).Message.Body) @@ -246,10 +243,10 @@ func (s *CoreBrokerTestSuite) TestReQueueCouldBreakOrder() { s.publish("hello2") s.publish("hello3") delivery := s.getDelivery(context.Background()) - messages := []ubroker.Delivery{<-delivery, <-delivery, <-delivery} - s.Nil(s.broker.ReQueue(context.Background(), messages[2].ID)) - s.Nil(s.broker.ReQueue(context.Background(), messages[1].ID)) - s.Nil(s.broker.ReQueue(context.Background(), messages[0].ID)) + messages := []*ubroker.Delivery{<-delivery, <-delivery, <-delivery} + s.Nil(s.broker.ReQueue(context.Background(), messages[2].Id)) + s.Nil(s.broker.ReQueue(context.Background(), messages[1].Id)) + s.Nil(s.broker.ReQueue(context.Background(), messages[0].Id)) s.Equal(messages[2].Message.Body, (<-delivery).Message.Body) s.Equal(messages[1].Message.Body, (<-delivery).Message.Body) s.Equal(messages[0].Message.Body, (<-delivery).Message.Body) @@ -261,32 +258,35 @@ func (s *CoreBrokerTestSuite) TestDeliveryShouldReQueueUponHalfSecondTTL() { delivery := s.getDelivery(context.Background()) msg1 := <-delivery time.Sleep(250 * time.Millisecond) - s.Nil(s.broker.Acknowledge(context.Background(), msg1.ID)) + s.Nil(s.broker.Acknowledge(context.Background(), msg1.Id)) s.publish("hello2") msg2 := <-delivery time.Sleep(750 * time.Millisecond) - s.assertErrorEquals(ubroker.ErrInvalidID, s.broker.Acknowledge(context.Background(), msg2.ID)) - s.assertErrorEquals(ubroker.ErrInvalidID, s.broker.ReQueue(context.Background(), msg2.ID)) + s.assertErrorEquals(ubroker.ErrInvalidID, s.broker.Acknowledge(context.Background(), msg2.Id)) + s.assertErrorEquals(ubroker.ErrInvalidID, s.broker.ReQueue(context.Background(), msg2.Id)) msg3 := <-delivery - s.NotEqual(msg1.ID, msg3.ID) - s.NotEqual(msg2.ID, msg3.ID) - s.Equal("hello2", msg3.Message.Body) - s.Nil(s.broker.Acknowledge(context.Background(), msg3.ID)) + s.NotEqual(msg1.Id, msg3.Id) + s.NotEqual(msg2.Id, msg3.Id) + s.Equal("hello2", string(msg3.Message.Body)) + s.Nil(s.broker.Acknowledge(context.Background(), msg3.Id)) } func (s *CoreBrokerTestSuite) TestPublishShouldFailOnClosedBroker() { s.prepareClosed() - s.assertErrorEquals(ubroker.ErrClosed, s.broker.Publish(context.Background(), ubroker.Message{})) + s.assertErrorEquals(ubroker.ErrClosed, + s.broker.Publish(context.Background(), &ubroker.Message{})) } func (s *CoreBrokerTestSuite) TestAcknowledgeShouldFailOnClosedBroker() { s.prepareClosed() - s.assertErrorEquals(ubroker.ErrClosed, s.broker.Acknowledge(context.Background(), 1)) + s.assertErrorEquals(ubroker.ErrClosed, + s.broker.Acknowledge(context.Background(), 1)) } func (s *CoreBrokerTestSuite) TestReQueueShouldFailOnClosedBroker() { s.prepareClosed() - s.assertErrorEquals(ubroker.ErrClosed, s.broker.ReQueue(context.Background(), 1)) + s.assertErrorEquals(ubroker.ErrClosed, + s.broker.ReQueue(context.Background(), 1)) } func (s *CoreBrokerTestSuite) TestDeliveryShouldFailOnClosedBroker() { @@ -297,14 +297,9 @@ func (s *CoreBrokerTestSuite) TestDeliveryShouldFailOnClosedBroker() { func (s *CoreBrokerTestSuite) TestCloseShouldCloseDeliveryChannel() { s.prepareTest(1 * time.Second) - fmt.Println(context.Background()) delivery := s.getDelivery(context.Background()) - fmt.Println(delivery) s.Nil(s.broker.Close()) _, ok := <-delivery - fmt.Println(s) - - fmt.Println(ok) s.False(ok) } @@ -334,7 +329,8 @@ func (s *CoreBrokerTestSuite) TestPublishShouldFailOnCanceledContext() { s.prepareTest(1 * time.Second) ctx, cancel := context.WithCancel(context.Background()) cancel() - s.assertErrorEquals(ctx.Err(), s.broker.Publish(ctx, ubroker.Message{})) + s.assertErrorEquals(ctx.Err(), + s.broker.Publish(ctx, &ubroker.Message{})) } func (s *CoreBrokerTestSuite) TestDataRace() { @@ -354,8 +350,8 @@ func (s *CoreBrokerTestSuite) TestDataRace() { return default: - err := s.broker.Publish(context.Background(), ubroker.Message{ - Body: fmt.Sprint(rand.Intn(1000)), + err := s.broker.Publish(context.Background(), &ubroker.Message{ + Body: []byte(fmt.Sprint(rand.Intn(1000))), }) if err == ubroker.ErrClosed { return @@ -384,8 +380,12 @@ func (s *CoreBrokerTestSuite) TestDataRace() { case <-ticker.C: return - case msg := <-delivery: - err = s.broker.Acknowledge(context.Background(), msg.ID) + case msg, ok := <-delivery: + if !ok { + return + } + + err = s.broker.Acknowledge(context.Background(), msg.Id) if err == ubroker.ErrClosed { return } @@ -412,8 +412,12 @@ func (s *CoreBrokerTestSuite) TestDataRace() { case <-ticker.C: return - case msg := <-delivery: - err = s.broker.ReQueue(context.Background(), msg.ID) + case msg, ok := <-delivery: + if !ok { + return + } + + err = s.broker.ReQueue(context.Background(), msg.Id) if err == ubroker.ErrClosed { return } @@ -437,8 +441,8 @@ func (s *CoreBrokerTestSuite) TestDataRace() { } func (s *CoreBrokerTestSuite) publish(body string) { - s.Nil(s.broker.Publish(context.Background(), ubroker.Message{ - Body: body, + s.Nil(s.broker.Publish(context.Background(), &ubroker.Message{ + Body: []byte(body), })) } @@ -450,7 +454,7 @@ func (s *CoreBrokerTestSuite) assertErrorEquals(expected error, actual error) { } } -func (s *CoreBrokerTestSuite) getDelivery(ctx context.Context) <-chan ubroker.Delivery { +func (s *CoreBrokerTestSuite) getDelivery(ctx context.Context) <-chan *ubroker.Delivery { result, err := s.broker.Delivery(ctx) if err != nil { s.FailNow(err.Error(), "could not obtain delivery") @@ -459,7 +463,7 @@ func (s *CoreBrokerTestSuite) getDelivery(ctx context.Context) <-chan ubroker.De return result } -func (s *CoreBrokerTestSuite) assertEmpty(delivery <-chan ubroker.Delivery) { +func (s *CoreBrokerTestSuite) assertEmpty(delivery <-chan *ubroker.Delivery) { ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() diff --git a/internal/server/grpc.go b/internal/server/grpc.go new file mode 100644 index 0000000..b0a0f79 --- /dev/null +++ b/internal/server/grpc.go @@ -0,0 +1,80 @@ +package server + +import ( + "context" + "github.com/arcana261/ubroker/pkg/ubroker" + "github.com/golang/protobuf/ptypes/empty" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "io" +) + +type grpcServicer struct { + broker ubroker.Broker +} + +func NewGRPC(broker ubroker.Broker) ubroker.BrokerServer { + return &grpcServicer{ + broker: broker, + } +} + +func (s *grpcServicer) Fetch(stream ubroker.Broker_FetchServer) error { + delivery, err := s.broker.Delivery(stream.Context()) + if err != nil { + return s.ReturnError(err) + } + + for { + _, err := stream.Recv() + if err == io.EOF { + return nil + } + if err != nil { + println("heeeeeeeeeeeeeeeeeeeeeer!") + return s.ReturnError(err) + } + delivered := <-delivery + if delivered == nil { + return status.Error(codes.Unavailable, "Unavailable") + } + err = stream.Send(delivered) + if err != nil { + return s.ReturnError(err) + } + } + //return status.Error(codes.OK, "OK") +} + +func (s *grpcServicer) Acknowledge(ctx context.Context, request *ubroker.AcknowledgeRequest) (*empty.Empty, error) { + err := s.broker.Acknowledge(ctx, request.Id) + if err != nil { + return &empty.Empty{}, s.ReturnError(err) + } + return &empty.Empty{}, status.Error(codes.OK, "OK") +} + +func (s *grpcServicer) ReQueue(ctx context.Context, request *ubroker.ReQueueRequest) (*empty.Empty, error) { + err := s.broker.ReQueue(ctx, request.Id) + if err != nil { + return &empty.Empty{}, s.ReturnError(err) + } + return &empty.Empty{}, status.Error(codes.OK, "OK") +} + +func (s *grpcServicer) Publish(ctx context.Context, request *ubroker.Message) (*empty.Empty, error) { + err := s.broker.Publish(ctx, request) + if err != nil { + return &empty.Empty{}, s.ReturnError(err) + } + return &empty.Empty{}, status.Error(codes.OK, "OK") +} +func (s *grpcServicer) ReturnError(err error) error { + if err == ubroker.ErrClosed { + return status.Error(codes.Unavailable, "Unavailable") + } + if err == ubroker.ErrInvalidID { + return status.Error(codes.InvalidArgument, "InvalidID") + } + return nil +} diff --git a/internal/server/grpc_test.go b/internal/server/grpc_test.go new file mode 100644 index 0000000..6c093c9 --- /dev/null +++ b/internal/server/grpc_test.go @@ -0,0 +1,382 @@ +package server_test + +import ( + "context" + "fmt" + "net" + "testing" + "time" + + "github.com/stretchr/testify/mock" + + "github.com/arcana261/ubroker/internal/server" + "github.com/arcana261/ubroker/pkg/ubroker" + "github.com/phayes/freeport" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/stretchr/testify/suite" +) + +type GRPCServerTestSuite struct { + suite.Suite +} + +func TestGRPCServerTestSuite(t *testing.T) { + suite.Run(t, new(GRPCServerTestSuite)) +} + +func (s *GRPCServerTestSuite) TestFetchShouldReturnUnavailableIfClosed() { + broker := &mockBroker{} + broker.On("Delivery", mock.Anything).Once().Return(nil, ubroker.ErrClosed) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + stream, err := client.Fetch(ctx, grpc.WaitForReady(true)) + s.Nil(err) + + s.Nil(stream.Send(&ubroker.FetchRequest{})) + + _, err = stream.Recv() + s.assertStatusCode(codes.Unavailable, err) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) TestFetchShouldReturnUnavailableIfDeliveryClosed() { + broker := &mockBroker{} + broker.On("Delivery", mock.Anything).Once().Return(s.makeChannel(), nil) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + stream, err := client.Fetch(ctx, grpc.WaitForReady(true)) + s.Nil(err) + + s.Nil(stream.Send(&ubroker.FetchRequest{})) + + _, err = stream.Recv() + s.assertStatusCode(codes.Unavailable, err) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) TestFetchShouldReturnOneItem() { + broker := &mockBroker{} + broker.On("Delivery", mock.Anything).Once().Return(s.makeChannel("hello"), nil) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + stream, err := client.Fetch(ctx, grpc.WaitForReady(true)) + s.Nil(err) + + s.Nil(stream.Send(&ubroker.FetchRequest{})) + + result, err := stream.Recv() + s.Nil(err) + + s.Equal("hello", string(result.Message.Body)) + + s.Nil(stream.CloseSend()) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) TestFetchShouldReturnTwoItems() { + broker := &mockBroker{} + broker.On("Delivery", mock.Anything).Once().Return(s.makeChannel("hello", "salam"), nil) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + stream, err := client.Fetch(ctx, grpc.WaitForReady(true)) + s.Nil(err) + + s.Nil(stream.Send(&ubroker.FetchRequest{})) + result, err := stream.Recv() + s.Nil(err) + s.Equal("hello", string(result.Message.Body)) + s.Nil(stream.Send(&ubroker.FetchRequest{})) + result, err = stream.Recv() + s.Nil(err) + s.Equal("salam", string(result.Message.Body)) + + s.Nil(stream.CloseSend()) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) TestFetchShouldNotStreamIfNotRequestedForFirstData() { + broker := &mockBroker{} + broker.On("Delivery", mock.Anything).Once().Return(s.makeChannel("salam"), nil) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + stream, err := client.Fetch(ctx, grpc.WaitForReady(true)) + s.Nil(err) + + dataReceived := make(chan struct{}) + go func() { + defer close(dataReceived) + + result, err := stream.Recv() + s.Nil(err) + s.Equal("salam", string(result.Message.Body)) + }() + + time.Sleep(100 * time.Millisecond) + select { + case <-dataReceived: + s.FailNow("Fetch should not delivery if not requested") + return + + default: + } + + s.Nil(stream.Send(&ubroker.FetchRequest{})) + + <-dataReceived + + s.Nil(stream.CloseSend()) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) TestFetchShouldNotStreamIfNotRequestedForMoreData() { + broker := &mockBroker{} + broker.On("Delivery", mock.Anything).Once().Return(s.makeChannel("hello", "salam"), nil) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + stream, err := client.Fetch(ctx, grpc.WaitForReady(true)) + s.Nil(err) + + s.Nil(stream.Send(&ubroker.FetchRequest{})) + result, err := stream.Recv() + s.Nil(err) + s.Equal("hello", string(result.Message.Body)) + + dataReceived := make(chan struct{}) + go func() { + defer close(dataReceived) + + result, err = stream.Recv() + s.Nil(err) + s.Equal("salam", string(result.Message.Body)) + }() + + time.Sleep(100 * time.Millisecond) + select { + case <-dataReceived: + s.FailNow("Fetch should not delivery if not requested") + return + + default: + } + + s.Nil(stream.Send(&ubroker.FetchRequest{})) + + <-dataReceived + + s.Nil(stream.CloseSend()) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) TestAcknowledgeShouldReturnUnavailableIfClosed() { + broker := &mockBroker{} + broker.On("Acknowledge", mock.Anything, int32(10)).Once().Return(ubroker.ErrClosed) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + _, err := client.Acknowledge(ctx, &ubroker.AcknowledgeRequest{ + Id: 10, + }, grpc.WaitForReady(true)) + s.assertStatusCode(codes.Unavailable, err) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) TestAcknowledgeShouldReturnUnavailableIfInvalidID() { + broker := &mockBroker{} + broker.On("Acknowledge", mock.Anything, int32(10)).Once().Return(ubroker.ErrInvalidID) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + _, err := client.Acknowledge(ctx, &ubroker.AcknowledgeRequest{ + Id: 10, + }, grpc.WaitForReady(true)) + s.assertStatusCode(codes.InvalidArgument, err) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) TestAcknowledgeShouldReturnOKOnSuccess() { + broker := &mockBroker{} + broker.On("Acknowledge", mock.Anything, int32(10)).Once().Return(nil) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + _, err := client.Acknowledge(ctx, &ubroker.AcknowledgeRequest{ + Id: 10, + }, grpc.WaitForReady(true)) + s.Nil(err) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) TestReQueueShouldReturnUnavailableIfClosed() { + broker := &mockBroker{} + broker.On("ReQueue", mock.Anything, int32(10)).Once().Return(ubroker.ErrClosed) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + _, err := client.ReQueue(ctx, &ubroker.ReQueueRequest{ + Id: 10, + }, grpc.WaitForReady(true)) + s.assertStatusCode(codes.Unavailable, err) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) TestReQueueShouldReturnUnavailableIfInvalidID() { + broker := &mockBroker{} + broker.On("ReQueue", mock.Anything, int32(10)).Once().Return(ubroker.ErrInvalidID) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + _, err := client.ReQueue(ctx, &ubroker.ReQueueRequest{ + Id: 10, + }, grpc.WaitForReady(true)) + s.assertStatusCode(codes.InvalidArgument, err) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) TestReQueueShouldReturnOKOnSuccess() { + broker := &mockBroker{} + broker.On("ReQueue", mock.Anything, int32(10)).Once().Return(nil) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + _, err := client.ReQueue(ctx, &ubroker.ReQueueRequest{ + Id: 10, + }, grpc.WaitForReady(true)) + s.Nil(err) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) TestPublishShouldReturnUnavailableIfClosed() { + broker := &mockBroker{} + broker.On("Publish", mock.Anything, mock.MatchedBy(func(msg *ubroker.Message) bool { + s.Equal("hello", string(msg.Body)) + return "hello" == string(msg.Body) + })).Once().Return(ubroker.ErrClosed) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + _, err := client.Publish(ctx, &ubroker.Message{ + Body: []byte("hello"), + }, grpc.WaitForReady(true)) + s.assertStatusCode(codes.Unavailable, err) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) TestPublishShouldReturnOKIfSuccessful() { + broker := &mockBroker{} + broker.On("Publish", mock.Anything, mock.MatchedBy(func(msg *ubroker.Message) bool { + s.Equal("hello", string(msg.Body)) + return "hello" == string(msg.Body) + })).Once().Return(nil) + + s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { + _, err := client.Publish(ctx, &ubroker.Message{ + Body: []byte("hello"), + }, grpc.WaitForReady(true)) + s.Nil(err) + + broker.AssertExpectations(s.T()) + }, broker) +} + +func (s *GRPCServerTestSuite) makeChannel(args ...string) <-chan *ubroker.Delivery { + result := make(chan *ubroker.Delivery, len(args)) + var id int32 + + for _, arg := range args { + result <- &ubroker.Delivery{ + Id: id, + Message: &ubroker.Message{ + Body: []byte(arg), + }, + } + + id = id + 1 + } + + close(result) + return result +} + +func (s *GRPCServerTestSuite) assertStatusCode(code codes.Code, err error) { + if code == codes.OK { + s.Nil(err) + return + } + + grpcStatus, ok := status.FromError(err) + s.True(ok) + + if grpcStatus != nil { + s.Equal(code, grpcStatus.Code()) + } +} + +func (s *GRPCServerTestSuite) runTest( + tester func(ctx context.Context, client ubroker.BrokerClient), + broker ubroker.Broker) { + + port, err := freeport.GetFreePort() + if err != nil { + s.FailNow(err.Error(), "failed to obtain free port") + } + + endpoint := fmt.Sprintf("127.0.0.1:%d", port) + servicer := server.NewGRPC(broker) + + grpcServer := grpc.NewServer() + ubroker.RegisterBrokerServer(grpcServer, servicer) + + listener, err := net.Listen("tcp", endpoint) + if err != nil { + s.FailNow(err.Error(), "failed to open listener") + } + + go func() { + grpcServer.Serve(listener) + }() + + dialCtx, dialCtxCancel := context.WithTimeout(context.Background(), 5*time.Second) + defer dialCtxCancel() + + conn, err := grpc.DialContext(dialCtx, endpoint, + grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + s.FailNow(err.Error(), "failed to dial to gRPC‌ server") + } + + client := ubroker.NewBrokerClient(conn) + + clientCtx, clientCtxCancel := context.WithTimeout(context.Background(), 10*time.Second) + defer clientCtxCancel() + + tester(clientCtx, client) + + err = conn.Close() + if err != nil { + s.FailNow(err.Error(), "failed to properly close gRPC‌ client connection") + } + + grpcServer.GracefulStop() +} diff --git a/internal/server/http.go b/internal/server/http.go index c4b75d9..1badf2c 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "regexp" "strconv" @@ -13,11 +12,12 @@ import ( "sync" "time" + "github.com/golang/protobuf/jsonpb" "github.com/gorilla/mux" "github.com/sirupsen/logrus" - "github.com/mahtabfarrokh/ubroker/pkg/ubroker" + "github.com/arcana261/ubroker/pkg/ubroker" "github.com/pkg/errors" ) @@ -37,7 +37,7 @@ func init() { type httpServer struct { broker ubroker.Broker router *mux.Router - delivery <-chan ubroker.Delivery + delivery <-chan *ubroker.Delivery endpoint string server *http.Server mutex sync.Mutex @@ -79,7 +79,7 @@ func (s *httpServer) Run() error { ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) defer cancel() - print(ctx) + s.delivery, err = s.broker.Delivery(ctx) if err != nil { return err @@ -126,14 +126,8 @@ func (s *httpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request } func (s *httpServer) handlePublish(writer http.ResponseWriter, request *http.Request) { - data, err := ioutil.ReadAll(request.Body) - if err != nil { - s.handleError(writer, request, err) - return - } - var msg ubroker.Message - err = json.Unmarshal(data, &msg) + err := jsonpb.Unmarshal(request.Body, &msg) if err != nil { s.handleError(writer, request, err) } @@ -141,7 +135,7 @@ func (s *httpServer) handlePublish(writer http.ResponseWriter, request *http.Req ctx, cancel := s.makeContext(request) defer cancel() - err = s.broker.Publish(ctx, msg) + err = s.broker.Publish(ctx, &msg) if err != nil { s.handleError(writer, request, err) } @@ -166,7 +160,7 @@ func (s *httpServer) handleAcknowledge(writer http.ResponseWriter, request *http ctx, cancel := s.makeContext(request) defer cancel() - err = s.broker.Acknowledge(ctx, id) + err = s.broker.Acknowledge(ctx, int32(id)) if err != nil { s.handleError(writer, request, err) return @@ -192,7 +186,7 @@ func (s *httpServer) handleReQueue(writer http.ResponseWriter, request *http.Req ctx, cancel := s.makeContext(request) defer cancel() - err = s.broker.ReQueue(ctx, id) + err = s.broker.ReQueue(ctx, int32(id)) if err != nil { s.handleError(writer, request, err) return diff --git a/internal/server/http_test.go b/internal/server/http_test.go index b7ce875..1141457 100644 --- a/internal/server/http_test.go +++ b/internal/server/http_test.go @@ -1,7 +1,6 @@ package server_test import ( - "context" "fmt" "io/ioutil" "net/http" @@ -15,35 +14,6 @@ import ( "github.com/stretchr/testify/suite" ) -type mockBroker struct { - mock.Mock -} - -func (m *mockBroker) Close() error { - args := m.Called() - return args.Error(0) -} - -func (m *mockBroker) Delivery(ctx context.Context) (<-chan ubroker.Delivery, error) { - args := m.Called(ctx) - return args.Get(0).(<-chan ubroker.Delivery), args.Error(1) -} - -func (m *mockBroker) Acknowledge(ctx context.Context, id int) error { - args := m.Called(ctx, id) - return args.Error(0) -} - -func (m *mockBroker) ReQueue(ctx context.Context, id int) error { - args := m.Called(ctx, id) - return args.Error(0) -} - -func (m *mockBroker) Publish(ctx context.Context, message ubroker.Message) error { - args := m.Called(ctx, message) - return args.Error(0) -} - type HTTPServerTestSuite struct { suite.Suite t *testing.T @@ -57,14 +27,14 @@ func TestHTTPServerTestSuite(t *testing.T) { func (s *HTTPServerTestSuite) prepareTest() { s.broker = new(mockBroker) - s.broker.On("Delivery", mock.Anything).Return(make(<-chan ubroker.Delivery, 0), nil) + s.broker.On("Delivery", mock.Anything).Return(make(<-chan *ubroker.Delivery, 0), nil) s.server = server.NewHTTP(s.broker, ":0") s.server.Run() } func (s *HTTPServerTestSuite) TestEmptyFetch() { s.broker = new(mockBroker) - s.broker.On("Delivery", mock.Anything).Return(make(<-chan ubroker.Delivery, 0), nil) + s.broker.On("Delivery", mock.Anything).Return(make(<-chan *ubroker.Delivery, 0), nil) s.server = server.NewHTTP(s.broker, ":0") s.server.Run() @@ -73,63 +43,63 @@ func (s *HTTPServerTestSuite) TestEmptyFetch() { func (s *HTTPServerTestSuite) TestPublish() { s.prepareTest() - s.broker.On("Publish", mock.Anything, mock.Anything).Return(nil) - s.httpPublish(`{"body": "hello"}`) - s.broker.AssertCalled( - s.t, - "Publish", - mock.Anything, - ubroker.Message{Body: "hello"}, - ) + + s.broker.On("Publish", mock.Anything, mock.MatchedBy(func(msg *ubroker.Message) bool { + s.Equal("hello", string(msg.Body)) + return "hello" == string(msg.Body) + })).Return(nil) + + s.httpPublish(`{"body":"aGVsbG8="}`) + s.broker.AssertExpectations(s.T()) } func (s *HTTPServerTestSuite) TestFailedReQueue() { s.prepareTest() - s.broker.On("ReQueue", mock.Anything, 123).Return(ubroker.ErrInvalidID) + s.broker.On("ReQueue", mock.Anything, int32(123)).Return(ubroker.ErrInvalidID) body := `{"error": "id is invalid"}` s.httpReQueue(123, 400, &body) s.broker.AssertCalled( s.t, "ReQueue", mock.Anything, - 123, + int32(123), ) } func (s *HTTPServerTestSuite) TestReQueue() { s.prepareTest() - s.broker.On("ReQueue", mock.Anything, 123).Return(nil) + s.broker.On("ReQueue", mock.Anything, int32(123)).Return(nil) s.httpReQueue(123, 200, nil) s.broker.AssertCalled( s.t, "ReQueue", mock.Anything, - 123, + int32(123), ) } func (s *HTTPServerTestSuite) TestFailedAcknowledge() { s.prepareTest() - s.broker.On("Acknowledge", mock.Anything, 123).Return(ubroker.ErrInvalidID) + s.broker.On("Acknowledge", mock.Anything, int32(123)).Return(ubroker.ErrInvalidID) body := `{"error": "id is invalid"}` s.httpAcknowledge(123, 400, &body) s.broker.AssertCalled( s.t, "Acknowledge", mock.Anything, - 123, + int32(123), ) } func (s *HTTPServerTestSuite) TestAcknowledge() { s.prepareTest() - s.broker.On("Acknowledge", mock.Anything, 123).Return(nil) + s.broker.On("Acknowledge", mock.Anything, int32(123)).Return(nil) s.httpAcknowledge(123, 200, nil) s.broker.AssertCalled( s.t, "Acknowledge", mock.Anything, - 123, + int32(123), ) } diff --git a/internal/server/moc_broker_test.go b/internal/server/moc_broker_test.go new file mode 100644 index 0000000..d3a0fa4 --- /dev/null +++ b/internal/server/moc_broker_test.go @@ -0,0 +1,44 @@ +package server_test + +import ( + "context" + + "github.com/arcana261/ubroker/pkg/ubroker" + "github.com/stretchr/testify/mock" +) + +type mockBroker struct { + mock.Mock +} + +func (m *mockBroker) Close() error { + args := m.Called() + return args.Error(0) +} + +func (m *mockBroker) Delivery(ctx context.Context) (<-chan *ubroker.Delivery, error) { + args := m.Called(ctx) + + var res0 <-chan *ubroker.Delivery + + if args.Get(0) != nil { + res0 = args.Get(0).(<-chan *ubroker.Delivery) + } + + return res0, args.Error(1) +} + +func (m *mockBroker) Acknowledge(ctx context.Context, id int32) error { + args := m.Called(ctx, id) + return args.Error(0) +} + +func (m *mockBroker) ReQueue(ctx context.Context, id int32) error { + args := m.Called(ctx, id) + return args.Error(0) +} + +func (m *mockBroker) Publish(ctx context.Context, message *ubroker.Message) error { + args := m.Called(ctx, message) + return args.Error(0) +} diff --git a/pkg/ubroker/ubroker.go b/pkg/ubroker/ubroker.go index 931872f..cbc090d 100644 --- a/pkg/ubroker/ubroker.go +++ b/pkg/ubroker/ubroker.go @@ -33,7 +33,7 @@ type Broker interface { // returned // 3. If broker is closed, `ErrClosed` is returned // 4. should be thread-safe - Delivery(ctx context.Context) (<-chan Delivery, error) + Delivery(ctx context.Context) (<-chan *Delivery, error) // Acknowledge is called by clients to declare that // specified message id has been successfuly processed @@ -48,7 +48,7 @@ type Broker interface { // returned // 5. If broker is closed, `ErrClosed` is returned // 6. should be thread-safe - Acknowledge(ctx context.Context, id int) error + Acknowledge(ctx context.Context, id int32) error // ReQueue is called by clients to declare that // specified message id should be put back in @@ -62,7 +62,7 @@ type Broker interface { // returned // 5. If broker is closed, `ErrClosed` is returned // 6. should be thread-safe - ReQueue(ctx context.Context, id int) error + ReQueue(ctx context.Context, id int32) error // Publish is used to enqueue a new message to broker // We demand following: @@ -71,7 +71,7 @@ type Broker interface { // returned // 2. If broker is closed, `ErrClosed` is returned // 3. should be thread-safe - Publish(ctx context.Context, message Message) error + Publish(ctx context.Context, message *Message) error } // HTTPServer defines an HTTP‌ API‌ server provider @@ -81,18 +81,3 @@ type HTTPServer interface { Run() error } - -// Message encapsulates a queued message -type Message struct { - // Body is an abitrary client-defined string - Body string `json:"body"` -} - -// Delivery encapsulates a message fetched -// from queue -type Delivery struct { - // Message is the message fetched from queue - Message Message `json:"message"` - // ID - ID int `json:"id"` -} diff --git a/pkg/ubroker/ubroker.pb.go b/pkg/ubroker/ubroker.pb.go new file mode 100644 index 0000000..06a9c69 --- /dev/null +++ b/pkg/ubroker/ubroker.pb.go @@ -0,0 +1,506 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: api/ubroker.proto + +package ubroker + +import ( + context "context" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + empty "github.com/golang/protobuf/ptypes/empty" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type Message struct { + Body []byte `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Message) Reset() { *m = Message{} } +func (m *Message) String() string { return proto.CompactTextString(m) } +func (*Message) ProtoMessage() {} +func (*Message) Descriptor() ([]byte, []int) { + return fileDescriptor_c9a5bc08b618fc5f, []int{0} +} + +func (m *Message) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Message.Unmarshal(m, b) +} +func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Message.Marshal(b, m, deterministic) +} +func (m *Message) XXX_Merge(src proto.Message) { + xxx_messageInfo_Message.Merge(m, src) +} +func (m *Message) XXX_Size() int { + return xxx_messageInfo_Message.Size(m) +} +func (m *Message) XXX_DiscardUnknown() { + xxx_messageInfo_Message.DiscardUnknown(m) +} + +var xxx_messageInfo_Message proto.InternalMessageInfo + +func (m *Message) GetBody() []byte { + if m != nil { + return m.Body + } + return nil +} + +type Delivery struct { + Message *Message `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + Id int32 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Delivery) Reset() { *m = Delivery{} } +func (m *Delivery) String() string { return proto.CompactTextString(m) } +func (*Delivery) ProtoMessage() {} +func (*Delivery) Descriptor() ([]byte, []int) { + return fileDescriptor_c9a5bc08b618fc5f, []int{1} +} + +func (m *Delivery) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Delivery.Unmarshal(m, b) +} +func (m *Delivery) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Delivery.Marshal(b, m, deterministic) +} +func (m *Delivery) XXX_Merge(src proto.Message) { + xxx_messageInfo_Delivery.Merge(m, src) +} +func (m *Delivery) XXX_Size() int { + return xxx_messageInfo_Delivery.Size(m) +} +func (m *Delivery) XXX_DiscardUnknown() { + xxx_messageInfo_Delivery.DiscardUnknown(m) +} + +var xxx_messageInfo_Delivery proto.InternalMessageInfo + +func (m *Delivery) GetMessage() *Message { + if m != nil { + return m.Message + } + return nil +} + +func (m *Delivery) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +type FetchRequest struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FetchRequest) Reset() { *m = FetchRequest{} } +func (m *FetchRequest) String() string { return proto.CompactTextString(m) } +func (*FetchRequest) ProtoMessage() {} +func (*FetchRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9a5bc08b618fc5f, []int{2} +} + +func (m *FetchRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FetchRequest.Unmarshal(m, b) +} +func (m *FetchRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FetchRequest.Marshal(b, m, deterministic) +} +func (m *FetchRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_FetchRequest.Merge(m, src) +} +func (m *FetchRequest) XXX_Size() int { + return xxx_messageInfo_FetchRequest.Size(m) +} +func (m *FetchRequest) XXX_DiscardUnknown() { + xxx_messageInfo_FetchRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_FetchRequest proto.InternalMessageInfo + +type AcknowledgeRequest struct { + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AcknowledgeRequest) Reset() { *m = AcknowledgeRequest{} } +func (m *AcknowledgeRequest) String() string { return proto.CompactTextString(m) } +func (*AcknowledgeRequest) ProtoMessage() {} +func (*AcknowledgeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9a5bc08b618fc5f, []int{3} +} + +func (m *AcknowledgeRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AcknowledgeRequest.Unmarshal(m, b) +} +func (m *AcknowledgeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AcknowledgeRequest.Marshal(b, m, deterministic) +} +func (m *AcknowledgeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AcknowledgeRequest.Merge(m, src) +} +func (m *AcknowledgeRequest) XXX_Size() int { + return xxx_messageInfo_AcknowledgeRequest.Size(m) +} +func (m *AcknowledgeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AcknowledgeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AcknowledgeRequest proto.InternalMessageInfo + +func (m *AcknowledgeRequest) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +type ReQueueRequest struct { + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReQueueRequest) Reset() { *m = ReQueueRequest{} } +func (m *ReQueueRequest) String() string { return proto.CompactTextString(m) } +func (*ReQueueRequest) ProtoMessage() {} +func (*ReQueueRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c9a5bc08b618fc5f, []int{4} +} + +func (m *ReQueueRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ReQueueRequest.Unmarshal(m, b) +} +func (m *ReQueueRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ReQueueRequest.Marshal(b, m, deterministic) +} +func (m *ReQueueRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReQueueRequest.Merge(m, src) +} +func (m *ReQueueRequest) XXX_Size() int { + return xxx_messageInfo_ReQueueRequest.Size(m) +} +func (m *ReQueueRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ReQueueRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ReQueueRequest proto.InternalMessageInfo + +func (m *ReQueueRequest) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +func init() { + proto.RegisterType((*Message)(nil), "ubroker.Message") + proto.RegisterType((*Delivery)(nil), "ubroker.Delivery") + proto.RegisterType((*FetchRequest)(nil), "ubroker.FetchRequest") + proto.RegisterType((*AcknowledgeRequest)(nil), "ubroker.AcknowledgeRequest") + proto.RegisterType((*ReQueueRequest)(nil), "ubroker.ReQueueRequest") +} + +func init() { proto.RegisterFile("api/ubroker.proto", fileDescriptor_c9a5bc08b618fc5f) } + +var fileDescriptor_c9a5bc08b618fc5f = []byte{ + // 309 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x4b, 0xc3, 0x30, + 0x14, 0xc6, 0xc9, 0x70, 0xab, 0xbc, 0x8d, 0xe1, 0x1e, 0xa8, 0x63, 0x43, 0x18, 0xc5, 0xc3, 0x10, + 0x49, 0x65, 0xc3, 0x8b, 0x37, 0x87, 0xee, 0x26, 0x68, 0x8f, 0xde, 0x9a, 0xf5, 0xad, 0x2d, 0x6d, + 0x4d, 0x6d, 0x13, 0x65, 0xff, 0xb7, 0x7f, 0x80, 0x98, 0x35, 0x65, 0x32, 0x7a, 0x7b, 0x79, 0x7c, + 0x5f, 0xbe, 0x5f, 0x3e, 0x02, 0xa3, 0xa0, 0x48, 0x3c, 0x2d, 0x4a, 0x99, 0x52, 0xc9, 0x8b, 0x52, + 0x2a, 0x89, 0x4e, 0x7d, 0x9c, 0x4c, 0x23, 0x29, 0xa3, 0x8c, 0x3c, 0xb3, 0x16, 0x7a, 0xeb, 0x51, + 0x5e, 0xa8, 0xdd, 0x5e, 0xe5, 0x5e, 0x81, 0xf3, 0x42, 0x55, 0x15, 0x44, 0x84, 0x08, 0x27, 0x42, + 0x86, 0xbb, 0x31, 0x9b, 0xb1, 0xf9, 0xc0, 0x37, 0xb3, 0xbb, 0x86, 0xd3, 0x27, 0xca, 0x92, 0x2f, + 0x2a, 0x77, 0x78, 0x03, 0x4e, 0xbe, 0x97, 0x1a, 0x49, 0x7f, 0x71, 0xc6, 0x6d, 0x62, 0x7d, 0x85, + 0x6f, 0x05, 0x38, 0x84, 0x4e, 0x12, 0x8e, 0x3b, 0x33, 0x36, 0xef, 0xfa, 0x9d, 0x24, 0x74, 0x87, + 0x30, 0x58, 0x93, 0xda, 0xc4, 0x3e, 0x7d, 0x6a, 0xaa, 0x94, 0x7b, 0x0d, 0xf8, 0xb8, 0x49, 0x3f, + 0xe4, 0x77, 0x46, 0x61, 0x44, 0xf5, 0xb6, 0x76, 0xb1, 0xc6, 0x35, 0x83, 0xa1, 0x4f, 0x6f, 0x9a, + 0x74, 0x9b, 0x62, 0xf1, 0xc3, 0xa0, 0xb7, 0x32, 0x0c, 0x78, 0x0f, 0x5d, 0x13, 0x81, 0xe7, 0x0d, + 0xd6, 0x61, 0xe4, 0x64, 0xd4, 0xac, 0xed, 0x8b, 0xe6, 0xec, 0x8e, 0xe1, 0x0a, 0xfa, 0x07, 0x24, + 0x38, 0x6d, 0x54, 0xc7, 0x7c, 0x93, 0x0b, 0xbe, 0xaf, 0x92, 0xdb, 0x2a, 0xf9, 0xf3, 0x5f, 0x95, + 0xf8, 0x00, 0x4e, 0xcd, 0x89, 0x97, 0x8d, 0xff, 0x3f, 0x79, 0xab, 0x77, 0x09, 0xce, 0xab, 0x16, + 0x59, 0x52, 0xc5, 0x78, 0xd4, 0x67, 0x9b, 0x69, 0xc5, 0xdf, 0x6f, 0xa3, 0x44, 0xc5, 0x5a, 0xf0, + 0x8d, 0xcc, 0xbd, 0x3c, 0x88, 0x55, 0x20, 0xb6, 0x41, 0x59, 0xca, 0x34, 0xb6, 0xbf, 0xc0, 0x2b, + 0xd2, 0xc8, 0xce, 0xa2, 0x67, 0xfc, 0xcb, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe5, 0x8b, 0xcc, + 0x81, 0x27, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// BrokerClient is the client API for Broker service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type BrokerClient interface { + // Fetch should return a single Delivery per FetchRequest. + // Should return: + // Unavailable: If broker has been closed + Fetch(ctx context.Context, opts ...grpc.CallOption) (Broker_FetchClient, error) + // Acknowledge a message + // Should return: + // OK: on success + // Unavailable: If broker has been closed + // InvalidArgument: If requested ID is invalid + Acknowledge(ctx context.Context, in *AcknowledgeRequest, opts ...grpc.CallOption) (*empty.Empty, error) + // ReQueue a message + // OK: on success + // Unavailable: If broker has been closed + // InvalidArgument: If requested ID is invalid + ReQueue(ctx context.Context, in *ReQueueRequest, opts ...grpc.CallOption) (*empty.Empty, error) + // Publish message to Queue + // OK: on success + // Unavailable: If broker has been closed + Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*empty.Empty, error) +} + +type brokerClient struct { + cc *grpc.ClientConn +} + +func NewBrokerClient(cc *grpc.ClientConn) BrokerClient { + return &brokerClient{cc} +} + +func (c *brokerClient) Fetch(ctx context.Context, opts ...grpc.CallOption) (Broker_FetchClient, error) { + stream, err := c.cc.NewStream(ctx, &_Broker_serviceDesc.Streams[0], "/ubroker.Broker/Fetch", opts...) + if err != nil { + return nil, err + } + x := &brokerFetchClient{stream} + return x, nil +} + +type Broker_FetchClient interface { + Send(*FetchRequest) error + Recv() (*Delivery, error) + grpc.ClientStream +} + +type brokerFetchClient struct { + grpc.ClientStream +} + +func (x *brokerFetchClient) Send(m *FetchRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *brokerFetchClient) Recv() (*Delivery, error) { + m := new(Delivery) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *brokerClient) Acknowledge(ctx context.Context, in *AcknowledgeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + out := new(empty.Empty) + err := c.cc.Invoke(ctx, "/ubroker.Broker/Acknowledge", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *brokerClient) ReQueue(ctx context.Context, in *ReQueueRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + out := new(empty.Empty) + err := c.cc.Invoke(ctx, "/ubroker.Broker/ReQueue", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *brokerClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*empty.Empty, error) { + out := new(empty.Empty) + err := c.cc.Invoke(ctx, "/ubroker.Broker/Publish", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// BrokerServer is the server API for Broker service. +type BrokerServer interface { + // Fetch should return a single Delivery per FetchRequest. + // Should return: + // Unavailable: If broker has been closed + Fetch(Broker_FetchServer) error + // Acknowledge a message + // Should return: + // OK: on success + // Unavailable: If broker has been closed + // InvalidArgument: If requested ID is invalid + Acknowledge(context.Context, *AcknowledgeRequest) (*empty.Empty, error) + // ReQueue a message + // OK: on success + // Unavailable: If broker has been closed + // InvalidArgument: If requested ID is invalid + ReQueue(context.Context, *ReQueueRequest) (*empty.Empty, error) + // Publish message to Queue + // OK: on success + // Unavailable: If broker has been closed + Publish(context.Context, *Message) (*empty.Empty, error) +} + +// UnimplementedBrokerServer can be embedded to have forward compatible implementations. +type UnimplementedBrokerServer struct { +} + +func (*UnimplementedBrokerServer) Fetch(srv Broker_FetchServer) error { + return status.Errorf(codes.Unimplemented, "method Fetch not implemented") +} +func (*UnimplementedBrokerServer) Acknowledge(ctx context.Context, req *AcknowledgeRequest) (*empty.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Acknowledge not implemented") +} +func (*UnimplementedBrokerServer) ReQueue(ctx context.Context, req *ReQueueRequest) (*empty.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReQueue not implemented") +} +func (*UnimplementedBrokerServer) Publish(ctx context.Context, req *Message) (*empty.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Publish not implemented") +} + +func RegisterBrokerServer(s *grpc.Server, srv BrokerServer) { + s.RegisterService(&_Broker_serviceDesc, srv) +} + +func _Broker_Fetch_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(BrokerServer).Fetch(&brokerFetchServer{stream}) +} + +type Broker_FetchServer interface { + Send(*Delivery) error + Recv() (*FetchRequest, error) + grpc.ServerStream +} + +type brokerFetchServer struct { + grpc.ServerStream +} + +func (x *brokerFetchServer) Send(m *Delivery) error { + return x.ServerStream.SendMsg(m) +} + +func (x *brokerFetchServer) Recv() (*FetchRequest, error) { + m := new(FetchRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _Broker_Acknowledge_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AcknowledgeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BrokerServer).Acknowledge(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ubroker.Broker/Acknowledge", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BrokerServer).Acknowledge(ctx, req.(*AcknowledgeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Broker_ReQueue_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReQueueRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BrokerServer).ReQueue(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ubroker.Broker/ReQueue", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BrokerServer).ReQueue(ctx, req.(*ReQueueRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Broker_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Message) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BrokerServer).Publish(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ubroker.Broker/Publish", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BrokerServer).Publish(ctx, req.(*Message)) + } + return interceptor(ctx, in, info, handler) +} + +var _Broker_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ubroker.Broker", + HandlerType: (*BrokerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Acknowledge", + Handler: _Broker_Acknowledge_Handler, + }, + { + MethodName: "ReQueue", + Handler: _Broker_ReQueue_Handler, + }, + { + MethodName: "Publish", + Handler: _Broker_Publish_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Fetch", + Handler: _Broker_Fetch_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "api/ubroker.proto", +} From f9d76b1007c7b76630cf1eeea265c05a535a1cfd Mon Sep 17 00:00:00 2001 From: mahtabfarrokh Date: Thu, 23 May 2019 18:18:51 +0430 Subject: [PATCH 11/14] GRPC Done. --- internal/server/grpc_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/server/grpc_test.go b/internal/server/grpc_test.go index 6c093c9..ec28f00 100644 --- a/internal/server/grpc_test.go +++ b/internal/server/grpc_test.go @@ -94,6 +94,7 @@ func (s *GRPCServerTestSuite) TestFetchShouldReturnTwoItems() { result, err := stream.Recv() s.Nil(err) s.Equal("hello", string(result.Message.Body)) + s.Nil(stream.Send(&ubroker.FetchRequest{})) result, err = stream.Recv() s.Nil(err) From 69c7cbaa3fe9fe81cdaa6d3b17008e9cf650501d Mon Sep 17 00:00:00 2001 From: mahtab farrokh Date: Thu, 23 May 2019 18:35:36 +0430 Subject: [PATCH 12/14] Delete ubroker.pb.go --- pkg/ubroker/ubroker.pb.go | 506 -------------------------------------- 1 file changed, 506 deletions(-) delete mode 100644 pkg/ubroker/ubroker.pb.go diff --git a/pkg/ubroker/ubroker.pb.go b/pkg/ubroker/ubroker.pb.go deleted file mode 100644 index 06a9c69..0000000 --- a/pkg/ubroker/ubroker.pb.go +++ /dev/null @@ -1,506 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: api/ubroker.proto - -package ubroker - -import ( - context "context" - fmt "fmt" - proto "github.com/golang/protobuf/proto" - empty "github.com/golang/protobuf/ptypes/empty" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - math "math" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type Message struct { - Body []byte `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Message) Reset() { *m = Message{} } -func (m *Message) String() string { return proto.CompactTextString(m) } -func (*Message) ProtoMessage() {} -func (*Message) Descriptor() ([]byte, []int) { - return fileDescriptor_c9a5bc08b618fc5f, []int{0} -} - -func (m *Message) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Message.Unmarshal(m, b) -} -func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Message.Marshal(b, m, deterministic) -} -func (m *Message) XXX_Merge(src proto.Message) { - xxx_messageInfo_Message.Merge(m, src) -} -func (m *Message) XXX_Size() int { - return xxx_messageInfo_Message.Size(m) -} -func (m *Message) XXX_DiscardUnknown() { - xxx_messageInfo_Message.DiscardUnknown(m) -} - -var xxx_messageInfo_Message proto.InternalMessageInfo - -func (m *Message) GetBody() []byte { - if m != nil { - return m.Body - } - return nil -} - -type Delivery struct { - Message *Message `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` - Id int32 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Delivery) Reset() { *m = Delivery{} } -func (m *Delivery) String() string { return proto.CompactTextString(m) } -func (*Delivery) ProtoMessage() {} -func (*Delivery) Descriptor() ([]byte, []int) { - return fileDescriptor_c9a5bc08b618fc5f, []int{1} -} - -func (m *Delivery) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Delivery.Unmarshal(m, b) -} -func (m *Delivery) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Delivery.Marshal(b, m, deterministic) -} -func (m *Delivery) XXX_Merge(src proto.Message) { - xxx_messageInfo_Delivery.Merge(m, src) -} -func (m *Delivery) XXX_Size() int { - return xxx_messageInfo_Delivery.Size(m) -} -func (m *Delivery) XXX_DiscardUnknown() { - xxx_messageInfo_Delivery.DiscardUnknown(m) -} - -var xxx_messageInfo_Delivery proto.InternalMessageInfo - -func (m *Delivery) GetMessage() *Message { - if m != nil { - return m.Message - } - return nil -} - -func (m *Delivery) GetId() int32 { - if m != nil { - return m.Id - } - return 0 -} - -type FetchRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FetchRequest) Reset() { *m = FetchRequest{} } -func (m *FetchRequest) String() string { return proto.CompactTextString(m) } -func (*FetchRequest) ProtoMessage() {} -func (*FetchRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_c9a5bc08b618fc5f, []int{2} -} - -func (m *FetchRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FetchRequest.Unmarshal(m, b) -} -func (m *FetchRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FetchRequest.Marshal(b, m, deterministic) -} -func (m *FetchRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_FetchRequest.Merge(m, src) -} -func (m *FetchRequest) XXX_Size() int { - return xxx_messageInfo_FetchRequest.Size(m) -} -func (m *FetchRequest) XXX_DiscardUnknown() { - xxx_messageInfo_FetchRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_FetchRequest proto.InternalMessageInfo - -type AcknowledgeRequest struct { - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *AcknowledgeRequest) Reset() { *m = AcknowledgeRequest{} } -func (m *AcknowledgeRequest) String() string { return proto.CompactTextString(m) } -func (*AcknowledgeRequest) ProtoMessage() {} -func (*AcknowledgeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_c9a5bc08b618fc5f, []int{3} -} - -func (m *AcknowledgeRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_AcknowledgeRequest.Unmarshal(m, b) -} -func (m *AcknowledgeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_AcknowledgeRequest.Marshal(b, m, deterministic) -} -func (m *AcknowledgeRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_AcknowledgeRequest.Merge(m, src) -} -func (m *AcknowledgeRequest) XXX_Size() int { - return xxx_messageInfo_AcknowledgeRequest.Size(m) -} -func (m *AcknowledgeRequest) XXX_DiscardUnknown() { - xxx_messageInfo_AcknowledgeRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_AcknowledgeRequest proto.InternalMessageInfo - -func (m *AcknowledgeRequest) GetId() int32 { - if m != nil { - return m.Id - } - return 0 -} - -type ReQueueRequest struct { - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ReQueueRequest) Reset() { *m = ReQueueRequest{} } -func (m *ReQueueRequest) String() string { return proto.CompactTextString(m) } -func (*ReQueueRequest) ProtoMessage() {} -func (*ReQueueRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_c9a5bc08b618fc5f, []int{4} -} - -func (m *ReQueueRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ReQueueRequest.Unmarshal(m, b) -} -func (m *ReQueueRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ReQueueRequest.Marshal(b, m, deterministic) -} -func (m *ReQueueRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ReQueueRequest.Merge(m, src) -} -func (m *ReQueueRequest) XXX_Size() int { - return xxx_messageInfo_ReQueueRequest.Size(m) -} -func (m *ReQueueRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ReQueueRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_ReQueueRequest proto.InternalMessageInfo - -func (m *ReQueueRequest) GetId() int32 { - if m != nil { - return m.Id - } - return 0 -} - -func init() { - proto.RegisterType((*Message)(nil), "ubroker.Message") - proto.RegisterType((*Delivery)(nil), "ubroker.Delivery") - proto.RegisterType((*FetchRequest)(nil), "ubroker.FetchRequest") - proto.RegisterType((*AcknowledgeRequest)(nil), "ubroker.AcknowledgeRequest") - proto.RegisterType((*ReQueueRequest)(nil), "ubroker.ReQueueRequest") -} - -func init() { proto.RegisterFile("api/ubroker.proto", fileDescriptor_c9a5bc08b618fc5f) } - -var fileDescriptor_c9a5bc08b618fc5f = []byte{ - // 309 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x4b, 0xc3, 0x30, - 0x14, 0xc6, 0xc9, 0x70, 0xab, 0xbc, 0x8d, 0xe1, 0x1e, 0xa8, 0x63, 0x43, 0x18, 0xc5, 0xc3, 0x10, - 0x49, 0x65, 0xc3, 0x8b, 0x37, 0x87, 0xee, 0x26, 0x68, 0x8f, 0xde, 0x9a, 0xf5, 0xad, 0x2d, 0x6d, - 0x4d, 0x6d, 0x13, 0x65, 0xff, 0xb7, 0x7f, 0x80, 0x98, 0x35, 0x65, 0x32, 0x7a, 0x7b, 0x79, 0x7c, - 0x5f, 0xbe, 0x5f, 0x3e, 0x02, 0xa3, 0xa0, 0x48, 0x3c, 0x2d, 0x4a, 0x99, 0x52, 0xc9, 0x8b, 0x52, - 0x2a, 0x89, 0x4e, 0x7d, 0x9c, 0x4c, 0x23, 0x29, 0xa3, 0x8c, 0x3c, 0xb3, 0x16, 0x7a, 0xeb, 0x51, - 0x5e, 0xa8, 0xdd, 0x5e, 0xe5, 0x5e, 0x81, 0xf3, 0x42, 0x55, 0x15, 0x44, 0x84, 0x08, 0x27, 0x42, - 0x86, 0xbb, 0x31, 0x9b, 0xb1, 0xf9, 0xc0, 0x37, 0xb3, 0xbb, 0x86, 0xd3, 0x27, 0xca, 0x92, 0x2f, - 0x2a, 0x77, 0x78, 0x03, 0x4e, 0xbe, 0x97, 0x1a, 0x49, 0x7f, 0x71, 0xc6, 0x6d, 0x62, 0x7d, 0x85, - 0x6f, 0x05, 0x38, 0x84, 0x4e, 0x12, 0x8e, 0x3b, 0x33, 0x36, 0xef, 0xfa, 0x9d, 0x24, 0x74, 0x87, - 0x30, 0x58, 0x93, 0xda, 0xc4, 0x3e, 0x7d, 0x6a, 0xaa, 0x94, 0x7b, 0x0d, 0xf8, 0xb8, 0x49, 0x3f, - 0xe4, 0x77, 0x46, 0x61, 0x44, 0xf5, 0xb6, 0x76, 0xb1, 0xc6, 0x35, 0x83, 0xa1, 0x4f, 0x6f, 0x9a, - 0x74, 0x9b, 0x62, 0xf1, 0xc3, 0xa0, 0xb7, 0x32, 0x0c, 0x78, 0x0f, 0x5d, 0x13, 0x81, 0xe7, 0x0d, - 0xd6, 0x61, 0xe4, 0x64, 0xd4, 0xac, 0xed, 0x8b, 0xe6, 0xec, 0x8e, 0xe1, 0x0a, 0xfa, 0x07, 0x24, - 0x38, 0x6d, 0x54, 0xc7, 0x7c, 0x93, 0x0b, 0xbe, 0xaf, 0x92, 0xdb, 0x2a, 0xf9, 0xf3, 0x5f, 0x95, - 0xf8, 0x00, 0x4e, 0xcd, 0x89, 0x97, 0x8d, 0xff, 0x3f, 0x79, 0xab, 0x77, 0x09, 0xce, 0xab, 0x16, - 0x59, 0x52, 0xc5, 0x78, 0xd4, 0x67, 0x9b, 0x69, 0xc5, 0xdf, 0x6f, 0xa3, 0x44, 0xc5, 0x5a, 0xf0, - 0x8d, 0xcc, 0xbd, 0x3c, 0x88, 0x55, 0x20, 0xb6, 0x41, 0x59, 0xca, 0x34, 0xb6, 0xbf, 0xc0, 0x2b, - 0xd2, 0xc8, 0xce, 0xa2, 0x67, 0xfc, 0xcb, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe5, 0x8b, 0xcc, - 0x81, 0x27, 0x02, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// BrokerClient is the client API for Broker service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type BrokerClient interface { - // Fetch should return a single Delivery per FetchRequest. - // Should return: - // Unavailable: If broker has been closed - Fetch(ctx context.Context, opts ...grpc.CallOption) (Broker_FetchClient, error) - // Acknowledge a message - // Should return: - // OK: on success - // Unavailable: If broker has been closed - // InvalidArgument: If requested ID is invalid - Acknowledge(ctx context.Context, in *AcknowledgeRequest, opts ...grpc.CallOption) (*empty.Empty, error) - // ReQueue a message - // OK: on success - // Unavailable: If broker has been closed - // InvalidArgument: If requested ID is invalid - ReQueue(ctx context.Context, in *ReQueueRequest, opts ...grpc.CallOption) (*empty.Empty, error) - // Publish message to Queue - // OK: on success - // Unavailable: If broker has been closed - Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*empty.Empty, error) -} - -type brokerClient struct { - cc *grpc.ClientConn -} - -func NewBrokerClient(cc *grpc.ClientConn) BrokerClient { - return &brokerClient{cc} -} - -func (c *brokerClient) Fetch(ctx context.Context, opts ...grpc.CallOption) (Broker_FetchClient, error) { - stream, err := c.cc.NewStream(ctx, &_Broker_serviceDesc.Streams[0], "/ubroker.Broker/Fetch", opts...) - if err != nil { - return nil, err - } - x := &brokerFetchClient{stream} - return x, nil -} - -type Broker_FetchClient interface { - Send(*FetchRequest) error - Recv() (*Delivery, error) - grpc.ClientStream -} - -type brokerFetchClient struct { - grpc.ClientStream -} - -func (x *brokerFetchClient) Send(m *FetchRequest) error { - return x.ClientStream.SendMsg(m) -} - -func (x *brokerFetchClient) Recv() (*Delivery, error) { - m := new(Delivery) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *brokerClient) Acknowledge(ctx context.Context, in *AcknowledgeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { - out := new(empty.Empty) - err := c.cc.Invoke(ctx, "/ubroker.Broker/Acknowledge", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *brokerClient) ReQueue(ctx context.Context, in *ReQueueRequest, opts ...grpc.CallOption) (*empty.Empty, error) { - out := new(empty.Empty) - err := c.cc.Invoke(ctx, "/ubroker.Broker/ReQueue", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *brokerClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*empty.Empty, error) { - out := new(empty.Empty) - err := c.cc.Invoke(ctx, "/ubroker.Broker/Publish", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// BrokerServer is the server API for Broker service. -type BrokerServer interface { - // Fetch should return a single Delivery per FetchRequest. - // Should return: - // Unavailable: If broker has been closed - Fetch(Broker_FetchServer) error - // Acknowledge a message - // Should return: - // OK: on success - // Unavailable: If broker has been closed - // InvalidArgument: If requested ID is invalid - Acknowledge(context.Context, *AcknowledgeRequest) (*empty.Empty, error) - // ReQueue a message - // OK: on success - // Unavailable: If broker has been closed - // InvalidArgument: If requested ID is invalid - ReQueue(context.Context, *ReQueueRequest) (*empty.Empty, error) - // Publish message to Queue - // OK: on success - // Unavailable: If broker has been closed - Publish(context.Context, *Message) (*empty.Empty, error) -} - -// UnimplementedBrokerServer can be embedded to have forward compatible implementations. -type UnimplementedBrokerServer struct { -} - -func (*UnimplementedBrokerServer) Fetch(srv Broker_FetchServer) error { - return status.Errorf(codes.Unimplemented, "method Fetch not implemented") -} -func (*UnimplementedBrokerServer) Acknowledge(ctx context.Context, req *AcknowledgeRequest) (*empty.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Acknowledge not implemented") -} -func (*UnimplementedBrokerServer) ReQueue(ctx context.Context, req *ReQueueRequest) (*empty.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method ReQueue not implemented") -} -func (*UnimplementedBrokerServer) Publish(ctx context.Context, req *Message) (*empty.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Publish not implemented") -} - -func RegisterBrokerServer(s *grpc.Server, srv BrokerServer) { - s.RegisterService(&_Broker_serviceDesc, srv) -} - -func _Broker_Fetch_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(BrokerServer).Fetch(&brokerFetchServer{stream}) -} - -type Broker_FetchServer interface { - Send(*Delivery) error - Recv() (*FetchRequest, error) - grpc.ServerStream -} - -type brokerFetchServer struct { - grpc.ServerStream -} - -func (x *brokerFetchServer) Send(m *Delivery) error { - return x.ServerStream.SendMsg(m) -} - -func (x *brokerFetchServer) Recv() (*FetchRequest, error) { - m := new(FetchRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func _Broker_Acknowledge_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AcknowledgeRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(BrokerServer).Acknowledge(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/ubroker.Broker/Acknowledge", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BrokerServer).Acknowledge(ctx, req.(*AcknowledgeRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Broker_ReQueue_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReQueueRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(BrokerServer).ReQueue(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/ubroker.Broker/ReQueue", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BrokerServer).ReQueue(ctx, req.(*ReQueueRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Broker_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Message) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(BrokerServer).Publish(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/ubroker.Broker/Publish", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BrokerServer).Publish(ctx, req.(*Message)) - } - return interceptor(ctx, in, info, handler) -} - -var _Broker_serviceDesc = grpc.ServiceDesc{ - ServiceName: "ubroker.Broker", - HandlerType: (*BrokerServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Acknowledge", - Handler: _Broker_Acknowledge_Handler, - }, - { - MethodName: "ReQueue", - Handler: _Broker_ReQueue_Handler, - }, - { - MethodName: "Publish", - Handler: _Broker_Publish_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "Fetch", - Handler: _Broker_Fetch_Handler, - ServerStreams: true, - ClientStreams: true, - }, - }, - Metadata: "api/ubroker.proto", -} From e1164bf571f4b53c1744752e820719a47c7fc20b Mon Sep 17 00:00:00 2001 From: mahtabfarrokh Date: Fri, 24 May 2019 11:03:52 +0430 Subject: [PATCH 13/14] back to hw1 on master --- api/ubroker.proto | 51 ---- internal/broker/core.go | 436 +++++++++++------------------ internal/server/grpc.go | 80 ------ internal/server/grpc_test.go | 383 ------------------------- internal/server/moc_broker_test.go | 44 --- pkg/ubroker/errors.go | 2 +- pkg/ubroker/ubroker.go | 24 +- 7 files changed, 180 insertions(+), 840 deletions(-) delete mode 100644 api/ubroker.proto delete mode 100644 internal/server/grpc.go delete mode 100644 internal/server/grpc_test.go delete mode 100644 internal/server/moc_broker_test.go diff --git a/api/ubroker.proto b/api/ubroker.proto deleted file mode 100644 index 7b2c23c..0000000 --- a/api/ubroker.proto +++ /dev/null @@ -1,51 +0,0 @@ -syntax = "proto3"; - -package ubroker; -option go_package = "github.com/arcana261/ubroker/pkg/ubroker"; - -import "google/protobuf/empty.proto"; - -service Broker { - // Fetch should return a single Delivery per FetchRequest. - // Should return: - // Unavailable: If broker has been closed - rpc Fetch(stream FetchRequest) returns (stream Delivery); - - // Acknowledge a message - // Should return: - // OK: on success - // Unavailable: If broker has been closed - // InvalidArgument: If requested ID is invalid - rpc Acknowledge(AcknowledgeRequest) returns (google.protobuf.Empty); - - // ReQueue a message - // OK: on success - // Unavailable: If broker has been closed - // InvalidArgument: If requested ID is invalid - rpc ReQueue(ReQueueRequest) returns (google.protobuf.Empty); - - // Publish message to Queue - // OK: on success - // Unavailable: If broker has been closed - rpc Publish(Message) returns (google.protobuf.Empty); -} - -message Message { - bytes body = 1; -} - -message Delivery { - Message message = 1; - int32 id = 2; -} - -message FetchRequest { -} - -message AcknowledgeRequest { - int32 id = 1; -} - -message ReQueueRequest { - int32 id = 1; -} \ No newline at end of file diff --git a/internal/broker/core.go b/internal/broker/core.go index c040e9b..0a50afa 100644 --- a/internal/broker/core.go +++ b/internal/broker/core.go @@ -5,9 +5,8 @@ import ( "sync" "time" - "github.com/pkg/errors" - "github.com/arcana261/ubroker/pkg/ubroker" + "github.com/pkg/errors" ) // New creates a new instance of ubroker.Broker @@ -15,328 +14,211 @@ import ( // we requeue an unacknowledged/unrequeued message // automatically. func New(ttl time.Duration) ubroker.Broker { - broker := &core{ + temp := &core{ + closed: false, + brokerChan: make(chan ubroker.Delivery, 1000), + closedChan: make(chan bool, 5000), + publishedQueue: []item{}, + receivedId: []int{}, + receivedAck: []int{}, + receivedRequeue: []int{}, + lastIdValue: -1, + deliveryStarted: false, + wg: sync.WaitGroup{}, ttl: ttl, - requests: make(chan interface{}), - deliveryChannel: make(chan *ubroker.Delivery), - closed: make(chan bool, 1), - closing: make(chan bool, 1), - pending: make(map[int32]*ubroker.Message), - messages: []*ubroker.Delivery{{}}, } - broker.wg.Add(1) - go broker.startDelivery() - - return broker -} - -type core struct { - nextID int32 - ttl time.Duration - - mutex sync.Mutex - working sync.WaitGroup - wg sync.WaitGroup - - requests chan interface{} - deliveryChannel chan *ubroker.Delivery - closed chan bool - closing chan bool - pending map[int32]*ubroker.Message - messages []*ubroker.Delivery - channel chan *ubroker.Delivery -} - -type acknowledgeRequest struct { - id int32 - response chan acknowledgeResponse -} - -type acknowledgeResponse struct { - id int32 - err error -} - -type requeueRequest struct { - id int32 - response chan requeueResponse -} - -type requeueResponse struct { - id int32 - err error + return temp } -type publishRequest struct { - message *ubroker.Message - response chan publishResponse +type item struct { + Message ubroker.Message + ID int + receivedAckChannel chan int } - -type publishResponse struct { - err error +type core struct { + closed bool + brokerChan chan ubroker.Delivery + closedChan chan bool + publishedQueue []item + receivedId []int + lastIdValue int + receivedAck []int + receivedRequeue []int + wg sync.WaitGroup + mut sync.Mutex + deliveryStarted bool + ttl time.Duration +} + +func contextProblem(ctx context.Context) bool { + if ctx.Err() == context.Canceled { + return true + } + if ctx.Err() == context.DeadlineExceeded { + return true + } + return false } +func (c *core) Delivery(ctx context.Context) (<-chan ubroker.Delivery, error) { -func (c *core) Delivery(ctx context.Context) (<-chan *ubroker.Delivery, error) { - if isCanceledContext(ctx) { + if contextProblem(ctx) { return nil, ctx.Err() } - - if !c.startWorking() { + if c.closed { return nil, ubroker.ErrClosed } - defer c.working.Done() - - return c.deliveryChannel, nil + c.mut.Lock() + c.deliveryStarted = true + c.mut.Unlock() + return c.brokerChan, nil + //return nil, errors.Wrap(ubroker.ErrUnimplemented, "method Delivery is not implemented") } -func (c *core) Acknowledge(ctx context.Context, id int32) error { - if isCanceledContext(ctx) { - return ctx.Err() - } - - if !c.startWorking() { +func (c *core) Acknowledge(ctx context.Context, id int) error { + c.mut.Lock() + if c.closed { + c.mut.Unlock() return ubroker.ErrClosed } - defer c.working.Done() - - request := &acknowledgeRequest{ - id: id, - response: make(chan acknowledgeResponse, 1), + if contextProblem(ctx) { + c.mut.Unlock() + return ctx.Err() } + temp := false - select { - case <-ctx.Done(): - return ctx.Err() - case c.requests <- request: - select { - case response := <-request.response: - return response.err - case <-ctx.Done(): - return ctx.Err() + if c.deliveryStarted { + temp = true + } + for _, element := range c.receivedAck { + if element == id { + temp = false } } -} -func (c *core) ReQueue(ctx context.Context, id int32) error { - if isCanceledContext(ctx) { - return ctx.Err() + if !temp { + c.mut.Unlock() + return errors.Wrap(ubroker.ErrInvalidID, "invalid Id") } + if c.closed { + c.mut.Unlock() - if !c.startWorking() { return ubroker.ErrClosed } - defer c.working.Done() - - request := &requeueRequest{ - id: id, - response: make(chan requeueResponse, 1), - } - - select { - case <-ctx.Done(): - return ctx.Err() - case c.requests <- request: - select { - case response := <-request.response: - return response.err - case <-ctx.Done(): - return ctx.Err() + c.receivedAck = append(c.receivedAck, id) + for i, element := range c.publishedQueue { + if element.ID == id { + c.publishedQueue[i].receivedAckChannel <- id + c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) + break } } + c.mut.Unlock() + + //c.wg.Done() + return nil } +func (c *core) DoingReQueue(ctx context.Context, id int) { + for i, element := range c.publishedQueue { + if element.ID == id { + c.receivedRequeue = append(c.receivedRequeue, id) + c.receivedAck = append(c.receivedAck, id) + c.lastIdValue += 1 + c.receivedId = append(c.receivedId, c.lastIdValue) + v := ubroker.Delivery{Message: element.Message, ID: c.lastIdValue} + v2 := item{Message: element.Message, ID: c.lastIdValue, receivedAckChannel: make(chan int, 10)} + //fmt.Println(len(c.publishedQueue), id, i) + c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) + c.publishedQueue = append(c.publishedQueue, v2) + c.brokerChan <- v + c.mut.Unlock() + go c.HandelingTTL(ctx, v2) + break + } -func (c *core) Publish(ctx context.Context, message *ubroker.Message) error { - if isCanceledContext(ctx) { - return ctx.Err() } - if !c.startWorking() { - return ubroker.ErrClosed +} +func (c *core) HandelingTTL(ctx context.Context, element item) { + select { + case <-time.After(c.ttl): + c.mut.Lock() + c.DoingReQueue(ctx, element.ID) + return + case <-element.receivedAckChannel: + return + case <-c.closedChan: + return } - defer c.working.Done() - - request := &publishRequest{ - message: message, - response: make(chan publishResponse, 1), +} +func (c *core) ReQueue(ctx context.Context, id int) error { + c.mut.Lock() + if c.closed { + c.mut.Unlock() + return ubroker.ErrClosed } - - select { - case <-ctx.Done(): + if contextProblem(ctx) { + c.mut.Unlock() return ctx.Err() - case <-c.closed: - return ubroker.ErrClosed - case c.requests <- request: - return nil } -} - -func (c *core) Close() error { - if !c.startClosing() { - return errors.New("can not close channel, closing in progress") + temp := false + if c.deliveryStarted { + temp = true } - c.working.Wait() - close(c.closed) - c.wg.Wait() - close(c.deliveryChannel) - - return nil -} - -func (c *core) startDelivery() { - defer c.wg.Done() - for { - select { - case <-c.closed: - return - - case request := <-c.requests: - if isAcknowledgeRequest(request) { - c.wg.Add(1) - req, _ := request.(*acknowledgeRequest) - req.response <- c.handleAcknowledge(req) - } else if isRequeueRequest(request) { - c.wg.Add(1) - req, _ := request.(*requeueRequest) - req.response <- c.handleRequeue(req) - } else if isPublishRequest(request) { - c.wg.Add(1) - req, _ := request.(*publishRequest) - req.response <- c.handlePublish(req) - } else { - panic(errors.New("UNKNOWN REQUEST")) - } - - case c.channel <- c.messages[0]: - if c.channel != nil { - c.pending[c.messages[0].Id] = c.messages[0].Message - c.wg.Add(1) - go c.snooze(c.messages[0].Id) - - c.messages = c.messages[1:] - if len(c.messages) == 0 { - c.channel = nil - c.messages = []*ubroker.Delivery{{}} - } - } + for _, element := range c.receivedRequeue { + if element == id { + temp = false } } -} - -func (c *core) startWorking() bool { - c.mutex.Lock() - defer c.mutex.Unlock() - - select { - case <-c.closing: - return false - default: - c.working.Add(1) - return true - } -} - -func (c *core) startClosing() bool { - c.mutex.Lock() - defer c.mutex.Unlock() - - select { - case <-c.closing: - return false - default: - close(c.closing) - return true + for _, element := range c.receivedAck { + if element == id { + temp = false + } } -} + if !temp { + c.mut.Unlock() -func isCanceledContext(ctx context.Context) bool { - select { - case <-ctx.Done(): - return true - default: - return false + return errors.Wrap(ubroker.ErrInvalidID, "invalid Id") } + c.DoingReQueue(ctx, id) + return nil } +func (c *core) DoingPublish(ctx context.Context, message ubroker.Message) { + c.lastIdValue += 1 + c.receivedId = append(c.receivedId, c.lastIdValue) + v := ubroker.Delivery{Message: message, ID: c.lastIdValue} + v2 := item{Message: message, ID: c.lastIdValue, receivedAckChannel: make(chan int, 10)} + c.publishedQueue = append(c.publishedQueue, v2) + c.brokerChan <- v + c.mut.Unlock() -func isAcknowledgeRequest(request interface{}) bool { - _, ok := request.(*acknowledgeRequest) - return ok -} - -func isRequeueRequest(request interface{}) bool { - _, ok := request.(*requeueRequest) - return ok -} - -func isPublishRequest(request interface{}) bool { - _, ok := request.(*publishRequest) - return ok + c.HandelingTTL(ctx, v2) + //defer c.wg.Done() } - -func (c *core) handleAcknowledge(request *acknowledgeRequest) acknowledgeResponse { - defer c.wg.Done() - _, ok := c.pending[request.id] - if !ok { - return acknowledgeResponse{id: request.id, err: ubroker.ErrInvalidID} +func (c *core) Publish(ctx context.Context, message ubroker.Message) error { + if contextProblem(ctx) { + return ctx.Err() } - delete(c.pending, request.id) - return acknowledgeResponse{id: request.id, err: nil} -} - -func (c *core) handleRequeue(request *requeueRequest) requeueResponse { - defer c.wg.Done() - message, ok := c.pending[request.id] - if !ok { - return requeueResponse{id: request.id, err: ubroker.ErrInvalidID} + c.mut.Lock() + if c.closed { + c.mut.Unlock() + return ubroker.ErrClosed } - delete(c.pending, request.id) - c.wg.Add(1) - c.handlePublish(&publishRequest{ - message: message, - response: make(chan publishResponse, 1), - }) - return requeueResponse{id: request.id, err: nil} + go c.DoingPublish(ctx, message) + return nil } -func (c *core) handlePublish(request *publishRequest) publishResponse { - defer c.wg.Done() - - if c.channel == nil { - c.messages = []*ubroker.Delivery{} - c.channel = c.deliveryChannel +func (c *core) Close() error { + if c.closed { + return nil } - - id := c.nextID - c.nextID++ - newDelivery := ubroker.Delivery{ - Id: id, - Message: request.message, + //c.wg.Wait() + for i := 0; i < 4000; i++ { + c.closedChan <- true } - - c.messages = append(c.messages, &newDelivery) - - return publishResponse{err: nil} -} - -func (c *core) snooze(id int32) { - defer c.wg.Done() - ticker := time.NewTicker(c.ttl) - defer ticker.Stop() - - select { - case <-c.closed: - return - - case <-ticker.C: - request := &requeueRequest{ - id: id, - response: make(chan requeueResponse, 1), - } - select { - case <-c.closed: - return - - case c.requests <- request: - } - } -} + c.mut.Lock() + close(c.brokerChan) + c.closed = true + c.mut.Unlock() + return nil +} \ No newline at end of file diff --git a/internal/server/grpc.go b/internal/server/grpc.go deleted file mode 100644 index b0a0f79..0000000 --- a/internal/server/grpc.go +++ /dev/null @@ -1,80 +0,0 @@ -package server - -import ( - "context" - "github.com/arcana261/ubroker/pkg/ubroker" - "github.com/golang/protobuf/ptypes/empty" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "io" -) - -type grpcServicer struct { - broker ubroker.Broker -} - -func NewGRPC(broker ubroker.Broker) ubroker.BrokerServer { - return &grpcServicer{ - broker: broker, - } -} - -func (s *grpcServicer) Fetch(stream ubroker.Broker_FetchServer) error { - delivery, err := s.broker.Delivery(stream.Context()) - if err != nil { - return s.ReturnError(err) - } - - for { - _, err := stream.Recv() - if err == io.EOF { - return nil - } - if err != nil { - println("heeeeeeeeeeeeeeeeeeeeeer!") - return s.ReturnError(err) - } - delivered := <-delivery - if delivered == nil { - return status.Error(codes.Unavailable, "Unavailable") - } - err = stream.Send(delivered) - if err != nil { - return s.ReturnError(err) - } - } - //return status.Error(codes.OK, "OK") -} - -func (s *grpcServicer) Acknowledge(ctx context.Context, request *ubroker.AcknowledgeRequest) (*empty.Empty, error) { - err := s.broker.Acknowledge(ctx, request.Id) - if err != nil { - return &empty.Empty{}, s.ReturnError(err) - } - return &empty.Empty{}, status.Error(codes.OK, "OK") -} - -func (s *grpcServicer) ReQueue(ctx context.Context, request *ubroker.ReQueueRequest) (*empty.Empty, error) { - err := s.broker.ReQueue(ctx, request.Id) - if err != nil { - return &empty.Empty{}, s.ReturnError(err) - } - return &empty.Empty{}, status.Error(codes.OK, "OK") -} - -func (s *grpcServicer) Publish(ctx context.Context, request *ubroker.Message) (*empty.Empty, error) { - err := s.broker.Publish(ctx, request) - if err != nil { - return &empty.Empty{}, s.ReturnError(err) - } - return &empty.Empty{}, status.Error(codes.OK, "OK") -} -func (s *grpcServicer) ReturnError(err error) error { - if err == ubroker.ErrClosed { - return status.Error(codes.Unavailable, "Unavailable") - } - if err == ubroker.ErrInvalidID { - return status.Error(codes.InvalidArgument, "InvalidID") - } - return nil -} diff --git a/internal/server/grpc_test.go b/internal/server/grpc_test.go deleted file mode 100644 index ec28f00..0000000 --- a/internal/server/grpc_test.go +++ /dev/null @@ -1,383 +0,0 @@ -package server_test - -import ( - "context" - "fmt" - "net" - "testing" - "time" - - "github.com/stretchr/testify/mock" - - "github.com/arcana261/ubroker/internal/server" - "github.com/arcana261/ubroker/pkg/ubroker" - "github.com/phayes/freeport" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "github.com/stretchr/testify/suite" -) - -type GRPCServerTestSuite struct { - suite.Suite -} - -func TestGRPCServerTestSuite(t *testing.T) { - suite.Run(t, new(GRPCServerTestSuite)) -} - -func (s *GRPCServerTestSuite) TestFetchShouldReturnUnavailableIfClosed() { - broker := &mockBroker{} - broker.On("Delivery", mock.Anything).Once().Return(nil, ubroker.ErrClosed) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - stream, err := client.Fetch(ctx, grpc.WaitForReady(true)) - s.Nil(err) - - s.Nil(stream.Send(&ubroker.FetchRequest{})) - - _, err = stream.Recv() - s.assertStatusCode(codes.Unavailable, err) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) TestFetchShouldReturnUnavailableIfDeliveryClosed() { - broker := &mockBroker{} - broker.On("Delivery", mock.Anything).Once().Return(s.makeChannel(), nil) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - stream, err := client.Fetch(ctx, grpc.WaitForReady(true)) - s.Nil(err) - - s.Nil(stream.Send(&ubroker.FetchRequest{})) - - _, err = stream.Recv() - s.assertStatusCode(codes.Unavailable, err) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) TestFetchShouldReturnOneItem() { - broker := &mockBroker{} - broker.On("Delivery", mock.Anything).Once().Return(s.makeChannel("hello"), nil) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - stream, err := client.Fetch(ctx, grpc.WaitForReady(true)) - s.Nil(err) - - s.Nil(stream.Send(&ubroker.FetchRequest{})) - - result, err := stream.Recv() - s.Nil(err) - - s.Equal("hello", string(result.Message.Body)) - - s.Nil(stream.CloseSend()) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) TestFetchShouldReturnTwoItems() { - broker := &mockBroker{} - broker.On("Delivery", mock.Anything).Once().Return(s.makeChannel("hello", "salam"), nil) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - stream, err := client.Fetch(ctx, grpc.WaitForReady(true)) - s.Nil(err) - - s.Nil(stream.Send(&ubroker.FetchRequest{})) - result, err := stream.Recv() - s.Nil(err) - s.Equal("hello", string(result.Message.Body)) - - s.Nil(stream.Send(&ubroker.FetchRequest{})) - result, err = stream.Recv() - s.Nil(err) - s.Equal("salam", string(result.Message.Body)) - - s.Nil(stream.CloseSend()) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) TestFetchShouldNotStreamIfNotRequestedForFirstData() { - broker := &mockBroker{} - broker.On("Delivery", mock.Anything).Once().Return(s.makeChannel("salam"), nil) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - stream, err := client.Fetch(ctx, grpc.WaitForReady(true)) - s.Nil(err) - - dataReceived := make(chan struct{}) - go func() { - defer close(dataReceived) - - result, err := stream.Recv() - s.Nil(err) - s.Equal("salam", string(result.Message.Body)) - }() - - time.Sleep(100 * time.Millisecond) - select { - case <-dataReceived: - s.FailNow("Fetch should not delivery if not requested") - return - - default: - } - - s.Nil(stream.Send(&ubroker.FetchRequest{})) - - <-dataReceived - - s.Nil(stream.CloseSend()) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) TestFetchShouldNotStreamIfNotRequestedForMoreData() { - broker := &mockBroker{} - broker.On("Delivery", mock.Anything).Once().Return(s.makeChannel("hello", "salam"), nil) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - stream, err := client.Fetch(ctx, grpc.WaitForReady(true)) - s.Nil(err) - - s.Nil(stream.Send(&ubroker.FetchRequest{})) - result, err := stream.Recv() - s.Nil(err) - s.Equal("hello", string(result.Message.Body)) - - dataReceived := make(chan struct{}) - go func() { - defer close(dataReceived) - - result, err = stream.Recv() - s.Nil(err) - s.Equal("salam", string(result.Message.Body)) - }() - - time.Sleep(100 * time.Millisecond) - select { - case <-dataReceived: - s.FailNow("Fetch should not delivery if not requested") - return - - default: - } - - s.Nil(stream.Send(&ubroker.FetchRequest{})) - - <-dataReceived - - s.Nil(stream.CloseSend()) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) TestAcknowledgeShouldReturnUnavailableIfClosed() { - broker := &mockBroker{} - broker.On("Acknowledge", mock.Anything, int32(10)).Once().Return(ubroker.ErrClosed) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - _, err := client.Acknowledge(ctx, &ubroker.AcknowledgeRequest{ - Id: 10, - }, grpc.WaitForReady(true)) - s.assertStatusCode(codes.Unavailable, err) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) TestAcknowledgeShouldReturnUnavailableIfInvalidID() { - broker := &mockBroker{} - broker.On("Acknowledge", mock.Anything, int32(10)).Once().Return(ubroker.ErrInvalidID) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - _, err := client.Acknowledge(ctx, &ubroker.AcknowledgeRequest{ - Id: 10, - }, grpc.WaitForReady(true)) - s.assertStatusCode(codes.InvalidArgument, err) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) TestAcknowledgeShouldReturnOKOnSuccess() { - broker := &mockBroker{} - broker.On("Acknowledge", mock.Anything, int32(10)).Once().Return(nil) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - _, err := client.Acknowledge(ctx, &ubroker.AcknowledgeRequest{ - Id: 10, - }, grpc.WaitForReady(true)) - s.Nil(err) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) TestReQueueShouldReturnUnavailableIfClosed() { - broker := &mockBroker{} - broker.On("ReQueue", mock.Anything, int32(10)).Once().Return(ubroker.ErrClosed) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - _, err := client.ReQueue(ctx, &ubroker.ReQueueRequest{ - Id: 10, - }, grpc.WaitForReady(true)) - s.assertStatusCode(codes.Unavailable, err) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) TestReQueueShouldReturnUnavailableIfInvalidID() { - broker := &mockBroker{} - broker.On("ReQueue", mock.Anything, int32(10)).Once().Return(ubroker.ErrInvalidID) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - _, err := client.ReQueue(ctx, &ubroker.ReQueueRequest{ - Id: 10, - }, grpc.WaitForReady(true)) - s.assertStatusCode(codes.InvalidArgument, err) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) TestReQueueShouldReturnOKOnSuccess() { - broker := &mockBroker{} - broker.On("ReQueue", mock.Anything, int32(10)).Once().Return(nil) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - _, err := client.ReQueue(ctx, &ubroker.ReQueueRequest{ - Id: 10, - }, grpc.WaitForReady(true)) - s.Nil(err) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) TestPublishShouldReturnUnavailableIfClosed() { - broker := &mockBroker{} - broker.On("Publish", mock.Anything, mock.MatchedBy(func(msg *ubroker.Message) bool { - s.Equal("hello", string(msg.Body)) - return "hello" == string(msg.Body) - })).Once().Return(ubroker.ErrClosed) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - _, err := client.Publish(ctx, &ubroker.Message{ - Body: []byte("hello"), - }, grpc.WaitForReady(true)) - s.assertStatusCode(codes.Unavailable, err) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) TestPublishShouldReturnOKIfSuccessful() { - broker := &mockBroker{} - broker.On("Publish", mock.Anything, mock.MatchedBy(func(msg *ubroker.Message) bool { - s.Equal("hello", string(msg.Body)) - return "hello" == string(msg.Body) - })).Once().Return(nil) - - s.runTest(func(ctx context.Context, client ubroker.BrokerClient) { - _, err := client.Publish(ctx, &ubroker.Message{ - Body: []byte("hello"), - }, grpc.WaitForReady(true)) - s.Nil(err) - - broker.AssertExpectations(s.T()) - }, broker) -} - -func (s *GRPCServerTestSuite) makeChannel(args ...string) <-chan *ubroker.Delivery { - result := make(chan *ubroker.Delivery, len(args)) - var id int32 - - for _, arg := range args { - result <- &ubroker.Delivery{ - Id: id, - Message: &ubroker.Message{ - Body: []byte(arg), - }, - } - - id = id + 1 - } - - close(result) - return result -} - -func (s *GRPCServerTestSuite) assertStatusCode(code codes.Code, err error) { - if code == codes.OK { - s.Nil(err) - return - } - - grpcStatus, ok := status.FromError(err) - s.True(ok) - - if grpcStatus != nil { - s.Equal(code, grpcStatus.Code()) - } -} - -func (s *GRPCServerTestSuite) runTest( - tester func(ctx context.Context, client ubroker.BrokerClient), - broker ubroker.Broker) { - - port, err := freeport.GetFreePort() - if err != nil { - s.FailNow(err.Error(), "failed to obtain free port") - } - - endpoint := fmt.Sprintf("127.0.0.1:%d", port) - servicer := server.NewGRPC(broker) - - grpcServer := grpc.NewServer() - ubroker.RegisterBrokerServer(grpcServer, servicer) - - listener, err := net.Listen("tcp", endpoint) - if err != nil { - s.FailNow(err.Error(), "failed to open listener") - } - - go func() { - grpcServer.Serve(listener) - }() - - dialCtx, dialCtxCancel := context.WithTimeout(context.Background(), 5*time.Second) - defer dialCtxCancel() - - conn, err := grpc.DialContext(dialCtx, endpoint, - grpc.WithInsecure(), grpc.WithBlock()) - if err != nil { - s.FailNow(err.Error(), "failed to dial to gRPC‌ server") - } - - client := ubroker.NewBrokerClient(conn) - - clientCtx, clientCtxCancel := context.WithTimeout(context.Background(), 10*time.Second) - defer clientCtxCancel() - - tester(clientCtx, client) - - err = conn.Close() - if err != nil { - s.FailNow(err.Error(), "failed to properly close gRPC‌ client connection") - } - - grpcServer.GracefulStop() -} diff --git a/internal/server/moc_broker_test.go b/internal/server/moc_broker_test.go deleted file mode 100644 index d3a0fa4..0000000 --- a/internal/server/moc_broker_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package server_test - -import ( - "context" - - "github.com/arcana261/ubroker/pkg/ubroker" - "github.com/stretchr/testify/mock" -) - -type mockBroker struct { - mock.Mock -} - -func (m *mockBroker) Close() error { - args := m.Called() - return args.Error(0) -} - -func (m *mockBroker) Delivery(ctx context.Context) (<-chan *ubroker.Delivery, error) { - args := m.Called(ctx) - - var res0 <-chan *ubroker.Delivery - - if args.Get(0) != nil { - res0 = args.Get(0).(<-chan *ubroker.Delivery) - } - - return res0, args.Error(1) -} - -func (m *mockBroker) Acknowledge(ctx context.Context, id int32) error { - args := m.Called(ctx, id) - return args.Error(0) -} - -func (m *mockBroker) ReQueue(ctx context.Context, id int32) error { - args := m.Called(ctx, id) - return args.Error(0) -} - -func (m *mockBroker) Publish(ctx context.Context, message *ubroker.Message) error { - args := m.Called(ctx, message) - return args.Error(0) -} diff --git a/pkg/ubroker/errors.go b/pkg/ubroker/errors.go index 8e73291..903fa7d 100644 --- a/pkg/ubroker/errors.go +++ b/pkg/ubroker/errors.go @@ -16,4 +16,4 @@ var ( // servicer has been shutted-down and a new request // rolls in ErrClosed = errors.New("closed") -) +) \ No newline at end of file diff --git a/pkg/ubroker/ubroker.go b/pkg/ubroker/ubroker.go index cbc090d..3a76d77 100644 --- a/pkg/ubroker/ubroker.go +++ b/pkg/ubroker/ubroker.go @@ -33,7 +33,7 @@ type Broker interface { // returned // 3. If broker is closed, `ErrClosed` is returned // 4. should be thread-safe - Delivery(ctx context.Context) (<-chan *Delivery, error) + Delivery(ctx context.Context) (<-chan Delivery, error) // Acknowledge is called by clients to declare that // specified message id has been successfuly processed @@ -48,7 +48,7 @@ type Broker interface { // returned // 5. If broker is closed, `ErrClosed` is returned // 6. should be thread-safe - Acknowledge(ctx context.Context, id int32) error + Acknowledge(ctx context.Context, id int) error // ReQueue is called by clients to declare that // specified message id should be put back in @@ -62,7 +62,7 @@ type Broker interface { // returned // 5. If broker is closed, `ErrClosed` is returned // 6. should be thread-safe - ReQueue(ctx context.Context, id int32) error + ReQueue(ctx context.Context, id int) error // Publish is used to enqueue a new message to broker // We demand following: @@ -71,7 +71,7 @@ type Broker interface { // returned // 2. If broker is closed, `ErrClosed` is returned // 3. should be thread-safe - Publish(ctx context.Context, message *Message) error + Publish(ctx context.Context, message Message) error } // HTTPServer defines an HTTP‌ API‌ server provider @@ -81,3 +81,19 @@ type HTTPServer interface { Run() error } + +// Message encapsulates a queued message +type Message struct { + // Body is an abitrary client-defined string + Body string `json:"body"` +} + +// Delivery encapsulates a message fetched +// from queue +type Delivery struct { + // Message is the message fetched from queue + Message Message `json:"message"` + + // ID + ID int `json:"id"` +} \ No newline at end of file From 088cd86b3832cf175ca02052fa6c27f755b889cb Mon Sep 17 00:00:00 2001 From: mahtabfarrokh Date: Fri, 24 May 2019 17:37:15 +0430 Subject: [PATCH 14/14] Revert "go -race bug fixed" This reverts commit 25491b33676da19bff2c8037ecb045b6ffc35160. --- internal/broker/core.go | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/internal/broker/core.go b/internal/broker/core.go index 0a50afa..d6dbdbd 100644 --- a/internal/broker/core.go +++ b/internal/broker/core.go @@ -2,6 +2,7 @@ package broker import ( "context" + "fmt" "sync" "time" @@ -68,25 +69,23 @@ func (c *core) Delivery(ctx context.Context) (<-chan ubroker.Delivery, error) { if c.closed { return nil, ubroker.ErrClosed } - c.mut.Lock() c.deliveryStarted = true - c.mut.Unlock() + //c.wg.Done() return c.brokerChan, nil //return nil, errors.Wrap(ubroker.ErrUnimplemented, "method Delivery is not implemented") } func (c *core) Acknowledge(ctx context.Context, id int) error { - c.mut.Lock() + fmt.Println("id", id) if c.closed { - c.mut.Unlock() return ubroker.ErrClosed } if contextProblem(ctx) { - c.mut.Unlock() return ctx.Err() } temp := false - + c.mut.Lock() + fmt.Println("locked in ack") if c.deliveryStarted { temp = true } @@ -97,10 +96,12 @@ func (c *core) Acknowledge(ctx context.Context, id int) error { } if !temp { + fmt.Println("locked in ack released") c.mut.Unlock() return errors.Wrap(ubroker.ErrInvalidID, "invalid Id") } if c.closed { + fmt.Println("locked in ack released") c.mut.Unlock() return ubroker.ErrClosed @@ -108,11 +109,14 @@ func (c *core) Acknowledge(ctx context.Context, id int) error { c.receivedAck = append(c.receivedAck, id) for i, element := range c.publishedQueue { if element.ID == id { + fmt.Println("-") c.publishedQueue[i].receivedAckChannel <- id + fmt.Println("--") c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) break } } + fmt.Println("locked in ack released") c.mut.Unlock() //c.wg.Done() @@ -131,6 +135,7 @@ func (c *core) DoingReQueue(ctx context.Context, id int) { c.publishedQueue = append(c.publishedQueue[:i], c.publishedQueue[i+1:]...) c.publishedQueue = append(c.publishedQueue, v2) c.brokerChan <- v + fmt.Println("locked released doing requeue") c.mut.Unlock() go c.HandelingTTL(ctx, v2) break @@ -140,12 +145,16 @@ func (c *core) DoingReQueue(ctx context.Context, id int) { } func (c *core) HandelingTTL(ctx context.Context, element item) { + fmt.Println("ha?") select { case <-time.After(c.ttl): + fmt.Println("inja? ") c.mut.Lock() + fmt.Println("locked in handeling ttl ") c.DoingReQueue(ctx, element.ID) return case <-element.receivedAckChannel: + fmt.Println("what ?") return case <-c.closedChan: return @@ -153,11 +162,14 @@ func (c *core) HandelingTTL(ctx context.Context, element item) { } func (c *core) ReQueue(ctx context.Context, id int) error { c.mut.Lock() + fmt.Println("locked here requqeue!") if c.closed { + fmt.Println("locked requeue released") c.mut.Unlock() return ubroker.ErrClosed } if contextProblem(ctx) { + fmt.Println("locked requeue released") c.mut.Unlock() return ctx.Err() } @@ -176,6 +188,7 @@ func (c *core) ReQueue(ctx context.Context, id int) error { } } if !temp { + fmt.Println("locked requeue released") c.mut.Unlock() return errors.Wrap(ubroker.ErrInvalidID, "invalid Id") @@ -190,6 +203,7 @@ func (c *core) DoingPublish(ctx context.Context, message ubroker.Message) { v2 := item{Message: message, ID: c.lastIdValue, receivedAckChannel: make(chan int, 10)} c.publishedQueue = append(c.publishedQueue, v2) c.brokerChan <- v + fmt.Println("locked doing publish released") c.mut.Unlock() c.HandelingTTL(ctx, v2) @@ -200,6 +214,7 @@ func (c *core) Publish(ctx context.Context, message ubroker.Message) error { return ctx.Err() } c.mut.Lock() + fmt.Println("locked publish") if c.closed { c.mut.Unlock() return ubroker.ErrClosed @@ -216,7 +231,9 @@ func (c *core) Close() error { for i := 0; i < 4000; i++ { c.closedChan <- true } + fmt.Println("here wait") c.mut.Lock() + fmt.Println("closed") close(c.brokerChan) c.closed = true c.mut.Unlock()