Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ releases](https://github.com/lucaong/cubdb/releases).
Since `v1.0.0`, `CubDB` follows [semantic versioning](https://semver.org), and
reports changes here.

## Unreleased

- Add function `CubDB.writes_since_compaction/1` to get the number of writes since the last successful compaction

Bug fixes:

- Fix dirt calculation upon restart of `CubDB`

## v2.0.2 (2023-01-01)

Bug fixes:
Expand Down
28 changes: 28 additions & 0 deletions lib/cubdb.ex
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,27 @@ defmodule CubDB do
GenServer.call(db, :dirt_factor, :infinity)
end

@spec writes_since_compaction(server) :: non_neg_integer

@doc """
Returns the number of writes performed since the last compaction.

This number is reset to 0 after a compaction operation, and increases by one
on each write operation on the BTree. Deletions are considered writes, as they
modify the BTree. The way writes are computed depends on the specific BTree
implementation, and might not map one-to-one to the high level write
operations performed by the user. This metric is provided only as a hint in
case of manual compaction, where it is often a good idea to skip a wasteful
compaction if only a few write operations were performed since the last
compaction.

The number of writes does not include uncommitted operations, therefore old
halted transactions that are never committed are not counted.
"""
def writes_since_compaction(db) do
GenServer.call(db, :writes_since_compaction, :infinity)
end

@spec put(server, key, value) :: :ok

@doc """
Expand Down Expand Up @@ -1306,6 +1327,13 @@ defmodule CubDB do
{:reply, Btree.dirt_factor(btree), state}
end

def handle_call(:writes_since_compaction, _, state) do
%State{btree: btree} = state
%Btree{dirt: dirt} = btree

{:reply, dirt, state}
end

def handle_call(:start_transaction, from, state = %State{writer: nil}) do
%State{btree: btree} = state

Expand Down
2 changes: 1 addition & 1 deletion lib/cubdb/btree.ex
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ defmodule CubDB.Btree do
# updates won't be committed to the database and will be lost in case of a
# restart.
def commit(tree = %Btree{store: store, size: size, root_loc: root_loc, dirt: dirt}) do
Store.put_header(store, header(size: size, location: root_loc, dirt: dirt + 1))
Store.put_header(store, header(size: size, location: root_loc, dirt: dirt))
tree
end

Expand Down
23 changes: 23 additions & 0 deletions test/cubdb_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -984,13 +984,16 @@ defmodule CubDBTest do
{:ok, db} = CubDB.start_link(tmp_dir, auto_compact: {3, 0.3})

assert CubDB.dirt_factor(db) == 0
assert CubDB.writes_since_compaction(db) == 0

CubDB.subscribe(db)

CubDB.put(db, :a, 1)
assert CubDB.writes_since_compaction(db) == 1
refute_received :compaction_started

CubDB.put(db, :b, 2)
assert CubDB.writes_since_compaction(db) == 2
refute_received :compaction_started

CubDB.put(db, :a, 3)
Expand All @@ -1000,6 +1003,26 @@ defmodule CubDBTest do
refute_received :compaction_started
end

test "dirt calculation is consistent after restart", %{tmp_dir: tmp_dir} do
{:ok, db} = CubDB.start_link(data_dir: tmp_dir, auto_compact: false)

CubDB.put(db, "foo", 123)
CubDB.put(db, "bar", 234)
CubDB.delete(db, "foo")
CubDB.clear(db)

original_dirt_factor = CubDB.dirt_factor(db)
original_writes_since_compaction = CubDB.writes_since_compaction(db)

assert original_writes_since_compaction == 4

CubDB.stop(db)

{:ok, db} = CubDB.start_link(data_dir: tmp_dir, auto_compact: false)
assert original_dirt_factor == CubDB.dirt_factor(db)
assert original_writes_since_compaction == CubDB.writes_since_compaction(db)
end

test "auto compaction is active by default", %{tmp_dir: tmp_dir} do
{:ok, db} = CubDB.start_link(tmp_dir)

Expand Down
Loading