BufFileSize() doesn't work on a "shared" BufFiles

From: Heikki Linnakangas <hlinnaka(at)iki(dot)fi>
To: pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>, Peter Geoghegan <pg(at)bowt(dot)ie>, Robert Haas <robertmhaas(at)gmail(dot)com>
Subject: BufFileSize() doesn't work on a "shared" BufFiles
Date: 2018-04-30 17:18:52
Message-ID: ea567a4a-dfda-a3d3-1561-0997b5b53516@iki.fi
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

I've been noodling around the tuplesort/logtape code, reviewing the
changes that were made in v11 to support parallel sorting, and also
rebasing my older patch to replace the polyphase merge with a simple
balanced merge.

Looking at TapeShare:

> /*
> * The approach tuplesort.c takes to parallel external sorts is that workers,
> * whose state is almost the same as independent serial sorts, are made to
> * produce a final materialized tape of sorted output in all cases. This is
> * frozen, just like any case requiring a final materialized tape. However,
> * there is one difference, which is that freezing will also export an
> * underlying shared fileset BufFile for sharing. Freezing produces TapeShare
> * metadata for the worker when this happens, which is passed along through
> * shared memory to leader.
> *
> * The leader process can then pass an array of TapeShare metadata (one per
> * worker participant) to LogicalTapeSetCreate(), alongside a handle to a
> * shared fileset, which is sufficient to construct a new logical tapeset that
> * consists of each of the tapes materialized by workers.
> *
> * Note that while logtape.c does create an empty leader tape at the end of the
> * tapeset in the leader case, it can never be written to due to a restriction
> * in the shared buffile infrastructure.
> */
> typedef struct TapeShare
> {
> /*
> * firstblocknumber is first block that should be read from materialized
> * tape.
> *
> * buffilesize is the size of associated BufFile following freezing.
> */
> long firstblocknumber;
> off_t buffilesize;
> } TapeShare;

Why is 'buffilesize' part of the exported state? The leader process can
easily call BufFileSize() itself, instead of having the worker process
pass it through shared memory, right? Wrong. I tried to change it that
way, and after some debugging, I realized that BufFileSize() doesn't
work if it's called on a "shared" BufFile. It always returns 0. That's
because it uses FileGetSize(), which in turn only works on a temporary
file. None of this is mentioned in the comments :-(.

Perhaps that would be OK, if it was properly commented. But it's not
actually hard to make BufFileSize() work on shared files, too, so I
think we should do that.

Another little bug I noticed is that BufFileAppend() incorrectly resets
the 'offsets' of the source files. You won't notice if you call
BufFileAppend() immediately after BufFileOpenShared(), but if you had
called BufFileSeek() or BufFileRead() on the shared BufFile handle
before calling BufFileAppend(), it would get confused.

I propose the attached patch.

- Heikki

Attachment Content-Type Size
0001-Fix-some-sloppiness-in-the-new-BufFileSize-and-BufFi.patch text/x-patch 6.0 KB

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Peter Eisentraut 2018-04-30 17:24:39 Re: Transform for pl/perl
Previous Message Tom Lane 2018-04-30 17:07:49 Re: "could not reattach to shared memory" on buildfarm member dory