Improved documentation
This commit is contained in:
parent
658cd6a8a3
commit
0c6db9d1f3
35
README.md
35
README.md
@ -11,12 +11,11 @@ go get git.akyoto.dev/go/ocean
|
|||||||
## Example
|
## Example
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// User type
|
// Define the User type
|
||||||
type User struct { Name string }
|
type User struct { Name string }
|
||||||
|
|
||||||
// Create a new collection
|
// Create a collection in ~/.ocean/myapp/User.dat
|
||||||
todolist := ocean.New("todolist")
|
users := ocean.New[User]("myapp", &storage.File[User]{})
|
||||||
users := todolist.NewCollection[User](&storage.File[User]{})
|
|
||||||
|
|
||||||
// Store some data
|
// Store some data
|
||||||
users.Set("1", &User{Name: "User 1"})
|
users.Set("1", &User{Name: "User 1"})
|
||||||
@ -32,9 +31,7 @@ for user := range users.All() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Data will be stored in `~/.ocean/todolist/User.dat`.
|
## File format
|
||||||
|
|
||||||
## Format
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
1
|
1
|
||||||
@ -45,6 +42,23 @@ Data will be stored in `~/.ocean/todolist/User.dat`.
|
|||||||
{"name":"User 3"}
|
{"name":"User 3"}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Storage systems
|
||||||
|
|
||||||
|
### storage.File
|
||||||
|
|
||||||
|
`storage.File` uses a single file to store all records.
|
||||||
|
Writes using `Set(key, value)` are async and only mark the collection as "dirty" which is very quick.
|
||||||
|
The sync to disk happens shortly afterwards.
|
||||||
|
Every collection uses one goroutine to check the "dirty" flag, write the new contents to disk and reset the flag.
|
||||||
|
|
||||||
|
The biggest advantage of `storage.File` is that it scales well with the number of requests:
|
||||||
|
|
||||||
|
Suppose `n` is the number of write requests and `io` is the time it takes for one write. Immediate storage would require `O(n * io)` time to complete all writes but the async behavior makes it `O(n)`.
|
||||||
|
|
||||||
|
You should use `storage.File` if you have a permanently running process such as a web server where end users expect quick responses and background work can happen after the user request has already been dealt with.
|
||||||
|
|
||||||
|
Make sure you `defer collection.Sync()` to ensure that queued writes will be handled when the process ends.
|
||||||
|
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -53,10 +67,3 @@ BenchmarkSet-12 4796011 251.0 ns/op 32 B/op
|
|||||||
BenchmarkDelete-12 471913158 2.530 ns/op 0 B/op 0 allocs/op
|
BenchmarkDelete-12 471913158 2.530 ns/op 0 B/op 0 allocs/op
|
||||||
BenchmarkNew-12 48838576 22.89 ns/op 80 B/op 1 allocs/op
|
BenchmarkNew-12 48838576 22.89 ns/op 80 B/op 1 allocs/op
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
1. Create all the collections you need at the start
|
|
||||||
2. `defer users.Sync()` to ensure queued writes will be handled on exit
|
|
||||||
3. Start your web server
|
|
||||||
4. Retrieve and update your data using `Get` and `Set` calls
|
|
||||||
|
@ -14,7 +14,10 @@ import (
|
|||||||
"git.akyoto.dev/go/ocean"
|
"git.akyoto.dev/go/ocean"
|
||||||
)
|
)
|
||||||
|
|
||||||
const diskWriteInterval = 100 * time.Millisecond
|
const (
|
||||||
|
diskWriteInterval = 100 * time.Millisecond
|
||||||
|
fileExtension = ".dat"
|
||||||
|
)
|
||||||
|
|
||||||
type File[T any] struct {
|
type File[T any] struct {
|
||||||
collection ocean.StorageData
|
collection ocean.StorageData
|
||||||
@ -28,7 +31,7 @@ func (fs *File[T]) Init(c ocean.StorageData) error {
|
|||||||
|
|
||||||
go fs.flushWorker()
|
go fs.flushWorker()
|
||||||
|
|
||||||
fileName := filepath.Join(c.Root(), c.Name()+".dat")
|
fileName := filepath.Join(c.Root(), c.Name()+fileExtension)
|
||||||
file, err := os.Open(fileName)
|
file, err := os.Open(fileName)
|
||||||
|
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
@ -79,7 +82,7 @@ func (fs *File[T]) flushWorker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fs *File[T]) flush() error {
|
func (fs *File[T]) flush() error {
|
||||||
oldPath := filepath.Join(fs.collection.Root(), fs.collection.Name()+".dat")
|
oldPath := filepath.Join(fs.collection.Root(), fs.collection.Name()+fileExtension)
|
||||||
newPath := oldPath + ".tmp"
|
newPath := oldPath + ".tmp"
|
||||||
file, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
|
file, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user