Course outline · 0% complete

0/28 lessons0%

Course overview →

Buffers and flushing

lesson 7-2 · ~11 min · 22/28

Where the write went

Here is a common and genuinely spooky surprise: a program calls write() on a file, and the data is not in the file yet.

Asking the OS to move bytes to disk is expensive, since unit 6 showed that giving up the CPU and waiting on hardware costs real time. So Python does not send every little write() to the OS. It collects writes in a buffer, a chunk of memory inside the process, and hands the whole batch over later.

Buffer is handed to the OS whenTrigger
the buffer fillsautomatic
flush() is calledexplicit
the file is closedautomatic, including at the end of a with block

Batching turns thousands of tiny expensive trips into a few big ones. The price is a window of time where the program thinks it wrote and the file has not changed, and that window is where a surprising number of production mysteries live.

A write nobody else can see yet

The file stays open while a second reader peeks at it.

f = open("buffered.txt", "w")
f.write("hello")

with open("buffered.txt", "r") as reader:
    print("before flush:", repr(reader.read()))

f.flush()
with open("buffered.txt", "r") as reader:
    print("after flush: ", repr(reader.read()))
f.close()

Output

before flush: ''
after flush:  'hello'

repr() shows the string with quotes, so an empty read is visibly '' rather than a blank line. The five bytes sat in the process's buffer, invisible to everyone else, until flush() pushed them out.

The reader here is a second file handle in the same program, but a completely separate process would see exactly the same nothing. The buffer lives in the writing process's private memory, which lesson 3-1 established nobody else can reach.

When this bites in real life

Crashes lose buffered data. If a program dies before flushing, the buffered writes never reach the file, which is why log lines sometimes vanish right before a crash, and they are exactly the lines that were needed.

Another process reads too early. Program A writes a status file, program B reads it and sees nothing, as in the demo above.

with open(...) is the fix for most cases. Closing a file flushes it, and the with block guarantees the close even on exceptions, which is the habit used throughout this course.

LayerHeld inForced by
Python bufferprocess memoryflush() or close
OS page cachekernel memoryos.fsync
diskthe devicethe write completing

One level deeper, flush() hands bytes to the OS, which keeps its own cache before the physical disk. Databases that absolutely cannot lose data call os.fsync to force that layer too, at the cost of real slowness. It is buffers all the way down, and each layer trades durability for speed.

your codef.write(...)Python bufferprocess memorykernel cacheOS memorywriteflush()fsynca crash loses everything still left of the diskclosing a file flushes it, which is why the with block is the habiteach arrow crossed is a system call, so batching is why buffers existdiskdurable
Bytes pass through a process buffer and the kernel cache before they are truly on disk.

Making a log line visible without closing the file

Real loggers keep the file open, so they need an explicit push.

f = open("log.txt", "w")
f.write("event 1\n")

f.flush()

with open("log.txt", "r") as r:
    print(repr(r.read()))

f.close()

Output

'event 1\n'

f.flush() sends the buffered bytes to the OS without closing the file, which is the only reason the reader below it sees anything.

This is the trade every logging library exposes as a setting. Flushing per record makes logs trustworthy during a crash and costs a system call on every line, while buffering is fast and can lose the tail of the log at the worst possible moment.

Why the last log lines vanished

When a script crashes and its final log lines are missing, the lines were most likely still in the write buffer and were lost when the process died before flushing.

Buffered writes live in process memory, and process memory evaporates on death. Lesson 1-1 established that RAM is volatile, and lesson 3-3 that dead processes are cleaned up entirely.

Where the log line wasSurvives a crash
in the Python bufferno
flushed to the OSyes, in almost all crashes
fsynced to diskyes, even on power loss

Loggers that must survive crashes flush after every record, or use unbuffered or line-buffered output. That is also why the most important log line, the one describing the failure, is the one most likely to be missing from a badly configured logger.