Skip to content

Commit

Permalink
Bitmap to v1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
kelindar committed Jun 30, 2021
1 parent 0fa7a3a commit db61c40
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 108 deletions.
74 changes: 33 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,8 @@ Let's first examine the `Range()` method. In the example below we select all of

```go
players.Query(func(txn *Txn) error {
txn.With("rogue").Range("name", func(v column.Cursor) bool {
txn.With("rogue").Range("name", func(v column.Cursor) {
println("rogue name ", v.String()) // Prints the name
return true
})
return nil
})
Expand All @@ -175,10 +174,9 @@ Now, what if you need two columns? The range only allows you to quickly select a

```go
players.Query(func(txn *Txn) error {
txn.With("rogue").Range("name", func(v column.Cursor) bool {
txn.With("rogue").Range("name", func(v column.Cursor) {
println("rogue name ", v.String()) // Prints the name
println("rogue age ", v.IntAt("age")) // Prints the age
return true
})
return nil
})
Expand Down Expand Up @@ -214,10 +212,9 @@ In the example below we're selecting all of the rogues and updating both their b

```go
players.Query(func(txn *Txn) error {
txn.With("rogue").Range("balance", func(v column.Cursor) bool {
txn.With("rogue").Range("balance", func(v column.Cursor) {
v.Update(10.0) // Update the "balance" to 10.0
v.UpdateAt("age", 50) // Update the "age" to 50
return true
}) // Select the balance
return nil
})
Expand All @@ -227,9 +224,8 @@ In certain cases, you might want to atomically increment or decrement numerical

```go
players.Query(func(txn *Txn) error {
txn.With("rogue").Range("balance", func(v column.Cursor) bool {
txn.With("rogue").Range("balance", func(v column.Cursor) {
v.Add(500.0) // Increment the "balance" by 500
return true
})
return nil
})
Expand All @@ -254,11 +250,10 @@ On an interestig node, since `expire` column which is automatically added to eac

```go
players.Query(func(txn *column.Txn) error {
return txn.Range("expire", func(v column.Cursor) bool {
return txn.Range("expire", func(v column.Cursor) {
oldExpire := time.Unix(0, v.Int()) // Convert expiration to time.Time
newExpire := expireAt.Add(1 * time.Hour).UnixNano() // Add some time
v.Update(newExpire)
return true
})
})
```
Expand All @@ -270,9 +265,8 @@ Transactions allow for isolation between two concurrent operations. In fact, all
```go
// Range over all of the players and update (successfully their balance)
players.Query(func(txn *column.Txn) error {
txn.Range("balance", func(v column.Cursor) bool {
txn.Range("balance", func(v column.Cursor) {
v.Update(10.0) // Update the "balance" to 10.0
return true
})

// No error, transaction will be committed
Expand All @@ -285,9 +279,8 @@ Now, in this example, we try to update balance but a query callback returns an e
```go
// Range over all of the players and update (successfully their balance)
players.Query(func(txn *column.Txn) error {
txn.Range("balance", func(v column.Cursor) bool {
txn.Range("balance", func(v column.Cursor) {
v.Update(10.0) // Update the "balance" to 10.0
return true
})

// Returns an error, transaction will be rolled back
Expand Down Expand Up @@ -398,9 +391,8 @@ func main(){
// Same condition as above, but we also select the actual names of those
// players and iterate through them.
players.Query(func(txn *column.Txn) error {
txn.With("human", "mage", "old").Range("name", func(v column.Cursor) bool {
txn.With("human", "mage", "old").Range("name", func(v column.Cursor) {
println(v.String()) // prints the name
return true
}) // The column to select
return nil
})
Expand Down Expand Up @@ -428,31 +420,31 @@ When testing for larger collections, I added a small example (see `examples` fol

```
running insert of 20000000 rows...
-> insert took 22.7740005s
running full scan of age >= 30...
-> result = 10200000
-> full scan took 171.712196ms
running full scan of class == "rogue"...
-> result = 7160000
-> full scan took 199.24443ms
running indexed query of human mages...
-> result = 1360000
-> indexed query took 574µs
running indexed query of human female mages...
-> result = 640000
-> indexed query took 747.148µs
running update of balance of everyone...
-> updated 20000000 rows
-> update took 317.528908ms
running update of age of mages...
-> updated 6040000 rows
-> update took 98.655836ms
-> insert took 22.6758949s
running full scan of age >= 30...
-> result = 10200000
-> full scan took 71.334496ms
running full scan of class == "rogue"...
-> result = 7160000
-> full scan took 88.987218ms
running indexed query of human mages...
-> result = 1360000
-> indexed query took 541.126µs
running indexed query of human female mages...
-> result = 640000
-> indexed query took 724.593µs
running update of balance of everyone...
-> updated 20000000 rows
-> update took 301.445804ms
running update of age of mages...
-> updated 6040000 rows
-> update took 95.673186ms
```

## Contributing
Expand Down
3 changes: 1 addition & 2 deletions collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,11 +278,10 @@ func (c *Collection) vacuum(ctx context.Context, interval time.Duration) {
case <-ticker.C:
now := time.Now().UnixNano()
c.Query(func(txn *Txn) error {
return txn.With(expireColumn).Range(expireColumn, func(v Cursor) bool {
return txn.With(expireColumn).Range(expireColumn, func(v Cursor) {
if expirateAt := v.Int(); expirateAt != 0 && now >= v.Int() {
v.Delete()
}
return true
})
})
}
Expand Down
30 changes: 13 additions & 17 deletions collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ import (

/*
cpu: Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz
BenchmarkCollection/insert-8 5531546 215.9 ns/op 18 B/op 0 allocs/op
BenchmarkCollection/fetch-8 23749724 44.97 ns/op 0 B/op 0 allocs/op
BenchmarkCollection/scan-8 855 1388532 ns/op 88 B/op 0 allocs/op
BenchmarkCollection/count-8 1000000 1081 ns/op 0 B/op 0 allocs/op
BenchmarkCollection/range-8 10000 100833 ns/op 14 B/op 0 allocs/op
BenchmarkCollection/update-at-8 4225484 281.7 ns/op 0 B/op 0 allocs/op
BenchmarkCollection/update-all-8 830 1388480 ns/op 105714 B/op 0 allocs/op
BenchmarkCollection/delete-at-8 6928646 171.9 ns/op 0 B/op 0 allocs/op
BenchmarkCollection/delete-all-8 180884 6373 ns/op 1 B/op 0 allocs/op
BenchmarkCollection/insert-8 5439637 221.3 ns/op 18 B/op 0 allocs/op
BenchmarkCollection/fetch-8 23985608 48.55 ns/op 0 B/op 0 allocs/op
BenchmarkCollection/scan-8 1845 689796 ns/op 25 B/op 0 allocs/op
BenchmarkCollection/count-8 1000000 1133 ns/op 0 B/op 0 allocs/op
BenchmarkCollection/range-8 10000 107436 ns/op 10 B/op 0 allocs/op
BenchmarkCollection/update-at-8 4171920 286.7 ns/op 0 B/op 0 allocs/op
BenchmarkCollection/update-all-8 837 1312193 ns/op 52392 B/op 0 allocs/op
BenchmarkCollection/delete-at-8 7141628 169.9 ns/op 0 B/op 0 allocs/op
BenchmarkCollection/delete-all-8 189722 6322 ns/op 0 B/op 0 allocs/op
*/
func BenchmarkCollection(b *testing.B) {
amount := 100000
Expand Down Expand Up @@ -100,10 +100,9 @@ func BenchmarkCollection(b *testing.B) {
b.ResetTimer()
for n := 0; n < b.N; n++ {
players.Query(func(txn *Txn) error {
txn.With("human", "mage", "old").Range("name", func(v Cursor) bool {
txn.With("human", "mage", "old").Range("name", func(v Cursor) {
count++
name = v.String()
return true
})
return nil
})
Expand All @@ -124,9 +123,8 @@ func BenchmarkCollection(b *testing.B) {
b.ResetTimer()
for n := 0; n < b.N; n++ {
players.Query(func(txn *Txn) error {
txn.Range("balance", func(v Cursor) bool {
txn.Range("balance", func(v Cursor) {
v.Update(1.0)
return true
})
return nil
})
Expand Down Expand Up @@ -236,7 +234,7 @@ func runReplication(t *testing.T, updates, inserts int) {
// Check if replica and primary are the same
assert.Equal(t, primary.Count(), replica.Count())
primary.Query(func(txn *Txn) error {
return txn.Range("float64", func(v Cursor) bool {
return txn.Range("float64", func(v Cursor) {
v1, v2 := v.FloatAt("float64"), v.IntAt("int32")
if v1 != 0 {
clone, _ := replica.Fetch(v.idx)
Expand All @@ -247,7 +245,6 @@ func runReplication(t *testing.T, updates, inserts int) {
clone, _ := replica.Fetch(v.idx)
assert.Equal(t, v.IntAt("int32"), clone.IntAt("int32"))
}
return true
})
})
})
Expand Down Expand Up @@ -329,10 +326,9 @@ func TestExpire(t *testing.T) {
// Insert an object
col.InsertWithTTL(obj, time.Microsecond)
col.Query(func(txn *Txn) error {
return txn.Range(expireColumn, func(v Cursor) bool {
return txn.Range(expireColumn, func(v Cursor) {
expireAt := time.Unix(0, v.Int())
v.Update(expireAt.Add(1 * time.Microsecond).UnixNano())
return true
})
})
assert.Equal(t, 1, col.Count())
Expand Down
9 changes: 3 additions & 6 deletions examples/million/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,9 @@ func main() {
measure("update", "balance of everyone", func() {
updates := 0
players.Query(func(txn *column.Txn) error {
return txn.Range("balance", func(v column.Cursor) bool {
return txn.Range("balance", func(v column.Cursor) {
updates++
v.Update(1000.0)
return true
})
})
fmt.Printf("-> updated %v rows\n", updates)
Expand All @@ -78,10 +77,9 @@ func main() {
measure("update", "age of mages", func() {
updates := 0
players.Query(func(txn *column.Txn) error {
return txn.With("mage").Range("age", func(v column.Cursor) bool {
return txn.With("mage").Range("age", func(v column.Cursor) {
updates++
v.Update(99.0)
return true
})
})
fmt.Printf("-> updated %v rows\n", updates)
Expand All @@ -91,10 +89,9 @@ func main() {
measure("update", "name of males", func() {
updates := 0
players.Query(func(txn *column.Txn) error {
return txn.With("male").Range("name", func(v column.Cursor) bool {
return txn.With("male").Range("name", func(v column.Cursor) {
updates++
v.Update("Sir " + v.String())
return true
})
})
fmt.Printf("-> updated %v rows\n", updates)
Expand Down
3 changes: 1 addition & 2 deletions examples/simple/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,8 @@ func main() {

// Run an indexed query
players.Query(func(txn *column.Txn) error {
return txn.With("human", "mage", "old").Range("name", func(v column.Cursor) bool {
return txn.With("human", "mage", "old").Range("name", func(v column.Cursor) {
println("human old mage", v.String())
return true
})
})
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ go 1.16

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kelindar/bitmap v1.0.12
github.com/kelindar/bitmap v1.1.0
github.com/stretchr/testify v1.7.0
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kelindar/bitmap v1.0.12 h1:MD696wPzddmfP/XrSm+kApsfVNJ5muR/2Sb2VAf+8QE=
github.com/kelindar/bitmap v1.0.12/go.mod h1:shAFyS8BOif+pvJ05GqxnCM0SdohHQjKvDetqI/9z6M=
github.com/kelindar/bitmap v1.1.0 h1:67PfkHFb+II2HQdeKrPugxMC7fZFEdKq31X+AOaHDq0=
github.com/kelindar/bitmap v1.1.0/go.mod h1:shAFyS8BOif+pvJ05GqxnCM0SdohHQjKvDetqI/9z6M=
github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI=
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
18 changes: 8 additions & 10 deletions txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,12 @@ func (txn *Txn) insert(object Object, expireAt int64) uint32 {
// Select iterates over the result set and allows to read any column. While this
// is flexible, it is not the most efficient way, consider Range() as an alternative
// iteration method over a specific column which also supports modification.
func (txn *Txn) Select(fn func(v Selector) bool) {
func (txn *Txn) Select(fn func(v Selector)) {
txn.owner.lock.RLock()
defer txn.owner.lock.RUnlock()

txn.index.Range(func(x uint32) bool {
return fn(Selector{
txn.index.Range(func(x uint32) {
fn(Selector{
idx: x,
txn: txn,
})
Expand All @@ -292,11 +292,10 @@ func (txn *Txn) Select(fn func(v Selector) bool) {
// the function returns true, the element at the index will be marked for deletion. The
// actual delete will take place once the transaction is committed.
func (txn *Txn) DeleteIf(fn func(v Selector) bool) {
txn.index.Range(func(x uint32) bool {
txn.index.Range(func(x uint32) {
if fn(Selector{idx: x, txn: txn}) {
txn.deletes.Set(x)
}
return true
})
}

Expand All @@ -307,9 +306,8 @@ func (txn *Txn) DeleteAll() {
}

// Range selects and iterates over a results for a specific column. The cursor provided
// also allows to select other columns, but at a slight performance cost. If the range
// function returns false, it halts the iteration.
func (txn *Txn) Range(column string, fn func(v Cursor) bool) error {
// also allows to select other columns, but at a slight performance cost.
func (txn *Txn) Range(column string, fn func(v Cursor)) error {
txn.owner.lock.RLock()
defer txn.owner.lock.RUnlock()

Expand All @@ -318,9 +316,9 @@ func (txn *Txn) Range(column string, fn func(v Cursor) bool) error {
return err
}

txn.index.Range(func(x uint32) bool {
txn.index.Range(func(x uint32) {
cur.idx = x
return fn(cur)
fn(cur)
})
return nil
}
Expand Down

0 comments on commit db61c40

Please sign in to comment.