Indeed, these processes are not all sitting there doing nothing.
Two processes in particular have been this exact sort of problem for me: mds_stores and mediaanalysisd. On three separate Macs (all Apple Silicon), I've observed the case heating up whenever the computer is plugged in but not actively being used. Assuming Activity Monitor is more or less accurate, the culprit seems to be those two, who always have massive amounts of accumulated CPU time, but never seem to actually be using CPU when watched. I suspect, given what they supposedly do, that they're also needlessly exhausting SSD write cycles, but that's harder to analyze/prove. Naturally, they are also in the untouchable area of the file system. Completely disabling Spotlight, which you can do without disabling SIP, seems to always fix this problem, albeit at the cost of seriously decreased usability. I've also had mixed results with just limiting the categories of Spotlight indexing in System Settings.
Yeah, that's not supposed to be happening. Yet it does. For me it's fseventsd that goes crazy sometimes. These processes are all meant to be lightweight, but they're just buggy and end up in bizarre loops. Once my Mac crashed because it was endlessly downloading the same Aerial screen saver videos in a temp directory until it ran out of space.
It's kinda hard to handle MERGE failures gracefully. You generally expect the whole thing to succeed, and the syntax deceptively makes it seem like you can handle all the cases. But because of MVCC, you get these TOCTOU-style spurious constraint violations, yet there's no way to address them on a per-row basis, leading to the entire statement rolling back even for the rows that had no issues. If you are designing for concurrent OLTP workloads against the table, you should probably just avoid MERGE altogether. It's more useful for one-off manual fixups.
I'm not sure why you'd expect partial updates of a single statement in the first place. I mean, if I run `UPDATE Account SET Status = 'Closed' WHERE LastAccess < NOW() - INTERVAL '90 days';`, I'm not going to be happy if there's 50 records that match, the DB updates 30 successfully, and then error on 20. Atomic isn't just about rows. Do all the work or none of it.
If you're experiencing things that smell like TOCTOU, first you need to be sure you don't have oddball many-to-one issues going on (i.e., a cardinality violation error), and then you're going to have to increase your transaction isolation level to eliminate non-repeatable reads and phantom reads.
Like, the alternative to a MERGE is writing a few UPDATE statements followed by an INSERT and wrapping the entire batch in a transaction. And you should likely still wrap the whole thing in a transaction. If it breaks, you just replay the whole thing. Re-run the whole job.
I don't want partial updates, I want full, conflict-free upserts.
At read committed (default) isolation level, INSERT ... ON CONFLICT handles concurrent, conflicting inserts just fine, while MERGE ... WHEN NOT MATCHED (e.g.) does not. This is surprising behavior from the syntax alone, one would assume the two statements, when written with the same intent, would have the same behavior. Of course, this difference is documented, see the last paragraph of the Notes section on MERGE: https://www.postgresql.org/docs/18/sql-merge.html#id-1.9.3.1...
I don't know this for sure, but I believe that the effect of raising the transaction isolation level will just be to turn the constraint violation into a serialization error. That's not any easier to handle gracefully.
A lot of HTML tags never have a body, so it makes no sense to close them. XML has self-closing tag syntax but it wasn't always handled well by browsers.
A p or li tag, at least when used and nested properly, logically ends where either the next one begins or the enclosing block ends. Closing li also creates the opportunity for nonsensical content inside of a list but not in any list item. Of course all of these corner cases are now well specified because people did close their tags sometimes.
> A p or li tag, at least when used and nested properly, logically ends where either the next one begins or the enclosing block ends
While this is true I’ve never liked it.
<p>blah<p>blah2</p>
Implies a closing </p> in the middle. But
<p>blah<span>blah2</p>
Does not. Obviously with the knowledge of the difference between what span and p represent I understand why but in terms of pure markup it’s always left a bad taste in my mouth. I’ll always close tags whenever relevant even if it’s not necessary.
This interpretation of the p element implies that it contains a paragraph. But HTML is first and foremost a document format, and one could just as logically conclude that the p element simply starts a new paragraph. Under the latter interpretation, </p> would never exist any more than </hr> or </img>.
In practice, modern HTML splits the difference with rigorous and well defined but not necessarily intuitive semantics.
I was going to respond that HTML was the original syntax and XML the usurper, but a comment in another thread casts some doubt on that version of events: https://news.ycombinator.com/item?id=46576844
You may already know these things, but for others who may not: the AWS SDK for Go suffers from shockingly bad discoverability.
For example, take the S3 library, github.com/aws/aws-sdk-go-v2/service/s3. If you have an s3.Client and you look at e.g. the ListObjectsV2 method, you might have no idea that there is a ListObjectsV2Paginator which makes it much easier to use, because nowhere in the method docs is it mentioned. Indeed, most operations that paginate have more ergonomic paginators, but none of them tell you this.
But that isn't even the worst of it. Say you want to download or upload a file to S3. If you haven't worked with AWS for other languages, you might think that you just do GetObject and PutObject. And yes, for small files, that's generally fine. But for large files you want to use resumable downloads and multipart uploads. So you look and lo, there is no simple way to do this in the AWS SDK for Go. But actually, there is! It's in a totally unrelated and unlinked package, called github.com/aws/aws-sdk-go-v2/feature/s3/manager.
Now you're getting some religion, so you ask "what are the other so-called 'feature' packages?" and you try to browse pkg.go.dev at the github.com/aws/aws-sdk-go-v2/feature level but nope, that's not a Go module so there's nothing to see there. In fact, you can't even browse the other s3 features, never mind find out what other services have features. Fortunately, you can browse their GitHub repo at least: https://github.com/aws/aws-sdk-go-v2/tree/main/feature
It's quite clear that they use poorly thought-out cross-language codegen for this, which partly explains the lack of ergonomics, but also shows that they don't much care whether you use their stuff properly.
Yep, this is a common misunderstanding, and the blog post itself repeats it.
The only way to "pass the file contents" would be through the standard input stream, but the script might want to use stdin like normal, so this isn't an option.
In what way does Python have more null safety than Go? Using None will cause exceptions in basically all the same places using nil will cause panics in Go, and Python similarly lacks the usual null-safe operators like traversal (?.), coalescing (??), etc.
You can abuse the falsity of None to do things like `var or ""`, but this ground gets quite shaky when real bools get involved.
The interesting thing about Go's loopvar change is that nobody was able to demonstrate any real-world code that it broke (*1), while several examples were found of real-world code (often tests) that it fixed (*2). Nevertheless, they gated it behind go.mod specifying a go version >= 1.22, which I personally think is overly conservative.
*1: A great many examples of synthetic code were contrived to argue against the change, but none of them ever corresponded to Go code anyone would actually write organically, and an extensive period of investigation turned up nothing
*2: As in, the original behavior of the code was actually incorrect, but this wasn't discovered until after the loopvar change caused e.g. some tests to fail, prompting manual review of the relevant code; as a tangent, this raises the question of how often tests just conform to the code rather than the other way around
You certainly won't find me arguing against that change, and the conservatism is why I called it borderline. The only reason I bring it up is because of the "absolute non-negotiable" bit, which I took to probably indicate a very exacting standard lest it include most widespread languages anyways.
Yes, I think it's also a good example of how "absolute" backwards compatibility is not necessarily a good thing. Not only was the old loopvar behavior probably the biggest noob trap in Go (*), it turned out not to be what anyone writing Go code in the wild actually wanted, even people experienced with the language. Everyone seems to have: a) assumed it always worked the way it does now, b) wrote code that wasn't sensitive to it in the first place, or c) worked around it but never benefitted from it.
*: strongest competitor for "biggest noob trap" IMO is using defer in a loop/thinking defer is block scoped
Strongly agree there. IMO breaking backwards compatibility is a tradeoff like any other, and the flexibility non-hardline stances give you is handy for real-world situations,
I interpret this as asking "why can't you get the address of a value in a map?"
There are two reasons, and we could also ask "why can't you get the address of a key in a map?"
The first reason is flexibility in implementation. Maps are fairly opaque, their implementation details are some of the least exposed in the language (see also: channels), and this is done on purpose to discourage users of the language from mucking with the internals and thus making it harder for the developers of the language to change them. Denying access to internal pointers makes it a lot easier to change the implementation of a map.
The second reason is that most ways of implementing a map move the value around copiously. Supposing you could get a pointer p := &m[k] for some map m and key k, what would it even point to? Just the value position of a slot in a hash table. If you do delete(m, k) now what does it point to? If you assign m[k2] but hash(k2) == hash(k) and the map handles the collision by picking a new slot for k, now what does it point to? And eventually you may assign so many keys that the old hash table is too small and so a new one somewhere else in memory has to be allocated, leaving the pointer dangling.
While the above also apply to pointers-to-keys, there is another reason you can't get one of those: if you mutated the key, you would (with high probability) violate the core invariant of a hash table, namely that the slot for an entry is determined exactly by the hash of its key. The exact consequences of violating this would depend on the specific implementation, but they are mostly quite bad.
For comparison, Rust, with its strong control over mutability and lifetimes, can give you safe references to the entries of a HashMap in a way Go cannot.
I was burnt by the mutability of keys in go maps a few months ago, I'm not sure exactly how go handles it internally but it ended up with the map growing and duplicate keys in the key list when looking at it with a debugger.
The footgun was that url.QueryUnescape returned a slice of the original string if nothing needed to be escaped so if the original string was modified, it would modify the key in the map if you put the returned slice directly into the map.
This shouldn't be a race condition, reads were done by taking a RLock() from a mutex in a struct with the map, and defer RUnlock(), writes were similar where a Lock() was taken on the same mutex with a defer Unlock(). All these functions did was get/set values in the map and operated on a struct with just a mutex and the map. Unless I have a fundamental misunderstanding of how to use mutexes to avoid race conditions this shouldn't have been the case. This also feels a lot like a llm response with the Hypotheses section.
edit: this part below was originally a part of the comment I'm replying to
Hypotheses: you were modifying the map in another goroutine (do not share maps between goroutines unless they all treat it as read-only), the map implementation had some short-circuit logic for strings which was broken (file a bug report/it's probably already fixed), the debugger paused execution at an unsafe location (e.g. in the middle of non-user code), or the debugger incorrectly interpreted the contents of the map.
That was probably done by fiber[1] the code specifically took the param from it in the function passed to the Get(path string, handlers ...Handler) Router function. c is the *fiber.Ctx passed by fiber to the handler. My code took the string from c.Param("name") passed it to url.QueryUnescape then another function which had a mutex around setting the key/value in the map. I got the hint it was slices and something modifying the keys when I found truncated keys in the key list.
My guess is fiber used the same string for the param to avoid allocations. The fix for it is just to create a copy of the string with strings.Clone() to ensure it does not get mutated when it is used as a key. I understand it was an issue with my code, it just wasn't something I expected to be the case so it took several hours and using the debugger to find the root cause. Probably didn't help that a lot of the code was generated by Grok-4-Code/Sonic as a vibe coding test when I decided to go back a few months later and try and fix some of the issues I had myself.
And… I actually don't have an issue with it to be honest. I've done the same myself.
But this mutability should never escape. I'd never persist in using a library that would let it escape. But apparently… it's intentional: https://github.com/gofiber/fiber/issues/185
Oh well. You get what you ask for. Please don't complain about maps if you're using a broken library.
Two processes in particular have been this exact sort of problem for me: mds_stores and mediaanalysisd. On three separate Macs (all Apple Silicon), I've observed the case heating up whenever the computer is plugged in but not actively being used. Assuming Activity Monitor is more or less accurate, the culprit seems to be those two, who always have massive amounts of accumulated CPU time, but never seem to actually be using CPU when watched. I suspect, given what they supposedly do, that they're also needlessly exhausting SSD write cycles, but that's harder to analyze/prove. Naturally, they are also in the untouchable area of the file system. Completely disabling Spotlight, which you can do without disabling SIP, seems to always fix this problem, albeit at the cost of seriously decreased usability. I've also had mixed results with just limiting the categories of Spotlight indexing in System Settings.
reply