-
Notifications
You must be signed in to change notification settings - Fork 57
feat: Extend error handling to allow conflict resolution #124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
a0179e6
to
b7f2705
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
first pass!
copier, err := csvcopy.NewCopier(connStr, "test_metrics", | ||
csvcopy.WithColumns("device_id,label,value"), | ||
csvcopy.WithBatchSize(2), | ||
csvcopy.WithBatchErrorHandler(BatchConflictHandler(WithConflictHandlerNext(csvcopy.BatchHandlerNoop()))), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO, having next handler complicates the API. Do you think it would be useful? if so, why not just use array of batch error handlers.?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current design is a decorator pattern. A next handler is something is called after your function finishes and allows to chain options.
Now that you mention it, next it is provably not correct working, as the actual goal here is to fallback in case of not having a conflict error.
Having an array doesn't solve the problem. because if you do that, how the array should behave? execute all the handlers? just the last one? how do you combine outputs? .... it becomes hard to manage.
If we rename it, do you have any ideas for the name? 😅 I can't think of any good one.
// Create a copy of the input data to avoid issues with caller reusing the slice | ||
data := make([]byte, len(p)) | ||
copy(data, p) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no copy involved with net.Buffers., should we retain that property to avoid excessive garbage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a copy, but done in scan.go function
here exactly, I hope the link works
} | ||
|
||
// Seek sets the position for next Read or Write operation | ||
func (v *Seekable) Seek(offset int64, whence int) (int64, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a use-case for SeekCurrent
and SeekEnd
? I think we could have just stayed with net.Buffers
and buf[0][0] might be good enough?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
net buffer discards data on read.
here is the implementation. on Read it consumes the bytes by resizing the slice. That is why this implementation uses the moving indexes instead. It allows to just reset it to come back to the start.
// Read from the buffers.
//
// Read implements [io.Reader] for [Buffers].
//
// Read modifies the slice v as well as v[i] for 0 <= i < len(v),
// but does not modify v[i][j] for any i, j.
func (v *Buffers) Read(p []byte) (n int, err error) {
for len(p) > 0 && len(*v) > 0 {
n0 := copy(p, (*v)[0])
v.consume(int64(n0))
p = p[n0:]
n += n0
}
if len(*v) == 0 {
err = io.EOF
}
return
}
func (v *Buffers) consume(n int64) {
for len(*v) > 0 {
ln0 := int64(len((*v)[0]))
if ln0 > n {
(*v)[0] = (*v)[0][n:]
return
}
n -= ln0
(*v)[0] = nil
*v = (*v)[1:]
}
}
defer workerWg.Done() | ||
err := c.processBatches(ctx, batchChan) | ||
// Add worker ID to context for all operations in this worker | ||
workerCtx := WithWorkerID(ctx, i) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I failed to understand the usefulness of adding worker id into the ctx. Also, why adding worker id into the COPY statements will be useful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
why adding worker id to the context
Being in the context, it is automatically propagated across interfaces. This means you can implement theBatchErrorHandler
and you will still receive the worker id over the context. Then you can use it to log which worker executed the handler. The main advantage is that if you want, you can forget it exists. -
why adding worker id to copy statements
I hit a situation where there was a dead lock and the error statements display the entire query. having the id in there allows me to correlate the error with an specific worker. So it is easy for me to link app logs with postgres logs.
// Add worker ID comment if available in context | ||
if workerID := GetWorkerIDFromContext(ctx); workerID >= 0 { | ||
baseCmd = fmt.Sprintf("/* Worker-%d */ %s", workerID, baseCmd) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I failed to understand the usefulness of adding worker id to the copy command.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why adding worker id to copy statements
I hit a situation where there was a dead lock and the error statements display the entire query. having the id in there allows me to correlate the error with an specific worker. So it is easy for me to link app logs with postgres logs.
No description provided.