Skip to content

Commit

Permalink
introduce filesystem database (#483)
Browse files Browse the repository at this point in the history
  • Loading branch information
ushis authored Nov 13, 2023
1 parent e16d7e2 commit 5eca879
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 2 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,19 @@ gesundheit -conf /etc/gesundheit/gesundheit.toml
</tr>
</thead>
<tbody>
<tr>
<td rowspan="2"><strong>filesystem</strong></td>
<td rowspan="2">Simple filesystem baked database suitable for most setups.</td>
<td>Directory</td>
<td>Database directory, e.g. <code>"/var/lib/gesundheit"</td>
</tr>
<tr>
<td>VacuumInterval</td>
<td>
Interval in which expired entries are deleted from disk,
e.g. <code>"24h"</code>
</td>
</tr>
<tr>
<td><strong>memory</strong></td>
<td>
Expand Down
163 changes: 163 additions & 0 deletions db/filesystem/database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package filesystem

import (
"context"
"encoding/json"
"io"
"log"
"os"
"path/filepath"
"sync"
"time"

"github.com/ushis/gesundheit/db"
"github.com/ushis/gesundheit/db/memory"
"github.com/ushis/gesundheit/result"
)

func init() {
db.Register("filesystem", New)
}

const walFilename = "gesundheit.wal"
const tmpWalFilename = "_gesundheit.wal"

type Database struct {
*sync.Mutex
db db.Database
log *os.File
directory string
vacuumInterval time.Duration
cancelAutoVacuum context.CancelFunc
}

type Config struct {
Directory string
VacuumInterval string
}

func New(configure func(interface{}) error) (db.Database, error) {
conf := Config{Directory: ".", VacuumInterval: "24 hours"}

if err := configure(&conf); err != nil {
return nil, err
}
vacuumInterval, err := time.ParseDuration(conf.VacuumInterval)

if err != nil {
return nil, err
}
memoryDB, err := memory.New(nil)

if err != nil {
return nil, err
}
log, err := os.OpenFile(
filepath.Join(conf.Directory, walFilename),
os.O_APPEND|os.O_CREATE|os.O_RDWR,
0755,
)
if err != nil {
return nil, err
}
if err := readEvents(log, memoryDB.InsertEvent); err != nil {
return nil, err
}
ctx, cancel := context.WithCancel(context.Background())
db := &Database{&sync.Mutex{}, memoryDB, log, conf.Directory, vacuumInterval, cancel}
go db.autoVacuum(ctx)
return db, nil
}

func (db *Database) Close() error {
db.Lock()
defer db.Unlock()

db.cancelAutoVacuum()
return db.log.Close()
}

func (db *Database) InsertEvent(e result.Event) (bool, error) {
db.Lock()
defer db.Unlock()

if err := json.NewEncoder(db.log).Encode(e); err != nil {
return false, err
}
return db.db.InsertEvent(e)
}

func (db *Database) GetEvents() ([]result.Event, error) {
return db.db.GetEvents()
}

func (db *Database) GetEventsByNode(name string) ([]result.Event, error) {
return db.db.GetEventsByNode(name)
}

func (db *Database) autoVacuum(ctx context.Context) {
for {
select {
case <-time.After(db.vacuumInterval):
case <-ctx.Done():
return
}
log.Println("db: vacuum: start")

if err := db.vacuum(); err != nil {
log.Println("db: vacuum: failed:", err)
} else {
log.Println("db: vacuum: done")
}
}
}

func (db *Database) vacuum() error {
db.Lock()
defer db.Unlock()

newLog, err := os.OpenFile(
filepath.Join(db.directory, tmpWalFilename),
os.O_CREATE|os.O_TRUNC|os.O_WRONLY,
0755,
)
if err != nil {
return err
}
events, err := db.db.GetEvents()

if err != nil {
return err
}
w := json.NewEncoder(newLog)

for _, event := range events {
if err := w.Encode(event); err != nil {
return err
}
}
db.log.Close()
db.log = newLog

return os.Rename(
filepath.Join(db.directory, tmpWalFilename),
filepath.Join(db.directory, walFilename),
)
}

func readEvents(r io.Reader, insertEvent func(result.Event) (bool, error)) error {
decoder := json.NewDecoder(r)

for {
event := result.Event{}

if err := decoder.Decode(&event); err == io.EOF {
return nil
} else if err != nil {
return err
}
if _, err := insertEvent(event); err != nil {
return err
}
}
}
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"os"
Expand All @@ -27,6 +26,7 @@ import (
_ "github.com/ushis/gesundheit/check/node-alive"
_ "github.com/ushis/gesundheit/check/tls-cert"
"github.com/ushis/gesundheit/crypto"
_ "github.com/ushis/gesundheit/db/filesystem"
_ "github.com/ushis/gesundheit/db/memory"
_ "github.com/ushis/gesundheit/db/redis"
_ "github.com/ushis/gesundheit/filter/office-hours"
Expand Down Expand Up @@ -169,7 +169,7 @@ func cmdPubkey(args []string) {
flags.Usage()
os.Exit(2)
}
buf, err := ioutil.ReadAll(os.Stdin)
buf, err := io.ReadAll(os.Stdin)

if err != nil {
log.Fatalln("failed to read:", err)
Expand Down

0 comments on commit 5eca879

Please sign in to comment.