Conversation
| if !branch.Dirty { | ||
| branch.Dirty = true | ||
| // This triggers a branch record update | ||
| } |
There was a problem hiding this comment.
Too late to update it now. There's already an entry in staging. If another read request comes, it should be able to see it.
There was a problem hiding this comment.
You are absolutely right - we must first set dirty bit before any write
|
Out of curiosity - do we know how common this case is? (i.e. % of reads that go to "clean" branches?) Might help us understand if the added complexity is worth it. Perhaps a more granular approach could be beneficial: we can go further than a single boolean and maintain a small bitmap: one bit per range - essentially lakeFS' take on dirty pages. |
arielshaqed
left a comment
There was a problem hiding this comment.
This is a really great idea! Enhance the structure of branches to boost performance.
But not sure how this exact proposal can work correctly at a branch level - I think it will be easier at the token level. Requesting changes to understand how we know a branch is clean.
| 1. Attempts to read from the current staging token. | ||
| 2. Falls back to committed data if not found. | ||
|
|
||
| This happens **even when the branch has no staged changes**. |
There was a problem hiding this comment.
As a workaround, users can read lakefs://repo/branch@/. Obviously in order to do this the user needs to know that this is what they want. (Alternatively, this could be an easy way to show the performance difference!)
There was a problem hiding this comment.
We already know the performance difference as for example we have hard limits in DDB for read (3000/s) and writes (1000/s)
|
|
||
| ### High-level idea | ||
|
|
||
| Introduce a **branch-level boolean flag**: |
There was a problem hiding this comment.
An alternative might measure dirtiness per token. Doing this could additionally reduce read pressure during commits - when read pressure can anyway be higher.
There was a problem hiding this comment.
The read decision in Graveler is inherently branch-level: if any token (current or sealed) may contain entries, reads must consult staging. Tracking dirtiness per token doesn’t change that requirement and mostly adds state and maintenance complexity.
Commit-time read pressure is transient; the hot-partition issue we’re addressing is steady-state reads on clean branches. A branch-level dirty flag targets that directly with much lower risk.
|
|
||
| ``` | ||
| dirty = true ⟺ (StagingToken has entries) OR (SealedTokens is non-empty) | ||
| dirty = false ⟺ (StagingToken is empty) AND (SealedTokens is empty) |
There was a problem hiding this comment.
Intermediate cases exist! For instance while committing a clean branch (assume appropriate flags!) there is an empty staging token but non-empty sealed tokens. This proposal forces lakeFS to consider the branch dirty even though it could know it is clean.
The correctness requirement for dirty is that if there are changes to the branch then dirty``. An additional _performance_ requirement is that if dirty` then usually there are changes to the branch.
|
|
||
| For existing branches without the `dirty` field: | ||
|
|
||
| - **Default value: `true`** (conservative/safe) |
There was a problem hiding this comment.
The current version of Google protobufs has all fields optional. That means no default values. I therefore suggest using a clean field instead - the default ! false is precisely what we want.
There was a problem hiding this comment.
That's a good point. We can invert the logic accordingly without changing the design
| Operations that guarantee the absence of uncommitted changes set `dirty = false`. | ||
|
|
||
| Examples: | ||
| - successful commit (after sealed tokens are cleared) |
There was a problem hiding this comment.
I don't understand: writes can occur concurrently with the commit, and there might even be other concurrent commits. So there may be sealed tokens, or the staging token could already be dirty. You could work around the first, but I do not see how to work around the second.
(This may be an argument in favour of dirt-per-token.)
There was a problem hiding this comment.
Clearing dirty is not unconditional. It must be done via a conditional branch update that succeeds only if staging and sealed tokens are empty at that moment.
If a concurrent write or another commit introduces staged data, the condition fails and dirty remains true. False positives are acceptable; false negatives are not. This is the same concurrency pattern already used for commit ID and token rotation, and per-token dirtiness doesn’t eliminate the need for these conditional checks.
We don't really know how common this case is - this is the reason for suggesting the phased implementation which introduced the metrics to provide visibility.
I am worried the bitmap approach might add a lot of complexity to this solution and create additional dangerous pitfalls:
The branch-level dirty flag addresses the bottleneck (hot partitions) with far less complexity and risk. Additionally, this flag gives us a simple way to validate how frequently this scenario actually occurs before considering more granular optimizations |
Proposal for branch read optimization