HNHacker News
TopNewBestAskShowJobs

anarazel

6,287 karma · joined April 14, 2015

Andres Freund, PostgreSQL Hacker & Committer.

andres [at] hn dot anarazel dot de

submissionscomments
anarazel··on How do databases execute expressions?
Another related aspect is that the VM approach can allow JITing only some of the opcodes, which makes it more feasible and maintainable.
anarazel··on How do databases execute expressions?
I did the switch from a tree walk interpreter to a VM in postgres - it did lead to substantial speedups.

I've experimented with lowering into x86 "manually" as well, and it did provide gains, but the portability aspects are concerning. And for much of the desirable speedups you ime want more optimization than trivial lowering would give you.

Either way, without the tree walk -> VM move, it'd have been much harder to lower into x86 directly. And you'd still need a fallback for other platforms.

anarazel··on PostgreSQL 16
Why are you regularly doing vacuum full instead of just vacuuming more aggressively?
anarazel··on PostgreSQL 16
I think the 300% item is "Allow more efficient addition of heap and index pages". The source of the improvement is a number of related improvements around relation extension, see https://postgr.es/m/20221029025420.eplyow6k7tgu6he3@awork3.a...
anarazel··on Securing Your PostgreSQL DB with Roles and Privileges
Either. What the best approach is depends a bit on your needs / security model.

You can e.g. something like storing the session "application user" in a configuration variable (SET myapp.rls_user =...). But if the user can influence the SQL and that's part of the threat model, you need to do more, because that could be changed by further SQL.

Another solution is to just have a session level temp table indicating the current application user.

anarazel··on Securing Your PostgreSQL DB with Roles and Privileges
FWIW, you don't need to use database roles if you want to use RLS. You can instead have some other context indicating the current "application user" and use that in your RLS policies.
anarazel··on Downfall Attacks
Yes, it's certainly easier to mitigate at a boundary that's already as costly as switching between VMs.

The paper documents that disabling SMT does not entirely mitigate the problem (In 9.1). They briefly mention trying instructions to avoid the microarchitectural leaks, but don't go into more detail than mentioning verw isn't sufficient.

They state that a switch to/from SGX, with SMT disabled, doesn't prevent the attacks. See 8.1. That's not the same as a cross-vm switch, but it's certainly interesting that the attempts at flushing microarchitectural state when exiting SGX don't provide protection.

anarazel··on Downfall Attacks
It only seems to document that there's group scheduling for SMT cores. But that doesn't prevent issues due to switching between customers on the same physical core, no?

"It is possible, however, for two burstable performance EC2 instances to run sequentially (not simultaneously) on the same core. It is also possible for physical memory pages to be reused, remapped, and swapped in and out as virtual memory pages. However, even burstable instances never share the same core at the same time, and virtual memory pages are never shared across instances. "

I only started reading the paper - so I very well might be wrong here - but it doesn't look to me like you need victim/attacker to be scheduled simultaneously on two SMT threads, but that a single core sequentially executing victim / attacker code would be vulnerable. It's possible that the cross-customer "context switch" is larger than the the vulnerable window, but I'd not want to bet on it.

anarazel··on Zenbleed
I think there's several levels. As a first step, I'd appreciate reducing the risk of javascript extracting contents from outside the browser. A second step could be to use more granular core scheduling within firefox, to prevent sharing cores that shouldn't be shared. A process/thread hierarchy can create multiple core scheduling groups.
anarazel··on Threads and Goroutines
Indeed. Making the program wait (using a barrier) after all the threads have started, I see a a substantially higher memory usage than reported by time. I only used 100k threads. For me time reports 1018376kB, but looking at /proc/meminfo I see a difference of 5231092kB - so about 52kB per thread. That's a far cry from 8MB, but also not 8kB.

/proc/slabinfo shows some drastic differences:

slabtop -o|head -n 15

with 100k threads running:

   Active / Total Objects (% used)    : 10134567 / 10280497 (98.6%)
   Active / Total Slabs (% used)      : 337756 / 337756 (100.0%)
   Active / Total Caches (% used)     : 132 / 175 (75.4%)
   Active / Total Size (% used)       : 3598333.95K / 3674904.09K (97.9%)
   Minimum / Average / Maximum Object : 0.01K / 0.36K / 11.81K

    OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
  3419013 3418579  99%    0.10K  87667       39    350668K buffer_head
  1485372 1484947  99%    0.19K  35366       42    282928K dentry
  635299 635038  99%    0.68K  13517       47    432544K proc_inode_cache
  575792 575708  99%    1.14K  20564       28    658048K ext4_inode_cache
  514794 514703  99%    0.04K   5047      102     20188K extent_status
  503140 502198  99%    0.18K  11435       44     91480K vm_area_struct
  501738 500514  99%    0.04K   4919      102     19676K vma_lock
  381304 378068  99%    0.57K  13618       28    217888K radix_tree_node
without:

   Active / Total Objects (% used)    : 7235070 / 7678039 (94.2%)
   Active / Total Slabs (% used)      : 187813 / 187813 (100.0%)
   Active / Total Caches (% used)     : 132 / 175 (75.4%)
   Active / Total Size (% used)       : 1751469.45K / 1851083.31K (94.6%)
   Minimum / Average / Maximum Object : 0.01K / 0.24K / 11.81K

    OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
  3419013 3418579  99%    0.10K  87667       39    350668K buffer_head
  898296 887716  98%    0.19K  21388       42    171104K dentry
  575792 575708  99%    1.14K  20564       28    658048K ext4_inode_cache
  514794 514703  99%    0.04K   5047      102     20188K extent_status
  381304 376987  98%    0.57K  13618       28    217888K radix_tree_node
  164352 164352 100%    0.06K   2568       64     10272K kmalloc-rcl-64
  157360  53925  34%    0.07K   2810       56     11240K vmap_area
  139638 109093  78%    0.04K   1369      102      5476K vma_lock
anarazel··on Threads and Goroutines
I don't think the 8KB calculation is quite right - there are allocations in the kernel for every thread as well, and I don't think that'll be accounted for by /use/bin/time.
anarazel··on Zenbleed
What does that chicken bit do?
anarazel··on Zenbleed
I wish Firefox would use PR_SCHED_CORE to reduce the likelihood of such leakage...
anarazel··on Show HN: Using C++23 <stacktrace> to get proper crash logs in C++ programs
It was more a rethorical doubting because I did not want to look the standard up on my phone....
anarazel··on Show HN: Using C++23 <stacktrace> to get proper crash logs in C++ programs
That assumes the specified allocator actually controls all allocations - somewhat doubtful across all platforms as things like dl_iterate_phdr() IIRC allocate memory (and take locks). And there's a lot more to writing signal safe code than not calling malloc. Unless an interface documents to be signal safe you're take better of assuming it is not.

FWIW I've run into malloc self dreadlocks due to rare signals plenty of time :(. In production workloads.

anarazel··on Show HN: Using C++23 <stacktrace> to get proper crash logs in C++ programs
The crashing thread might hold a lock in the memory allocator - which could either self deadlock when saving the stack trace (which certainly seems to do memory allocation and thus isn't a-signal safe), or could deadlock with the crash reporting thread which definitely allocates memory all over.

I also quite doubt that std::mutex, and even more so std::condition_variable are guaranteed to be signal safe.

anarazel··on Show HN: Using C++23 <stacktrace> to get proper crash logs in C++ programs
This does not even remotely look to be signal safe to me?
anarazel··on Show HN: Using C++23 <stacktrace> to get proper crash logs in C++ programs
Ime the execinfo.h backtraces are unfortunately not that useful in practice, due to being unable to resolve symbol names of static functions. But FWIW, you can use it for async signals with the _fd variant.
anarazel··on Parsing time stamps faster with SIMD instructions
FWIW, the last time I measured the string handling part was far slower than the timezone conversion when loading timestamptz data in postgres.
anarazel··on PostgreSQL reconsiders its process-based model
> > Of course it's even better to not block at all...

> Out of curiosity, is that something you ever want/hope to achieve in PostgreSQL? Many high-performance systems use this model, but switching a synchronous system in plain C to it sounds uncomfortably exciting, both in terms of the delta and the additional complexity of maintaining the result. To me it seems like a much riskier change than the process->thread one discussed here that Tom Lane already stated will be a disaster.

Depends on how you define it. In a lot of scenarios you can avoid blocking by scheduling IO in a smart way - and I think we can quite far towards that for a lot of workloads and the wins are substantial. But that obviously cannot alone guarantee that you never block.

I think we can get quite far avoiding blocking, but I don't think we're going to a complete asynchronous model in the foreseeable future. But it seems more feasible to incrementally make common blocking locations support asynchronicity. E.g. when a query scans multiple partitions, switch to processing a different partition while waiting for IO.

> Is having a large number of rings (as when supporting a large number of incoming connections) practical? I'm thinking of each ring being a significant reserved block of RAM, but maybe in this scenario that's not really true. A smallish ring for a smallish number of IOs for the query is enough.

It depends on the kernel version etc. The amount of memory isn't huge but initially it was affected by RLIMIT_MEMLOCK... That's one reason why the AIO patchset has a smaller number of io_uring "instances" than the allowed connections. The other reason is that we need to be able to complete IOs that other backends started (otherwise there would be deadlocks), which in turn requires having the file descriptor for each ring available in all processes... Which wouldn't be fun with a high max_connections.

> Speaking of large number of incoming connections, would/could the process->thread be a step toward having a thread per active query rather than per (potentially idle) connection?

Yes. Moving to threads really mainly would be to make subsequent improvements more realistic...

> That'd be a nice operational improvement if it meant folks no longer needed a pooler [1] to get decent performance. All else being equal, fewer moving parts is more pleasant...

You'd likely often still want a pooler on the "application server" side, to avoid TCP / SSL connection establishment overhead. But that can be a quite simple implementation.

anarazel··on PostgreSQL reconsiders its process-based model
Yea. Just about none of them could be optimized to constants because, uh, they're not constant. We're not perfect, but we do add const etc to TU level statics/globals that are actually read only. And if they are actually read only, we don't care about them in the context of threading anyway, since they wouldn't need any different behaviour anyway.
anarazel··on PostgreSQL reconsiders its process-based model
> io_uring is designed for systems that have a thread and ring per core

That's not needed to benefit from io_uring

> for you to give it a bunch of IO to do at once (batches and chains), and your threads to do other work in the meantime.

You can see substantial gains even if you just submit multiple IOs at once, and then block waiting for any of them to complete. The cost of blocking on IO is amortized to some degree over multiple IOs. Of course it's even better to not block at all...

> If your code is instead designed to be synchronous and thus can only do one IO at a time and needs a syscall to block on it, I don't think there's much if any benefit in using io_uring.

We/I have done the work to issue multiple IOs at a time as part of the patchset introducing AIO support (with among others, an io_uring backend). There's definitely more to do, particularly around index scans, but ...

anarazel··on PostgreSQL reconsiders its process-based model
> That said, I think there have been efforts to use io_uring on Linux. I'm not sure how that would work with the process per connection model. Haven't been following it...

There's some minor details that are easier with threads in that context, but on the whole it doesn't make much of a difference.

anarazel··on PostgreSQL reconsiders its process-based model
Imo unikernels are a complicated solution in search of a problem, which turns out to not exist.

There certainly are times OSs get in the way. But it's hard enough to write a good database, we don't need to maintain a third of an OS in addition.

anarazel··on PostgreSQL reconsiders its process-based model
The TLB issue are more a hardware issue than a software/OS one. To my knowledge neither x86 nor arm provide a way to partially share TLB contents between processes/entities. Tlb entries can be tagged with a process context identifier, but that's an all or nothing thing. Either the entire address space is shared, or it's not.

> Yet rewrites are always easier and more sexy. At first.

Moving to threads would not at all be a rewrite.

anarazel··on PostgreSQL reconsiders its process-based model
Not who you asked, but: He is a longtime contributor who has written/resigned important parts of postgres (WAL format, concurrent WAL insertion, 2PC support, parts of SSI support, much more). And he is just a nice person to work with.
anarazel··on PostgreSQL reconsiders its process-based model
What makes you think that it will require that many changes? There will be some widespread mechanical changes (which can be verified to be complete with a bit of low level work, like a script using objdump/nm to look for non-TLS mutable variables) and some areas changing more heavily (e.g. connection establishment, crash detection, signal handling, minor details of the locking code). But large portions of the code won't need to change. Note that we/postgres already shares a lot of state across processes.
anarazel··on PostgreSQL reconsiders its process-based model
We do fix crashes etc, even if the postgres manages to restart.

I think the post upthread references an out-of-core extension we don't control, which in turn depends on many external libraries it doesn't control either...

anarazel··on PostgreSQL reconsiders its process-based model
The issue is costlier (runtime, complexity, memory) resource sharing, not the cost of the fork itself. Pre-forking isn't going to help with any of that.
anarazel··on PostgreSQL reconsiders its process-based model
Hard to believe that would provide any benefit without also causing massive slowdowns.
← PreviousPage 7 of 34Next →