Earlier quoted context omitted.
It sounds like you may have some friction-studded history with Go. Any chance you can share your experience and perspective with using the language in your workloads?
It's mostly dead locked networking code. Hard to investigate, hard to search the culprit. And of course code bases without linter for err propagation and handling. And this-null for "methods".
Context should go away for Go 2 (2017)
171–176 of 176 posts
Re: Context should go away for Go 2 (2017)
#172Earlier quoted context omitted.
You're spawning a goroutine per Read call? This is pretty bonkers inefficient, to start, and a super weird approach in any case...
Yes, but this is just proof of concept. For any given case, you can optimize your approach to your needs. E.g. single goroutine ReadCloser: type ioContextReadCloser struct { io.ReadCloser ctx context.Context ch chan *readReq } type readReq struct { p []byte n *int err *error m sync.Mutex } func NewIoContextReadCloser(ctx context.Context, rc io.ReadCloser) *ioContextReadCloser { rcc := &ioContextReadCloser{ ReadCloser…
The io.Reader/Writer interfaces, and their implementations, are meant to provide a streaming model for reading and writing bytes, which is as efficient as reasonably possible, within the constraints of the core language.
If your goal is to make an io.Reader that respects a context.Context cancelation, then you can just do
type ContextReader struct {
ctx context.Context
r io.Reader
}
func NewContextReader(ctx context.Context, r io.Reader) *ContextReader {
return &ContextReader{
ctx: ctx,
r: r,
}
}
func (cr *ContextReader) Read(p []byte) (int, error) {
if err := cr.ctx.Err(); err != nil {
return 0, err
}
return cr.r.Read(p)
}
No goroutines or mutexes or whatever else required.Extending to a ReadCloser is a simple exercise left to the, er, reader.
Re: Context should go away for Go 2 (2017)
#173Earlier quoted context omitted.
An alternative is to add all dependencies explicitly into function argument list or object fields, instead of using them implicitly from the context, without documentation and static typing. Including logger.
I already talked about it above. Main problems with passing dependencies in function argument lists: 1) it pollutes the code and makes refactoring harder (a small change in one place must be propagated to all call sites in the dependency tree which recursively accept user ID/tenant ID and similar info) 2) it violates various architectural principles, for example, from the point of view of our business logic, there's…
If context isn't uniform and minimal, and people can add/remove fields for their own purposes, the context becomes a really sneaky point of coupling.
Adapting context-ful code from a request-response world to (for example) a parallel-batch-job world or continuous stream consumer world runs into friction: a given organization's idioms around context usually started out in one of those worlds, and don't translate well to others. If I'm a worker thread in a batch job working on a batch of "move records between tenant A and tenant B" work, but the business logic methods I'm calling to retrieve and store records are sensitive to a context field that assumes it'll be set in a web request (and that each web request will be made for exactly one tenant), what do I do? If your business is always going to be 99% request/response code, then sure, hack around the parts that aren't. But if your business does any continuous data pipeline wrangling, you rapidly end up with either a split codebase (request-response contextful vs "things that are only meant to be called from non-request-response code") or really thorny debugging around context issues in non-request-response code.
If you choose to deal with context thread-locally (or coroutine locally, or something that claims to be both but is in reality neither--looking at you, "contextlib"), that sneaky context mutation by the concurrency system multiplies the difficulties in reasoning about context behavior.
> it violates various architectural principles, for example, from the point of view of our business logic, there's no such thing as "tenant ID"
I think a lot of people lose sight of how incredibly useful explicit dependency management is because it's classed as "tight coupling" and "bad architecture" when it's nothing of the sort. I blame 2010s Java and dependency inversion/injection brainrot.
Business logic is rarely pure; most "business" code functions as transforming glue between I/O. The behavior of the business logic is fundamentally linked to _where_ (and often _how_ as well--e.g. is it in a database transaction?) it interacts with datastores and external services. "Read/write business code as if it didn't have side effects" is not a good approach if code is _primarily occupied with causing side effects_--and, in commercial software engineering, most of it is!
From that perspective, explicitly passing I/O system handles, settings, or whatnot everywhere can be a very good thing: when reading complex business logic, the presence (or absence) of those dependencies in a function call tells you what parts of the system will (or can) conduct I/O. That provides at-a-glance information into where the system can fail, where it can lag, what services or mocks need to be running to test a given piece of code, and at a high level what data flows it models (e.g. if a big business logic function receives an HTTP client factory for "s3.amazonaws.com/..." and a database handle, it's a safe bet that the code in question broadly moves data between S3 and the database).
While repetitive, doing this massively raises the chance of catching certain mistakes early. For example, say you're working on a complex businessy codebase and you see a long for-loop around a function call like "process_record(record, database_tenant_id, use_read_replica=True, timeout=5)"? That's a strong hint that there's an N+1 query/IO risk in that code, and the requirement that I/O system dependencies be passed around explicitly encodes that hint _semantically_.
That kind of visibility is vastly superior to "pure" and uncluttered business logic that relies on context/lexicals to plumb IO around. Is the pure code less noisy and easier to interpret? Sure, but the results of that interpretation are so much less valuable as to be actively misleading.
Put another way: business logic is concerned with things like tenant IDs and database connections; obscuring those dependencies is harmful. Separation of concerns means that good business code is code that avoids mutating, or making decisions based on, the dependencies it receives--not that it doesn't receive them/use them/pass them around.
Re: Context should go away for Go 2 (2017)
#174Earlier quoted context omitted.
In Jetpack compose, the Composer is embedded by the compiler at build time into function calls https://medium.com/androiddevelopers/under-the-hood-of-jetpa... I’m still not sure how I feel about it. While more annoying, I think I’d like to see it, rather than just have magic behind the hood
seeing it is great. coming into a hairy monolith and having to plumb one variable through half a dozen layers to get to the creamy nougat later you actually wanted it in, is not. having to do that more than once it's why they invented the "magic" implicit context variable.
And if it wasn’t immediately available, it would give you a strong signal to wonder what you’re doing wrong
Re: Context should go away for Go 2 (2017)
#175Earlier quoted context omitted.
I'm not sure what you consider a decent scala ide, but it was a problem with IntelliJ in several of our code bases, and I'd have to crawl the implicit resolution path. I eventually opted to desugaring the scala completely, but we were already on the way out of scala by that point
> it was a problem with IntelliJ in several of our code bases It shouldn't be, unless you were using macros (always officially experimental) or something - I was always primarily an Eclipse guy but IntelliJ worked well. Did you not get the green underline?
Didn't seem to matter how close to geotrellis we were though. That being said, I'm willing to buy that it confused IntelliJ enough that it gave up on entire packages
Either way, we ported a couple of the abstractions from there to go and just used that. The build times plus getting away from sbt have been enough of a win to keep us on go, and that's before we get to the memory reduction on in-memory rasters
Re: Context should go away for Go 2 (2017)
#176Earlier quoted context omitted.
> it was a problem with IntelliJ in several of our code bases It shouldn't be, unless you were using macros (always officially experimental) or something - I was always primarily an Eclipse guy but IntelliJ worked well. Did you not get the green underline?
We weren't using macros, but one of our major dependencies was (geotrellis). Didn't seem to matter how close to geotrellis we were though. That being said, I'm willing to buy that it confused IntelliJ enough that it gave up on entire packages Either way, we ported a couple of the abstractions from there to go and just used that. The build times plus getting away from sbt have been enough of a win to keep us on go, an…