NSQ Re-Queue half Message - go

I have an array of Ids of type int64 And this is my Nsq Message that I am trying to publish.
nsqMsg := st{
Action : "insert",
Ids : Ids
GID : Gids
}
msg, err := json.Marshal(nsqMsg)
if err != nil {
log.Println(err)
return err
}
err = nsqProducer.Publish(TOPIC-NAME, msg)
if err != nil {
log.Println(err)
return err
}
While in my consumer I am taking each Id one by one and fetching an info based on my Id from my datastore.
So while fetching there can be a case if my CreateObject method returns an error so I handle that case by requeue the msg (which is giving the error) and so it can be retried.
for i := 0; i < len(data.Ids); i++ {
Object, err := X.CreateObject(data.Ids[i)
if err != nil {
requeueMsgData = append(requeueMsgData, data.Ids[i])
continue
}
DataList = append(DataList, Object)
}
if len(requeueMsgData) > 0 {
msg, err := json.Marshal(requeueMsgData)
if err != nil {
log.Println(err)
return err
}
message.Body = msg
message.Requeue(60 * time.Second)
log.Println("error while creating Object", err)
return n
}
So, is this the right way of doing this?
Is their any drawback of this case?
Is it better to publish it again?

Some queues (like Kafka) support acknowledgement where items that are dequeued are not removed from the queue until the consumer has actually acknowledged successful receipt of the item.
The advantage of this model is that if the consumer dies after consumption but before acknowledgement, the item will be automatically re-queued. The downside of your model is that the item might be lost in that case.
The risk of an acknowledgment model is that items could now be double consumed. Where a consumer attempts consumption that has side-effects (like incrementing a counter or mutating a database) but doesn't acknowledge so retries might not create the desired result. (note that reading through the nsq docs, retries are not guaranteed to happen even if you don't re-enqueue the data so your code will likely have to be defensive against this anyway).
You should look into the topic of "Exactly Once" vs. "At Most Once" processing if you want to understand this deeper.
Reading through the nsq docs, it doesn't look like acknowledgement is supported so this might be the best option you have if you are obligated to use nsq.

Along the lines with what dolan was saying there are a couple of cases that you could encounter:
main message heartbeat/lease times out and you receive ALL ids again (from the original message). NSQ provides "at least once" semantics.
Requeue of any single message times out and is never complete (fallback to the main IDS)
Because nsq can (and most def will :p) deliver messages more than once CreateObjects could/should be idempotent in order to handle this case.
Additionally the redelivery is an important safety mechanism,
The original message shouldn’t be fin’d until all individual ids or confirmed created or successfully requeued, which ensures that no data is lost.
IMO the way you are handling it looks perfectly good, but the most important considerations IMO are handling correctness/data integrity in an environment where duplicate messages will be received.
Another option could be to batch the Requeue so that it attempts to produce a single output message of failed ids, which could cut back on the # of messages in the queue at any given time:
Consider a message with 3 ids:
message ids: [id1, id2, id3]
id1 succeeds creation and id2 and id3 fail:
the program could attempt all operations and emit a single requeue message, with id2, id3.
But trade offs with this too.

Related

Order of users.messages.list observed to be not descending

In order to sync mailboxes my application follows the sync recommendations by attempting to find the history ID of the latest message in the users mailbox. We then use this for partial syncs going forward.
Recently we noticed behavior that suggested an issue with these syncs. One explanation was that we were receiving a much older message and history ID. I've tested our functionality and it appears to work correctly. Still, in an attempt to rule out a potential root cause, I added some checks to detect if the users.messages.list API return results out of descending order. These checks ended up being hit suggesting that this is an issue.
Here is my function, in Go, for finding the latest history ID. This includes the additional checks I added to validate the ordering -- essentially instead of using messages.get for the first entry in the list, it also gets the last entry in the list and then compares dates/history IDs: the first entry in the list should have the greatest history ID and date.
func getLatestHistoryID(ctx context.Context, gmailService *gmail.Service) (uint64, time.Time, error) {
messagesResponse, err := gmailService.Users.Messages.List("me").IncludeSpamTrash(true).Context(ctx).Do()
if err != nil {
return 0, time.Time{}, err
}
messagesList := messagesResponse.Messages
if messagesList == nil || len(messagesList) == 0 {
return 0, time.Time{}, nil
}
latestMessage, err := gmailService.Users.Messages.Get("me", messagesList[0].Id).Context(ctx).Do()
if err != nil {
return 0, time.Time{}, err
} else if latestMessage == nil {
return 0, time.Time{}, nil
}
earliestMessage, err := gmailService.Users.Messages.Get("me", messagesList[len(messagesList)-1].Id).Context(ctx).Do()
if err != nil {
log.Errorf("error doing optional check to validate ordering of message list. %v", err)
} else if earliestMessage == nil {
log.Errorf("unexpected earliest message not retrieved")
} else {
if latestMessage.HistoryId < earliestMessage.HistoryId {
return 0, time.Time{}, fmt.Errorf("message list was not in the expected order by history id! first in list %d (%s), last %d (%s)",
latestMessage.HistoryId, latestMessage.Id,
earliestMessage.HistoryId, earliestMessage.Id)
}
// This could probably fail in rare clock skew cases, but right now we're observing this being a several hour difference between dates.
if latestMessage.InternalDate < earliestMessage.InternalDate {
return 0, time.Time{}, fmt.Errorf("message list was not in the expected order by date! first in list %s (%s), last %s (%s)",
time.UnixMilli(latestMessage.InternalDate).String(), latestMessage.Id,
time.UnixMilli(earliestMessage.InternalDate).String(), earliestMessage.Id)
}
}
return latestMessage.HistoryId, time.UnixMilli(latestMessage.InternalDate), nil
}
I've found several resources that confirm that users.messages.list is expected to be descending by date/history ID:
Gmail API - Getting different results with users.threads.list vs users.messages.list
In what order does the Gmail API return messages when calling "Users.messages: list"
https://developers.google.com/gmail/api/guides/sync#full_synchronization #3
Edited: originally linked to https://developers.google.com/gmail/api/guides/sync#limitations
When I test the function above locally it works as expected, and the return statement on the last line is hit. Yet I've observed the out of order detection errors hundred of times. Of the failures, ~9/10 times I'm seeing the HistoryId check fail. I believe this is largely failing on a small set of mailboxes, and I am currently not sure what proportion usages this occurs (working on gathering this).
Is there any reason the API may return results out of order? Is there anything wrong with the assumptions made by my checks?
API return results out of descending order.
If you check the documentation for users.messages.list you will find that there is no order by parameter. Which means that there is no way for you to guarantee the order the data arrives in.
It could arrive sometimes in descending order and other times not in descending order. There is no way to guarantee it if there was it would state the order in the docs.
#limitations does not mention anything about order it only mentions that it may or may not be alliable.
History records are typically available for at least one week and often longer.
you should always sort this locally.

Receive protobuf encoded messages that can be partially written?

I am trying to send and receive protobuff encoded messages in GoLang over TCP, where the sender can cancel the write() halfway through the operation, and the receiver can correctly receive partial messages.
Note that I use a single TCP connection to send messages of different user defined types, infinitely (this is not a per connection message case)
To explain my question concretely, first I will present how I implement the send/receive without partial writes.
In my program, there are multiple types of messages, defined in a .proto file. I will explain the mechanism for one such message type.
message MessageType {
int64 sender = 1;
int64 receiver = 2;
int64 operation = 3;
string message = 4;
}
Then I use Golang Protobuf plugin to generate the stubs.
Then in the sender side, the following is how I send.
func send(w *bufio.Writer, code uint8, oriMsg MessageType) {
err := w.WriteByte(code)
data, err := proto.Marshal(oriMsg)
lengthWritten := len(data)
var b [8]byte
bs := b[:8]
binary.LittleEndian.PutUint64(bs, uint64(lengthWritten))
_, err = w.Write(bs)
_, err = w.Write(data)
w.flush()
}
Then in the receiver side, the following is how I receive.
reader *bufio.Reader
for true {
if msgType, err = reader.ReadByte(); err != nil {
panic()
}
if msgType == 1 || msgType == 2{
var b [8]byte
bs := b[:8]
_, err := io.ReadFull(reader, bs)
numBytes := binary.LittleEndian.Uint64(bs)
data := make([]byte, numBytes)
length, err := io.ReadFull(reader, data)
msg *MessageType = new(GenericConsensus) // an empty message
err = proto.Unmarshal(data[:length], msg)
// do something with the message
} else {
// unknown message type handler
}
}
Now my question is, what if the sender aborts his writes in the middle: more concretely,
Case 1: what if the sender writes the message type byte, and then abort? In this case the receiver will read the message type byte, and waits to receive an 8 byte message length, but the sender doesn't send it.
Case 2: This is an extended version of case 1 where the sender first sends only the message type byte, and the aborts sending the message length and marshaled message, and then send the next message: the type byte, the length and encoded message. Now in the receiver side, everything goes wrong because the order of messages (type, length and encoded message) is violated.
So my question is, how can I modify the receiver such that it can continue to operate despite the sender violating the pre-agreed order of type:length:encoded-message?
Thanks
Why would the sender abort a message, but then send another message? You mean it's a fully byzantine sender? Or are you preparing for fuzzy-testing?
If your API contract says that the sender always needs to send a correct message, then the receiver can simply ignore wrong messages, or even close the connection if it sees a violation of the API contract.
If you really need it, here some ideas of how you could make it work:
start with a unique preamble - but then you will have to make sure this preamble never comes up in the data
add a checksum to the message before sending it to the decoder. So the full packet would be: [msg_type : msg_len : msg : chksum ]. This allows the receiver to check whether it's a correct message or a misformed one.
Also, as the code is currently, it is quite easy to crash by sending a size with the maximum of 64 bits. So you should also check for the size to be in a useful range. I would limit it to 32 bits...

Stream events to potentially slow clients

Given a (infinite) stream of events, a server program must stream the events to several clients. For example:
for _, event := range events {
for _, client := range clients {
client.write(event) // blocking operation
}
}
However, if a client is slow, it can throttle the other clients. Therefore, for each client, we can add a channel (and a client specific goroutine consuming that channel):
for _, event := range events {
for _, client := range clients {
client.writer <- event // writer channel is consumed by a per-client go routine
}
}
As long as the buffered channel is not full, this works. However, if the channel gets full, it'll block again. I can think of the following options:
Drop the event on channel full, close the channel, force the client to reconnect. This requires re-negotiating the channel position, or a complete re-stream. The force reset will potentially waste resources, making the slow client even slower
Add a channel that goes the other way, make the client signal the server that it is ready to receive. This requires the server to keep a track of stream positions for each client (perhaps that is not too bad?). It seems to have a bit more sync than necessary in the nominal (not-slow) case, increasing latency.
Something else? What's the idiomatic go way to do this?
EDIT: Here's a simple, good looking, but broken solution: track the stream position in the client object, and send the outstanding events:
for _, event := range events {
persisted_events = append(persisted_events, event)
for _, client := range clients {
for _, event := range persisted_events[client.last_event:] {
select {
case client.writer <- event:
client.last_event++
default:
break
}
}
}
}
This allows slow clients to catch-up, without starwing the others. It does not require a disconnect. However, it is also broken: if the stream of events stops while a client is catching up, it is possible that the slow client gets stuck - as the main loop if waiting for new events. Adding a ticker that triggers the loop sometimes is not efficient. Requiring the client to notify the main loop that it is now idle is complex, and potentially doubles the number of events.
The best setup I found so far:
for {
var event Event = nil
select {
case event, ok := <-events:
if !ok {
return
}
persisted_events = append(persisted_events, event)
case event <- ticker:
}
for _, client := range clients {
select {
case client.writer <- persisted_events[client.num_events:]:
client.num_events = len(persisted_events)
default:
}
}
}
This is a slight variation of the broken example in the question. There are two key differences:
The writer channel takes a slice of events (instead of a single event)
There's a ticker, that wakes the loop periodically, even if there are no new events
Combined, this achives that even if a slow client catches up right after it misses the last event(s) before a long silent period, the final catching up is only delayed by one period of the ticker, instead of the combination of the ticker period plus the number of events missed. Therefore, reducing the ticker period can cap the tail latency. Not ideal in terms of performance, but simple (in terms of concurrency).

Should there be a new datastore.Client per HTTP request?

The official Go documentation on the datastore package (client library for the GCP datastore service) has the following code snippet for demonstartion:
type Entity struct {
Value string
}
func main() {
ctx := context.Background()
// Create a datastore client. In a typical application, you would create
// a single client which is reused for every datastore operation.
dsClient, err := datastore.NewClient(ctx, "my-project")
if err != nil {
// Handle error.
}
k := datastore.NameKey("Entity", "stringID", nil)
e := new(Entity)
if err := dsClient.Get(ctx, k, e); err != nil {
// Handle error.
}
old := e.Value
e.Value = "Hello World!"
if _, err := dsClient.Put(ctx, k, e); err != nil {
// Handle error.
}
fmt.Printf("Updated value from %q to %q\n", old, e.Value)
}
As one can see, it states that the datastore.Client should ideally only be instantiated once in an application. Now given that the datastore.NewClient function requires a context.Context object does it mean that it should get instantiated only once per HTTP request or can it safely be instantiated once globally with a context.Background() object?
Each operation requires a context.Context object again (e.g. dsClient.Get(ctx, k, e)) so is that the point where the HTTP request's context should be used?
I'm new to Go and can't really find any online resources which explain something like this very well with real world examples and actual best practice patterns.
You may use any context.Context for the datastore client creation, it may be context.Background(), that's completely fine. Client creation may be lengthy, it may require connecting to a remote server, authenticating, fetching configuration etc. If your use case has limited time, you may pass a context with timeout to abort the operation. Also if creation takes longer than the time you have, you may use a context with cancel and abort the mission at your will. These are just options which you may or may not use. But the "tools" are given via context.Context.
Later when you use the datastore.Client during serving (HTTP) client requests, then using the request's context is reasonable, so if a request gets cancelled, then so will its context, and so will the datastore operation you issue, rightfully, because if the client cannot see the result, then there's no point completing the query. Terminating the query early you might not end up using certain resources (e.g. datastore reads), and you may lower the server's load (by aborting jobs whose result will not be sent back to the client).

How do I properly post a message to a simple Go Chatserver using REST API

I am currently building a simple chat server that supports posting messages through a REST API.
example:
========
```
curl -X POST -H "Content-Type: application/json" --data '{"user":"alex", "text":"this is a message"}' http://localhost:8081/message
{
"ok": true
}
Right now, I'm just currently storing the messages in an array of messages. I'm pretty sure this is an inefficient way. So is there a simple, better way to get and store the messages using goroutines and channels that will make it thread-safe.
Here is what I currently have:
type Message struct {
Text string
User string
Timestamp time.Time
}
var Messages = []Message{}
func messagePost(c http.ResponseWriter, req *http.Request){
decoder := json.NewDecoder(req.Body)
var m Message
err := decoder.Decode(&m)
if err != nil {
panic(err)
}
if m.Timestamp == (time.Time{}) {
m.Timestamp = time.Now()
}
addUser(m.User)
Messages = append(Messages, m)
}
Thanks!
It could be made thread safe using mutex, as #ThunderCat suggested but I think this does not add concurrency. If two or more requests are made simultaneously, one will have to wait for the other to complete first, slowing the server down.
Adding Concurrency: You make it faster and handle more concurrent request by using a queue (which is a Go channel) and a worker that listens on that channel - it'll be a simple implementation. Every time a message comes in through a Post request, you add to the queue (this is instantaneous and the HTTP response can be sent immediately). In another goroutine, you detect that a message has been added to the queue, you take it out append it to your Messages slice. While you're appending to Messages, the HTTP requests don't have to wait.
Note: You can make it even better by having multiple goroutines listen on the queue, but we can leave that for later.
This is how the code will somewhat look like:
type Message struct {
Text string
User string
Timestamp time.Time
}
var Messages = []Message{}
// messageQueue is the queue that holds new messages until they are processed
var messageQueue chan Message
func init() { // need the init function to initialize the channel, and the listeners
// initialize the queue, choosing the buffer size as 8 (number of messages the channel can hold at once)
messageQueue = make(chan Message, 8)
// start a goroutine that listens on the queue/channel for new messages
go listenForMessages()
}
func listenForMessages() {
// whenever we detect a message in the queue, append it to Messages
for m := range messageQueue {
Messages = append(Messages, m)
}
}
func messagePost(c http.ResponseWriter, req *http.Request){
decoder := json.NewDecoder(req.Body)
var m Message
err := decoder.Decode(&m)
if err != nil {
panic(err)
}
if m.Timestamp == (time.Time{}) {
m.Timestamp = time.Now()
}
addUser(m.User)
// add the message to the channel, it'll only wait if the channel is full
messageQueue <- m
}
Storing Messages: As other users have suggested, storing messages in memory may not be the right choice since the messages won't persist if the application is restarted. If you're working on a small, proof-of-concept type project and don't want to figure out the DB, you could save the Messages variable as a flat file on the server and then read from it every time the application starts (*Note: this should not be done on a production system, of course, for that you should set up a Database). But yeah, database should be the way to go.
Use a mutex to make the program threadsafe.
var Messages = []Message{}
var messageMu sync.Mutex
...
messageMu.Lock()
Messages = append(Messages, m)
messageMu.Unlock()
There's no need to use channels and goroutines to make the program threadsafe.
A database is probably a better choice for storing messages than the in memory slice used in the question. Asking how to use a database to implement a chat program is too broad a question for SO.

Resources