-
Notifications
You must be signed in to change notification settings - Fork 637
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
02c9953
commit 5c32d53
Showing
10 changed files
with
373 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
"github.com/rosedblabs/rosedb/v2" | ||
"github.com/rosedblabs/rosedb/v2/utils" | ||
) | ||
|
||
// this file shows how to use the Watch feature of rosedb. | ||
|
||
func main() { | ||
// specify the options | ||
options := rosedb.DefaultOptions | ||
options.DirPath = "/tmp/rosedb_merge" | ||
options.WatchQueueSize = 1000 | ||
|
||
// open a database | ||
db, err := rosedb.Open(options) | ||
if err != nil { | ||
panic(err) | ||
} | ||
defer func() { | ||
_ = db.Close() | ||
}() | ||
|
||
// run a new goroutine to handle db event. | ||
go func() { | ||
wc, err := db.WatchChan() | ||
if err != nil { | ||
return | ||
} | ||
for { | ||
event := <-wc | ||
// when db closed, the event will receive nil. | ||
if event == nil { | ||
fmt.Println("The db is closed, so the watch channel is closed.") | ||
return | ||
} | ||
// events can be captured here for processing | ||
fmt.Printf("==== Get a new event ==== %s \n", event.String()) | ||
} | ||
}() | ||
|
||
// write some data | ||
for i := 0; i < 10; i++ { | ||
_ = db.Put([]byte(utils.GetTestKey(i)), utils.RandomValue(64)) | ||
} | ||
// delete some data | ||
for i := 0; i < 10/2; i++ { | ||
_ = db.Delete([]byte(utils.GetTestKey(i))) | ||
} | ||
|
||
// wait for watch goroutine to finish. | ||
time.Sleep(1 * time.Second) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package rosedb | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
"time" | ||
) | ||
|
||
type WatchActionType = byte | ||
|
||
const ( | ||
WatchActionPut WatchActionType = iota | ||
WatchActionDelete | ||
) | ||
|
||
// Event | ||
type Event struct { | ||
Action WatchActionType | ||
Key []byte | ||
Value []byte | ||
BatchId uint64 | ||
} | ||
|
||
func (e *Event) String() string { | ||
return fmt.Sprintf(` | ||
Event{ | ||
Action: %d, | ||
Key: %s, | ||
Value: %s, | ||
BatchId: %d | ||
}`, | ||
e.Action, | ||
e.Key, | ||
e.Value, | ||
e.BatchId) | ||
} | ||
|
||
// Watcher temporarily stores event information, | ||
// as it is generated until it is synchronized to DB's watch. | ||
// | ||
// If the event is overflow, It will remove the oldest data, | ||
// even if event hasn't been read yet. | ||
type Watcher struct { | ||
queue eventQueue | ||
mu sync.RWMutex | ||
} | ||
|
||
func NewWatcher(capacity uint64) *Watcher { | ||
return &Watcher{ | ||
queue: eventQueue{ | ||
Events: make([]*Event, capacity), | ||
Capacity: capacity, | ||
}, | ||
} | ||
} | ||
|
||
func (w *Watcher) insertEvent(e *Event) { | ||
w.mu.Lock() | ||
w.queue.push(e) | ||
if w.queue.isFull() { | ||
w.queue.frontTakeAStep() | ||
} | ||
w.mu.Unlock() | ||
} | ||
|
||
// getEvent if queue is empty, it will return nil. | ||
func (w *Watcher) getEvent() *Event { | ||
w.mu.RLock() | ||
defer w.mu.RUnlock() | ||
if isEmpty := w.queue.isEmpty(); isEmpty { | ||
return nil | ||
} | ||
e := w.queue.pop() | ||
return e | ||
} | ||
|
||
// sendEvent send events to DB's watch | ||
func (w *Watcher) sendEvent(c chan *Event) { | ||
for { | ||
e := w.getEvent() | ||
if e == nil { | ||
time.Sleep(100 * time.Millisecond) | ||
continue | ||
} | ||
c <- e | ||
} | ||
} | ||
|
||
type eventQueue struct { | ||
Events []*Event | ||
Capacity uint64 | ||
Front uint64 // read point | ||
Back uint64 // write point | ||
} | ||
|
||
func (eq *eventQueue) push(e *Event) { | ||
eq.Events[eq.Back] = e | ||
eq.Back = (eq.Back + 1) % eq.Capacity | ||
} | ||
|
||
func (eq *eventQueue) pop() *Event { | ||
e := eq.Events[eq.Front] | ||
eq.frontTakeAStep() | ||
return e | ||
} | ||
|
||
func (eq *eventQueue) isFull() bool { | ||
return (eq.Back+1)%eq.Capacity == eq.Front | ||
} | ||
|
||
func (eq *eventQueue) isEmpty() bool { | ||
return eq.Back == eq.Front | ||
} | ||
|
||
func (eq *eventQueue) frontTakeAStep() { | ||
eq.Front = (eq.Front + 1) % eq.Capacity | ||
} |
Oops, something went wrong.