lithos 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7741f8dffd9dd5d4d0f710b18fca0a7b1a0d574e893d60c9f5735c36a93954d6
4
- data.tar.gz: 6d4eb49a96a6cb44a97587f997905cc77c4e93bd4c33f64579db48754180b7d8
3
+ metadata.gz: 010db6b8b65ba9a4c32abe95c6d58dfc11a9e8a50e9599aaca10b32460a545dc
4
+ data.tar.gz: 912268f45027878b57a7cf0629c8b7efac5043f1df9e02f7855e91e2bbbf0d2f
5
5
  SHA512:
6
- metadata.gz: 6d24765cfbfdc9d6394eacd84081eeba1891f9f8423d2c3665cb7270e417e0cfe4d3e147b79e97607cc1ed6c56d96894805c13a88db2957de37fe350401f60bb
7
- data.tar.gz: 4af63db49e563dc16b3411f44a238b86174336b532f1caa6ab478c8e7d68b8282e3ec8d291eeba821177bdda0d0ab654eeaca2a61b4eaf4d735797d40d244883
6
+ metadata.gz: ee08a3bac4e558814df30ae9c92a9a119912be3be89828755bb85186b68570cd86d32f2746f195ec4d7edd48ceb0e7628918f42fb6a1784875cdf7978c95690c
7
+ data.tar.gz: adc8d75678a6625460996dd376021ec4ed18101982015ac2ae96344df6533ba57e465aaca9ab6f353585dfeb8a53af10187de71ea6f1b638aa643456ceb9402c
data/README.md CHANGED
@@ -1,101 +1,11 @@
1
- # lithos
2
-
3
- **A small embedded, ordered, crash-safe key-value store for Ruby — no external database.**
4
-
5
- `lithos` is a from-scratch storage engine: a **log-structured merge (LSM) tree**,
6
- the same design behind LevelDB/RocksDB, written as a native Ruby extension with
7
- **zero external dependency**. You get:
8
-
9
- - **Durable writes** — every `put`/`delete` is appended to a CRC-checked
10
- write-ahead log and `fsync`'d before it returns (in the default `sync:` mode).
11
- - **Crash recovery** — on open, the WAL is replayed; a torn or corrupted tail is
12
- detected and discarded.
13
- - **Ordered keys** — keys are stored sorted (unsigned-byte order), so you get
14
- `each`, `each_key`, and range `scan`s for free.
15
- - **Binary safe** — keys and values are arbitrary byte strings (embedded NULs OK).
16
-
17
- The niche it fills: every other ordered + crash-safe Ruby KV store is a binding
18
- to an external C/C++ library (LMDB/LevelDB/RocksDB) that's a pain to build on a
19
- native-MSVC Windows Ruby. `lithos` is self-contained and builds with
20
- [`vcvars`](https://rubygems.org/gems/vcvars) — no prebuilt database needed.
21
-
22
- ## Requirements
23
-
24
- - **Windows** with a native **MSVC (mswin)** Ruby. Not supported on MinGW/UCRT.
25
- - Visual Studio 2017+ / Build Tools with the **Desktop development with C++** workload.
26
-
27
- ## Install
28
-
29
- ```sh
30
- gem install lithos
31
- ```
32
-
33
- ## Usage
34
-
35
- ```ruby
36
- require "lithos"
37
-
38
- Lithos.open("data/mydb") do |db|
39
- db["alpha"] = "one"
40
- db.put("beta", "two")
41
- db.put("\x00raw\xff", "binary ok") # arbitrary binary keys + values
42
-
43
- db["alpha"] # => "one"
44
- db["missing"] # => nil
45
- db.key?("beta") # => true
46
- db.fetch("nope", "default") # => "default" (or raises KeyError)
47
- db.delete("beta") # => true (existed)
48
-
49
- # ordered iteration (ascending unsigned-byte key order)
50
- db.each { |k, v| puts "#{k}=#{v}" }
51
- db.each_key { |k| puts k }
52
-
53
- # ordered range scans
54
- db.scan(gte: "a", lt: "m") { |k, v| ... } # gt/lt exclusive, gte/lte inclusive
55
- db.range("a", "m") { |k, v| ... } # half-open [a, m)
56
- db.scan(gte: "a").to_a # Enumerator without a block
57
-
58
- db.size # live key count
59
- db.flush # seal the memtable into an SSTable
60
- db.compact # merge SSTables, dropping shadowed keys + tombstones
61
- end # closed automatically (flush + fsync)
62
- ```
63
-
64
- Open without a block when you want to manage the lifetime yourself:
65
-
66
- ```ruby
67
- db = Lithos.open("data/mydb", sync: true)
68
- db["k"] = "v"
69
- db.close
70
- ```
71
-
72
- Data persists across reopens, and survives a crash (process kill, power loss at
73
- the OS-flush boundary): reopening replays the WAL and discards any partial tail.
74
-
75
- ## How it works
76
-
77
- ```
78
- put/delete ──> WAL (append + CRC + fsync) ──> memtable (sorted std::map)
79
- │ (size threshold)
80
- ▼
81
- SSTable (immutable, mmap'd:
82
- sorted data + bloom + sparse index)
83
- get ──> memtable ──> SSTables newest→oldest (bloom-filtered)
84
- compact ──> k-way merge of all SSTables ──> one SSTable (drops tombstones)
85
- ```
86
-
87
- The set of live SSTables + the active WAL is recorded in a `MANIFEST` that is
88
- rewritten atomically (temp file + `FlushFileBuffers` + atomic rename), so the
89
- on-disk catalog is never observed half-updated.
90
-
91
- ## Notes
92
-
93
- - **Durability vs speed:** `sync: true` (default) `fsync`s every write. For bulk
94
- loads, `Lithos.open(path, sync: false)` skips per-write `fsync` (call `#flush`
95
- or `#close` to make data durable) — faster, but a crash can lose recent writes.
96
- - **Single writer:** a store takes an exclusive directory lock on open; it is not
97
- safe to share one store across threads (give each thread its own, or use a Mutex).
98
-
99
- ## License
100
-
101
- [MIT](LICENSE.txt).
1
+ # lithos (discontinued)
2
+
3
+ **lithos is no longer maintained.** This final release (0.1.1) contains no
4
+ code: installing it compiles nothing, and `require "lithos"` raises a
5
+ `LoadError` explaining the discontinuation.
6
+
7
+ Use sqlite3, or PStore from the standard library instead.
8
+
9
+ Earlier releases remain on RubyGems only because RubyGems does not allow
10
+ versions older than 30 days to be yanked. They are unmaintained and not
11
+ recommended.
data/lib/lithos.rb CHANGED
@@ -1,170 +1,4 @@
1
- # frozen_string_literal: true
2
-
3
- require "lithos/version"
4
- require "lithos/lithos" # native extension: defines Lithos::DB + Lithos::Error
5
-
6
- # lithos — a small embedded, ordered, crash-safe key-value store.
7
- #
8
- # Lithos.open("data/db") do |db|
9
- # db["alpha"] = "1"
10
- # db.put("\x00bin\xff", "raw") # binary keys/values welcome
11
- # db["alpha"] # => "1"
12
- # db.scan(gte: "a", lt: "m") { |k, v| } # ordered range
13
- # end # auto-closed (flushes + fsyncs)
14
- module Lithos
15
- # Open a store at `path` (a directory, created if missing). Options:
16
- # sync: durably fsync each write before it returns (default true)
17
- # memtable_bytes: flush the in-memory table to an SSTable past this size
18
- #
19
- # With a block: yields the store, closes it when the block returns (even on
20
- # error), and returns the block's value. Without a block: returns the store,
21
- # and the caller must #close it.
22
- def self.open(path, **opts)
23
- db = DB.new(path, **opts)
24
- return db unless block_given?
25
-
26
- begin
27
- yield db
28
- ensure
29
- db.close
30
- end
31
- end
32
-
33
- class DB
34
- include Enumerable
35
-
36
- # --- mutators (forbidden while an each/scan is in progress) --------------
37
- # The merge iterator holds raw pointers into memory-mapped SSTables and live
38
- # memtable iterators; mutating mid-scan would free them underneath it. We
39
- # refuse here with a plain Ruby exception (which unwinds safely), rather than
40
- # let it reach the C engine during iteration.
41
-
42
- def put(key, value)
43
- forbid_during_iteration!
44
- _put(key, value)
45
- end
46
-
47
- def delete(key)
48
- forbid_during_iteration!
49
- _delete(key)
50
- end
51
-
52
- def flush
53
- forbid_during_iteration!
54
- _flush
55
- end
56
-
57
- def compact
58
- forbid_during_iteration!
59
- _compact
60
- end
61
-
62
- def close
63
- forbid_during_iteration!
64
- _close
65
- end
66
-
67
- # Hash-like access. `db[k]` => value or nil; `db[k] = v` stores v.
68
- def [](key)
69
- get(key)
70
- end
71
-
72
- def []=(key, value)
73
- put(key, value)
74
- value
75
- end
76
-
77
- # Like Hash#fetch: return the value, else the block result, else the default,
78
- # else raise KeyError. (Stored values are never nil, so nil == absent.)
79
- def fetch(key, *default)
80
- if default.size > 1
81
- raise ArgumentError, "wrong number of arguments (given #{default.size + 1}, expected 1..2)"
82
- end
83
-
84
- value = get(key)
85
- return value unless value.nil?
86
- return yield(key) if block_given?
87
- return default.first unless default.empty?
88
-
89
- raise KeyError, "key not found: #{key.inspect}"
90
- end
91
-
92
- alias include? key?
93
- alias has_key? key?
94
- alias member? key?
95
-
96
- # Iterate all pairs in ascending (unsigned-byte) key order.
97
- def each(&block)
98
- return enum_for(:each) unless block
99
-
100
- bounded_each(nil, false, nil, false, &block)
101
- end
102
- alias each_pair each
103
-
104
- # Iterate keys only, ascending.
105
- def each_key
106
- return enum_for(:each_key) unless block_given?
107
-
108
- bounded_each(nil, false, nil, false) { |k, _| yield k }
109
- end
110
-
111
- # Ordered range scan. Bounds are optional; gt/lt are exclusive, gte/lte
112
- # inclusive. Returns an Enumerator without a block.
113
- def scan(gte: nil, gt: nil, lte: nil, lt: nil, &block)
114
- lower, lower_incl = gt ? [gt, false] : (gte ? [gte, true] : [nil, false])
115
- upper, upper_incl = lt ? [lt, false] : (lte ? [lte, true] : [nil, false])
116
- bounded_each(lower, lower_incl, upper, upper_incl, &block)
117
- end
118
-
119
- # Half-open range [from, to) in ascending order.
120
- def range(from, to, &block)
121
- bounded_each(from, true, to, false, &block)
122
- end
123
-
124
- # Number of live keys. O(n) — counts via an ordered scan.
125
- def size
126
- n = 0
127
- each_key { n += 1 }
128
- n
129
- end
130
- alias length size
131
-
132
- def empty?
133
- each { return false }
134
- true
135
- end
136
-
137
- def to_h
138
- h = {}
139
- each { |k, v| h[k] = v }
140
- h
141
- end
142
-
143
- def inspect
144
- "#<Lithos::DB path=#{path.inspect}#{closed? ? ' (closed)' : ''}>"
145
- end
146
-
147
- private
148
-
149
- def forbid_during_iteration!
150
- return unless (@iterating || 0).positive?
151
-
152
- raise Error, "lithos: cannot modify the store during iteration"
153
- end
154
-
155
- # Shared range driver; positional args so enum_for forwards cleanly. The
156
- # @iterating depth lets reads nest while mutations are blocked, and the
157
- # ensure guarantees the count is restored even on break/exception.
158
- def bounded_each(lower, lower_incl, upper, upper_incl, &block)
159
- return enum_for(:bounded_each, lower, lower_incl, upper, upper_incl) unless block
160
-
161
- @iterating = (@iterating || 0) + 1
162
- begin
163
- _each_range(lower, lower_incl, upper, upper_incl, &block)
164
- ensure
165
- @iterating -= 1
166
- end
167
- self
168
- end
169
- end
170
- end
1
+ # frozen_string_literal: true
2
+
3
+ raise LoadError, "lithos is discontinued and this release contains no code. Use sqlite3, or PStore from the standard library instead." \
4
+ " Earlier lithos releases are unmaintained and not recommended."
metadata CHANGED
@@ -1,104 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lithos
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ned
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies:
12
- - !ruby/object:Gem::Dependency
13
- name: rake
14
- requirement: !ruby/object:Gem::Requirement
15
- requirements:
16
- - - "~>"
17
- - !ruby/object:Gem::Version
18
- version: '13.0'
19
- type: :development
20
- prerelease: false
21
- version_requirements: !ruby/object:Gem::Requirement
22
- requirements:
23
- - - "~>"
24
- - !ruby/object:Gem::Version
25
- version: '13.0'
26
- - !ruby/object:Gem::Dependency
27
- name: rake-compiler
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: '1.2'
33
- type: :development
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '1.2'
40
- - !ruby/object:Gem::Dependency
41
- name: minitest
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '5.0'
47
- type: :development
48
- prerelease: false
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '5.0'
54
- - !ruby/object:Gem::Dependency
55
- name: vcvars
56
- requirement: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '0.1'
61
- - - ">="
62
- - !ruby/object:Gem::Version
63
- version: 0.1.1
64
- type: :development
65
- prerelease: false
66
- version_requirements: !ruby/object:Gem::Requirement
67
- requirements:
68
- - - "~>"
69
- - !ruby/object:Gem::Version
70
- version: '0.1'
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- version: 0.1.1
74
- description: |
75
- lithos is a self-contained embedded key-value store written from scratch as a
76
- native extension — no external database dependency. It uses a log-structured
77
- merge (LSM) tree: a write-ahead log makes every write durable, an in-memory
78
- sorted memtable flushes to immutable SSTables (with bloom filters), and
79
- compaction merges them. Keys and values are arbitrary binary strings; keys
80
- are kept in sorted order so you get ordered iteration and range scans, plus
81
- crash recovery via WAL replay. Windows MSVC (mswin) Ruby only.
11
+ dependencies: []
12
+ description: lithos is discontinued. This final release contains no code and exists
13
+ only so that installing lithos explains the discontinuation instead of building
14
+ an unmaintained native extension.
82
15
  executables: []
83
- extensions:
84
- - ext/lithos/extconf.rb
16
+ extensions: []
85
17
  extra_rdoc_files: []
86
18
  files:
87
- - CHANGELOG.md
88
19
  - LICENSE.txt
89
20
  - README.md
90
- - ext/lithos/extconf.rb
91
- - ext/lithos/lithos.cpp
92
21
  - lib/lithos.rb
93
- - lib/lithos/version.rb
94
- homepage: https://github.com/main-path/lithos
95
22
  licenses:
96
23
  - MIT
97
24
  metadata:
98
- homepage_uri: https://github.com/main-path/lithos
99
- changelog_uri: https://github.com/main-path/lithos/blob/main/CHANGELOG.md
100
- bug_tracker_uri: https://github.com/main-path/lithos/issues
101
25
  rubygems_mfa_required: 'true'
26
+ post_install_message: lithos is discontinued and this release contains no code. Use
27
+ sqlite3, or PStore from the standard library instead.
102
28
  rdoc_options: []
103
29
  require_paths:
104
30
  - lib
@@ -106,7 +32,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
106
32
  requirements:
107
33
  - - ">="
108
34
  - !ruby/object:Gem::Version
109
- version: '3.0'
35
+ version: '2.0'
110
36
  required_rubygems_version: !ruby/object:Gem::Requirement
111
37
  requirements:
112
38
  - - ">="
@@ -115,5 +41,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
115
41
  requirements: []
116
42
  rubygems_version: 3.6.9
117
43
  specification_version: 4
118
- summary: A small embedded, ordered, crash-safe key-value store (LSM-tree) for Ruby.
44
+ summary: Discontinued. Use sqlite3, or PStore from the standard library instead.
119
45
  test_files: []
data/CHANGELOG.md DELETED
@@ -1,28 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to this project are documented here. The format is based on
4
- [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
- adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
-
7
- ## [Unreleased]
8
-
9
- ## [0.1.0] - 2026-05-30
10
-
11
- ### Added
12
- - `Lithos.open(path, sync:, memtable_bytes:)` with block (auto-close) and
13
- non-block forms; `#close`, `#closed?`, `#path`.
14
- - `#put`/`#[]=`, `#get`/`#[]`, `#delete`, `#key?` (+ `include?`/`has_key?`),
15
- `#fetch` (with default/block, raises `KeyError`).
16
- - Ordered iteration: `#each`, `#each_key`, `#scan(gte:/gt:/lte:/lt:)`,
17
- `#range(from, to)`; `Enumerable` mixed in; `#size`/`#length`, `#empty?`, `#to_h`.
18
- - `#flush` (seal the memtable into an SSTable) and `#compact` (merge SSTables,
19
- dropping shadowed keys and tombstones).
20
- - LSM engine: CRC32 write-ahead log (durable per write in sync mode), sorted
21
- memtable, immutable memory-mapped SSTables with bloom filters + sparse index,
22
- atomically-published MANIFEST catalog, crash recovery via WAL replay (torn /
23
- bad-CRC tail discarded), and an exclusive single-writer directory lock.
24
- - Arbitrary binary keys and values (embedded NULs); lexicographic unsigned-byte
25
- key ordering.
26
-
27
- [Unreleased]: https://github.com/main-path/lithos/compare/v0.1.0...HEAD
28
- [0.1.0]: https://github.com/main-path/lithos/releases/tag/v0.1.0
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # extconf.rb for the lithos LSM key-value store extension.
4
-
5
- require "mkmf"
6
-
7
- unless RbConfig::CONFIG["target_os"] =~ /mswin/
8
- abort <<~MSG
9
- lithos requires a native Windows MSVC (mswin) Ruby — its storage engine uses
10
- Win32 file APIs (memory-mapped SSTables, FlushFileBuffers, atomic rename) and
11
- is built with cl.exe. Your Ruby is "#{RbConfig['arch']}".
12
- MSG
13
- end
14
-
15
- # C++ engine (std::map/std::string/std::vector/std::priority_queue) — needs C++ exceptions.
16
- $CXXFLAGS << " -EHsc"
17
-
18
- # kernel32 (file APIs / mapping) is linked by default; no extra import libs needed.
19
-
20
- create_makefile("lithos/lithos")