Skip to content
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

Use rust bindgen for reading/writing the PostgreSQL control file. #6

Closed
wants to merge 3 commits into from

Conversation

hlinnaka
Copy link
Contributor

No description provided.

@ericseppanen
Copy link
Contributor

My findings so far on the use of transmute:

Since ControlFileData contains bools, transmute from a byte array is unsound (if the byte contains anything but 0 or 1, undefined behavior will result). If we expand our use of bindgen further, I expect we'll discover more ways to trigger dangerous things. So I'd prefer to avoid transmute on anything auto-generated: it's not obvious what types are in there, now or in the future.

I hoped there would be a way to attach #[derive(Serialize, Deserialize)] to the generated structs, but this doesn't seem possible right now. There are a few crates out there that also have this problem, and they seem to be using regex workarounds (example).

I also looked around at some of the "safe transmute" crates and RFC 2981. While there is a lot of potential there, I didn't find anything that's able to solve this problem now.

Some possible ways forward:
a) Continue with transmute until we can figure out how to do something better. Perhaps add a big warning comment, to remind us to fix this before it gets out of hand.
b) Go back to hand-written structs. We can replace the encode/decode functions with #[derive(Serialize, Deserialize)] if we like.
c) Build a procedural macro that can apply #[derive(Serialize, Deserialize)].
d) Run the bindgen output through a text regex to insert Serialize, Deserialize in the right place.

(d) seems crude, but it's so much less work than (c) that I think it's probably the right way forward, unless we expect to only have a few C structs imported with bindgen, then handwritten is probably fine.

@hlinnaka
Copy link
Contributor Author

My findings so far on the use of transmute:

Since ControlFileData contains bools, transmute from a byte array is unsound (if the byte contains anything but 0 or 1, undefined behavior will result). If we expand our use of bindgen further, I expect we'll discover more ways to trigger dangerous things. So I'd prefer to avoid transmute on anything auto-generated: it's not obvious what types are in there, now or in the future.

Perhaps we could map Postgres bool to Rust's u8 instead of bool. Not sure how to tell bindgen to do that.

I hoped there would be a way to attach #[derive(Serialize, Deserialize)] to the generated structs, but this doesn't seem possible right now. There are a few crates out there that also have this problem, and they seem to be using regex workarounds (example).

Yeah, that would've been nice.

I also looked around at some of the "safe transmute" crates and RFC 2981. While there is a lot of potential there, I didn't find anything that's able to solve this problem now.

Some possible ways forward:
a) Continue with transmute until we can figure out how to do something better. Perhaps add a big warning comment, to remind us to fix this before it gets out of hand.
b) Go back to hand-written structs. We can replace the encode/decode functions with #[derive(Serialize, Deserialize)] if we like.
c) Build a procedural macro that can apply #[derive(Serialize, Deserialize)].
d) Run the bindgen output through a text regex to insert Serialize, Deserialize in the right place.

(d) seems crude, but it's so much less work than (c) that I think it's probably the right way forward, unless we expect to only have a few C structs imported with bindgen, then handwritten is probably fine.

I'm inclined to do a) for now, and revisit this and perhaps switch to d) if we need many more of these structs. If we're lucky, this gets addressed in bindgen in the meanwhile.

We have a few hand-written structs in the WAL decoding routines that mirror Postgres structs, but those are fairly short and we only need to read them, not write. But if we had a more general solution, would be nice to use it for those too.

@ericseppanen
Copy link
Contributor

ericseppanen commented Apr 19, 2021

Perhaps we could map Postgres bool to Rust's u8 instead of bool. Not sure how to tell bindgen to do that.

I looked for a way to tell bindgen to remap bool to u8 and didn't find one. I think we'd have to write something to post-process the entire bindgen output file. Or trick it by patching the input file with #define bool uint8_t or something like that.

@ericseppanen
Copy link
Contributor

Do we want to be able to convert &[u8] to &ControlFileData (and vice versa)?

Right now it looks like there's a copy happening, unless the compiler can optimize it away.

@ericseppanen
Copy link
Contributor

There's an (undocumented?) standard practice in Rust, which is that whenever you use unsafe, you add a SAFETY comment explaining why this code can't cause bad things to happen. An example from the standard library:

        // SAFETY: the align is guaranteed by Rust to be a power of two and
        // the size+align combo is guaranteed to fit in our address space. As a
        // result use the unchecked constructor here to avoid inserting code
        // that panics if it isn't optimized well enough.
        unsafe { Layout::from_size_align_unchecked(size, align) }

@ericseppanen ericseppanen mentioned this pull request Apr 24, 2021
@ericseppanen
Copy link
Contributor

Should this be closed? The code was merged a while ago under another PR.

@hlinnaka
Copy link
Contributor Author

Should this be closed? The code was merged a while ago under another PR.

Yeah, I'll close this. We had some good discussion and suggestion here, but nothing concrete pending.

@hlinnaka hlinnaka closed this May 11, 2021
@hlinnaka hlinnaka deleted the controlfile-bindgen branch May 11, 2021 08:38
knizhnik added a commit that referenced this pull request Apr 4, 2024
## Problem

Running test_pageserver_restarts_under_workload in POR #7275 I get the
following assertion failure in prefetch:
```
#5  0x00005587220d4bf0 in ExceptionalCondition (
    conditionName=0x7fbf24d003c8 "(ring_index) < MyPState->ring_unused && (ring_index) >= MyPState->ring_last", 
    fileName=0x7fbf24d00240 "/home/knizhnik/neon.main//pgxn/neon/pagestore_smgr.c", lineNumber=644)
    at /home/knizhnik/neon.main//vendor/postgres-v16/src/backend/utils/error/assert.c:66
#6  0x00007fbf24cebc9b in prefetch_set_unused (ring_index=1509) at /home/knizhnik/neon.main//pgxn/neon/pagestore_smgr.c:644
#7  0x00007fbf24cec613 in prefetch_register_buffer (tag=..., force_latest=0x0, force_lsn=0x0)
    at /home/knizhnik/neon.main//pgxn/neon/pagestore_smgr.c:891
#8  0x00007fbf24cef21e in neon_prefetch (reln=0x5587233b7388, forknum=MAIN_FORKNUM, blocknum=14110)
    at /home/knizhnik/neon.main//pgxn/neon/pagestore_smgr.c:2055

(gdb) p ring_index
$1 = 1509
(gdb) p MyPState->ring_unused
$2 = 1636
(gdb) p MyPState->ring_last
$3 = 1636
```

## Summary of changes

Check status of `prefetch_wait_for`

## Checklist before requesting a review

- [ ] I have performed a self-review of my code.
- [ ] If it is a core feature, I have added thorough tests.
- [ ] Do we need to implement analytics? if so did you add the relevant
metrics to the dashboard?
- [ ] If this PR requires public announcement, mark it with
/release-notes label and add several sentences in this section.

## Checklist before merging

- [ ] Do not forget to reformat commit message to not include the above
checklist

---------

Co-authored-by: Konstantin Knizhnik <knizhnik@neon.tech>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants