The Internals of PostgreSQL
interdb.jp
interdb.jp
Just an anecdote with some fond memories that were made possible by Postgres and its internals. Postgres has a place in my heart for that, and being a damn fine DB of course!
We got an A on that first parser that was hand written as well, but once we got to DML, I felt like we could really use tools given our time frame.
Implementing your own WAL + storage API for example, is more something along the lines I would expect.
Those compilers classes turned out to be pretty useful. I ended up having to build custom parsers several times in my career... including an SQL parser.
If you're learning this stuff you need a simple challenge to work through the end.
With regards to lex and yacc, I absolut detest these tools. They are horrible but they do work. And if you just want a functioning parser they'll do. My main criticism of these tools are the horrible errors you might end up with and the lack of sensible extension points.
If it is your first foray into parsers, I do think a simple grammar and handwritten lexer/parser is a good first step. Iterate some on that, then use tools to help with the verbose stuff.
Though to be honest, I enjoy writing parsers by hand. So, I'm a bit biased.
The big thing for this particular project was all the pitfalls of C with having to track memory allocations and passing arrays of pointers around and mentally keeping track. Yacc streamlined this a lot. If this was a production project and not a class I took along side four other, hand writing would be a serious option I'd consider (mostly for error reporting purposes like you mentioned), but I might even lean towards getting an MVP done in a parser generator and eventually converting over to handwritten if the need arose.
What?
On my compilers class, I was explicitly asked to use lex and yacc. Sure, for the very first assignment on that class we were asked to write the lexer by hand. But when it came to actually do the interesting parts, lex and yacc it was.
Hand-written lexer/parser means you can be dumb as a doornail and just walk through the logic.
As an aside, it also gives you a better ability to write error messages, and an easier time debugging compared to autogenerated solutions.
This is not true. Instead of trying to use error recovery feature of parser genrator, you can just add error case to your grammar, and it works as well as hand-written parser. Parser generator error recovery is pure bonus on top.
> and an easier time debugging compared to autogenerated solutions
This is also not true. Most parser generators (certainly lex/yacc) have good enough debug support that you never see generated code while you debug.
> Whenever receiving a connection request from a client, it starts a backend process. (And then, the started backend process handles all queries issued by the connected client.)
> To achieve this [server] starts ("forks") a new process for each connection. From that point on, the client and the new server process communicate without intervention by the original postgres process. Thus, the master server process is always running, waiting for client connections, whereas client and associated server processes come and go. [1]
So, Postgres is using process-per-connection model. Can some explain why this is? And why not something like thread-per-connection?
Thread vs process under Linux is kind of a potato potato thing. They are essentially the same thing - based on the flags provided when you create the process, you can indicate if you want things like shared memory or not. But they are ultimately the same construct, with similar overhead. Plus, fork() is incredibly useful.
It makes sense, for such an important piece of software, to minimize shared memory.
E.g. why nginx is much faster and lighter than Apache and MySQL connections are cheaper and faster to create than Pg.
> DBMS code must run as a separate process from the application programs that access the database in order to provide data protection. The process structure can use one DBMS process per application program (i.e., a process-per-user model [STON81]) or one DBMS process for all application programs (i.e., a server model). The server model has many performance benefits (e.g., sharing of open file descriptors and buffers and optimized task switching and message send- ing overhead) in a large machine environment in which high performance is critical. However, this approach requires that a fairly complete special-purpose operating system be built. In constrast, the process-per-user model is simpler to implement but will not perform as well on most conventional operating systems. We decided after much soul searching to implement POSTGRES using a process-per-user model architecture because of our limited programming resources. POSTGRES is an ambitious undertaking and we believe the additional complexity introduced by the server architecture was not worth the additional risk of not getting the system running. Our current plan then is to implement POSTGRES as a process-per-user model on Unix 4.3 BSD.
(THE DESIGN OF POSTGRES, 1986, https://dsf.berkeley.edu/papers/ERL-M85-95.pdf )
> In POSTGRES they are run as subprocesses managed by the POSTMASTER. A last aspect of our design concerns the operating system process structure. Currently, POSTGRES runs as one process for each active user. This was done as an expedient to get a system operational as quickly as possible. We plan on converting POSTGRES to use lightweight processes available in the operating systems we are using. These include PRESTO for the Sequent Symmetry and threads in Version 4 of Sun/OS.
(The implementation of POSTGRES, 1990, https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.93... )
This is explained in their Wiki "Things we do NOT want"
https://wiki.postgresql.org/wiki/Todo#Features_We_Do_Not_Wan...
> All backends running as threads in a single process (not wanted)
> This eliminates the process protection we get from the current setup. Thread creation is usually the same overhead as process creation on modern systems, so it seems unwise to use a pure threaded model, and MySQL and DB2 have demonstrated that threads introduce as many issues as they solve. Threading specific operations such as I/O, seq scans, and connection management has been discussed and will probably be implemented to enable specific performance features.
Modern PG is already threaded out of necessity for many kinds of query, there's really no other way to fully exploit modern hardware (or indeed, much of the hardware released in the past 30 years).
[1] https://www.interdb.jp/pg/pgsql01.html#_1.3.
The internals of PostgreSQL - https://news.ycombinator.com/item?id=13488315 - Jan 2017 (53 comments)
The Internals of PostgreSQL - https://news.ycombinator.com/item?id=12142364 - July 2016 (1 comment)
> If you work at Amazon, you cannot use and refer to this document because of the copyright violation issues.
I wonder which one of the Amazon's missteps triggered the OP's ire. :)