[ 
https://issues.apache.org/jira/browse/CASSANDRA-21679?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Sam Lightfoot updated CASSANDRA-21679:
--------------------------------------
    Description: 
h4. Summary

`trickle_fsync` paces SSTable writeback with `fdatasync`, which also commits 
the ext4 journal, flushes the device cache on drives that have one, walks the 
whole file's dirty set, and most notably blocks the flush writer with no 
overlap between it and the device. None of that is required, durability is the 
commitlog's job and `SequentialWriter` fsyncs at `close()` regardless.

The `sync_file_range` syscall does only the pacing work. This changes the 
implementation beneath `trickle_fsync: true`; it does not add an option.
h4. What changes

At each interval, on Linux, two calls:
{code:java}
sync_file_range(fd, off_N, len, SYNC_FILE_RANGE_WRITE); // submit N, returns
sync_file_range(fd, off_N-1, len, SYNC_FILE_RANGE_WAIT_BEFORE); // wait for 
N-1{code}
Lagging the wait by one interval is the point. `fdatasync` waits on data it 
submitted in the same call, so producing and draining never overlap and every 
interval costs both. Waiting one behind offsets the wait by exactly one 
interval's generation time, N−1 has been in flight since the previous call, so 
an interval costs the larger of the two and the stall is `max(0, drain − 
produce)`.
 
That is zero whenever the device drains an interval faster than the writer 
generates one, which is the case here at 24–33% device utilisation: 15.3 s → 
1.0 s below, 93% of flush-writer stall removed, throughput and burst shaping 
unchanged. Where the device is the limit instead, the writer is paced by it, 
the backpressure `trickle_fsync` exists to provide. The writer runs at most one 
interval ahead either way, so the bound holds.


`sync_file_range` writes no metadata, so the close sync becomes the writer's 
only metadata commit.
h4. Measured

Single node, 10 MiB interval, ~411 MiB memtable flushes. Paired A/B, both arms 
in the same invocation, n=5, arms matched to 0.0001% on read count.
| |trickle off|`fdatasync`|this change|
|flush-writer stall|0.4 s|15.3 s|1.0 s|
|ext4 journal transactions|3493|6574 (+3081)|3497 |
|read p99 during flush|24.3 ms|11.4 ms|9.0 ms|
|system flush throughput|116.4 MiB/s|116.4|116.5|
|dirty-window peak|439 MiB|50.8|48.6|

Stall drops 14.8× with no overlap between arms (14.2–17.0 s vs 0.8–1.4 s). 
Flush-writer CPU is unchanged (69.7 vs 70.1 s), so that is time blocked, not 
work removed.

  was:
h4. Summary

`trickle_fsync` paces SSTable writeback with `fdatasync`, which also commits 
the ext4 journal, flushes the device cache on drives that have one, walks the 
whole file's dirty set, and most notably blocks the flush writer with no 
overlap between it and the device. None of that is required, durability is the 
commitlog's job and `SequentialWriter` fsyncs at `close()` regardless.

The `sync_file_range` syscall does only the pacing work. This changes the 
implementation beneath `trickle_fsync: true`; it does not add an option.
h4. What changes

At each interval, on Linux, two calls:
{code:java}
sync_file_range(fd, off_N, len, SYNC_FILE_RANGE_WRITE); // submit N, returns
sync_file_range(fd, off_N-1, len, SYNC_FILE_RANGE_WAIT_BEFORE); // wait for 
N-1{code}
Lagging the wait by one interval is the point. `fdatasync` waits on data it 
submitted in the same call, so producing and draining never overlap and every 
interval costs both. Waiting one behind offsets the wait by exactly one 
interval's generation time, N−1 has been in flight since the previous call, so 
an interval costs the larger of the two and the stall is `max(0, drain − 
produce)`.
 
That is zero whenever the device drains an interval faster than the writer 
generates one, which is the case here at 24–33% device utilisation: 15.3 s → 
1.0 s below, 93% of flush-writer stall removed, throughput and burst shaping 
unchanged. Where the device is the limit instead, the writer is paced by it, 
the backpressure `trickle_fsync` exists to provide. The writer runs at most one 
interval ahead either way, so the bound holds.
`sync_file_range` writes no metadata, so the close sync becomes the writer's 
only metadata commit.
h4. Measured

Single node, 10 MiB interval, ~411 MiB memtable flushes. Paired A/B, both arms 
in the same invocation, n=5, arms matched to 0.0001% on read count.
| |trickle off|`fdatasync`|this change|
|flush-writer stall|0.4 s|15.3 s|1.0 s|
|ext4 journal transactions|3493|6574 (+3081)|3497 |
|read p99 during flush|24.3 ms|11.4 ms|9.0 ms|
|system flush throughput|116.4 MiB/s|116.4|116.5|
|dirty-window peak|439 MiB|50.8|48.6|

Stall drops 14.8× with no overlap between arms (14.2–17.0 s vs 0.8–1.4 s). 
Flush-writer CPU is unchanged (69.7 vs 70.1 s), so that is time blocked, not 
work removed.


> Implement trickle_fsync with sync_file_range
> --------------------------------------------
>
>                 Key: CASSANDRA-21679
>                 URL: https://issues.apache.org/jira/browse/CASSANDRA-21679
>             Project: Apache Cassandra
>          Issue Type: Improvement
>          Components: Local/SSTable
>            Reporter: Sam Lightfoot
>            Assignee: Sam Lightfoot
>            Priority: Normal
>
> h4. Summary
> `trickle_fsync` paces SSTable writeback with `fdatasync`, which also commits 
> the ext4 journal, flushes the device cache on drives that have one, walks the 
> whole file's dirty set, and most notably blocks the flush writer with no 
> overlap between it and the device. None of that is required, durability is 
> the commitlog's job and `SequentialWriter` fsyncs at `close()` regardless.
> The `sync_file_range` syscall does only the pacing work. This changes the 
> implementation beneath `trickle_fsync: true`; it does not add an option.
> h4. What changes
> At each interval, on Linux, two calls:
> {code:java}
> sync_file_range(fd, off_N, len, SYNC_FILE_RANGE_WRITE); // submit N, returns
> sync_file_range(fd, off_N-1, len, SYNC_FILE_RANGE_WAIT_BEFORE); // wait for 
> N-1{code}
> Lagging the wait by one interval is the point. `fdatasync` waits on data it 
> submitted in the same call, so producing and draining never overlap and every 
> interval costs both. Waiting one behind offsets the wait by exactly one 
> interval's generation time, N−1 has been in flight since the previous call, 
> so an interval costs the larger of the two and the stall is `max(0, drain − 
> produce)`.
>  
> That is zero whenever the device drains an interval faster than the writer 
> generates one, which is the case here at 24–33% device utilisation: 15.3 s → 
> 1.0 s below, 93% of flush-writer stall removed, throughput and burst shaping 
> unchanged. Where the device is the limit instead, the writer is paced by it, 
> the backpressure `trickle_fsync` exists to provide. The writer runs at most 
> one interval ahead either way, so the bound holds.
> `sync_file_range` writes no metadata, so the close sync becomes the writer's 
> only metadata commit.
> h4. Measured
> Single node, 10 MiB interval, ~411 MiB memtable flushes. Paired A/B, both 
> arms in the same invocation, n=5, arms matched to 0.0001% on read count.
> | |trickle off|`fdatasync`|this change|
> |flush-writer stall|0.4 s|15.3 s|1.0 s|
> |ext4 journal transactions|3493|6574 (+3081)|3497 |
> |read p99 during flush|24.3 ms|11.4 ms|9.0 ms|
> |system flush throughput|116.4 MiB/s|116.4|116.5|
> |dirty-window peak|439 MiB|50.8|48.6|
> Stall drops 14.8× with no overlap between arms (14.2–17.0 s vs 0.8–1.4 s). 
> Flush-writer CPU is unchanged (69.7 vs 70.1 s), so that is time blocked, not 
> work removed.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to