storage: add a method to tidy/optimize the database
This commit is contained in:
parent
729a4e3fa0
commit
20d4299239
|
@ -63,6 +63,8 @@ func init() {
|
|||
router.POST("/api/users/:userid/send_validation_email", api.ApiHandler(userHandler(sendValidateUserEmail)))
|
||||
router.POST("/api/users/:userid/validation_link", api.ApiHandler(userHandler(emailValidationLink)))
|
||||
router.POST("/api/users/:userid/validate_email", api.ApiHandler(userHandler(validateEmail)))
|
||||
|
||||
router.POST("/api/tidy", api.ApiHandler(tidyDB))
|
||||
}
|
||||
|
||||
func getUsers(_ *config.Options, _ httprouter.Params, _ io.Reader) api.Response {
|
||||
|
@ -171,3 +173,7 @@ func validateEmail(_ *config.Options, user *happydns.User, _ httprouter.Params,
|
|||
user.EmailValidated = &now
|
||||
return api.NewAPIResponse(user, storage.MainStore.UpdateUser(user))
|
||||
}
|
||||
|
||||
func tidyDB(_ *config.Options, _ httprouter.Params, _ io.Reader) api.Response {
|
||||
return api.NewAPIResponse(true, storage.MainStore.Tidy())
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ import (
|
|||
|
||||
type Storage interface {
|
||||
DoMigration() error
|
||||
Tidy() error
|
||||
Close() error
|
||||
|
||||
GetDomains(u *happydns.User) (happydns.Domains, error)
|
||||
|
|
|
@ -59,6 +59,15 @@ func (s *LevelDBStorage) DoMigration() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) Tidy() error {
|
||||
for _, tidy := range []func() error{s.TidySessions, s.TidySources, s.TidyDomains, s.TidyZones} {
|
||||
if err := tidy(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) Close() error {
|
||||
return s.db.Close()
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ package database
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"git.happydns.org/happydns/model"
|
||||
|
||||
|
@ -60,9 +61,18 @@ func (s *LevelDBStorage) GetDomains(u *happydns.User) (domains happydns.Domains,
|
|||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetDomain(u *happydns.User, id int64) (z *happydns.Domain, err error) {
|
||||
func (s *LevelDBStorage) getDomain(id string) (z *happydns.Domain, err error) {
|
||||
z = &happydns.Domain{}
|
||||
err = s.get(fmt.Sprintf("domain-%d", id), &z)
|
||||
err = s.get(id, z)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetDomain(u *happydns.User, id int64) (z *happydns.Domain, err error) {
|
||||
z, err = s.getDomain(fmt.Sprintf("domain-%d", id))
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if z.IdUser != u.Id {
|
||||
z = nil
|
||||
|
@ -161,3 +171,43 @@ func (s *LevelDBStorage) ClearDomains() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) TidyDomains() error {
|
||||
tx, err := s.db.OpenTransaction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iter := tx.NewIterator(util.BytesPrefix([]byte("domain-")), nil)
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
domain, err := s.getDomain(string(iter.Key()))
|
||||
|
||||
if err == nil {
|
||||
_, err = s.GetUser(domain.IdUser)
|
||||
if err == leveldb.ErrNotFound {
|
||||
// Drop domain of unexistant users
|
||||
err = tx.Delete(iter.Key(), nil)
|
||||
log.Printf("Deleting orphan domain (user %d not found): %v\n", domain.IdUser, domain)
|
||||
}
|
||||
} else {
|
||||
// Drop domain of unexistant users
|
||||
log.Printf("Deleting unreadable domain (%w): %v\n", err, domain)
|
||||
err = tx.Delete(iter.Key(), nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -33,18 +33,24 @@ package database
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
|
||||
"git.happydns.org/happydns/model"
|
||||
)
|
||||
|
||||
func (s *LevelDBStorage) GetSession(id []byte) (session *happydns.Session, err error) {
|
||||
func (s *LevelDBStorage) getSession(id string) (session *happydns.Session, err error) {
|
||||
session = &happydns.Session{}
|
||||
err = s.get(fmt.Sprintf("user.session-%x", id), &session)
|
||||
err = s.get(id, &session)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetSession(id []byte) (session *happydns.Session, err error) {
|
||||
return s.getSession(fmt.Sprintf("user.session-%x", id))
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) CreateSession(session *happydns.Session) error {
|
||||
key, id, err := s.findBytesKey("user.session-", 255)
|
||||
if err != nil {
|
||||
|
@ -88,3 +94,43 @@ func (s *LevelDBStorage) ClearSessions() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) TidySessions() error {
|
||||
tx, err := s.db.OpenTransaction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iter := tx.NewIterator(util.BytesPrefix([]byte("user.session-")), nil)
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
session, err := s.getSession(string(iter.Key()))
|
||||
|
||||
if err != nil {
|
||||
// Drop unreadable sessions
|
||||
log.Printf("Deleting unreadable session (%w): %v\n", err, session)
|
||||
err = tx.Delete(iter.Key(), nil)
|
||||
} else {
|
||||
_, err = s.GetUser(session.IdUser)
|
||||
if err == leveldb.ErrNotFound {
|
||||
// Drop session from unexistant users
|
||||
log.Printf("Deleting orphan session (user %d not found): %v\n", session.IdUser, session)
|
||||
err = tx.Delete(iter.Key(), nil)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ package database
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
|
@ -42,6 +43,12 @@ import (
|
|||
"git.happydns.org/happydns/sources"
|
||||
)
|
||||
|
||||
func (s *LevelDBStorage) getSourceType(id []byte) (srcType *happydns.SourceType, err error) {
|
||||
srcType = &happydns.SourceType{}
|
||||
err = decodeData(id, srcType)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetSourceTypes(u *happydns.User) (srcs []happydns.SourceType, err error) {
|
||||
iter := s.search("source-")
|
||||
defer iter.Release()
|
||||
|
@ -152,3 +159,43 @@ func (s *LevelDBStorage) ClearSources() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) TidySources() error {
|
||||
tx, err := s.db.OpenTransaction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iter := tx.NewIterator(util.BytesPrefix([]byte("source-")), nil)
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
srcType, err := s.getSourceType(iter.Key())
|
||||
|
||||
if err != nil {
|
||||
// Drop unreadable sources
|
||||
log.Printf("Deleting unreadable source (%w): %v\n", err, srcType)
|
||||
err = tx.Delete(iter.Key(), nil)
|
||||
} else {
|
||||
_, err = s.GetUser(srcType.OwnerId)
|
||||
if err == leveldb.ErrNotFound {
|
||||
// Drop sources of unexistant users
|
||||
log.Printf("Deleting orphan source (user %d not found): %v\n", srcType.OwnerId, srcType)
|
||||
err = tx.Delete(iter.Key(), nil)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -33,6 +33,9 @@ package database
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.happydns.org/happydns/model"
|
||||
|
||||
|
@ -45,6 +48,17 @@ func (s *LevelDBStorage) GetZone(id int64) (z *happydns.Zone, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) getZoneMeta(id string) (z *happydns.ZoneMeta, err error) {
|
||||
z = &happydns.ZoneMeta{}
|
||||
err = s.get(id, z)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) GetZoneMeta(id int64) (z *happydns.ZoneMeta, err error) {
|
||||
z, err = s.getZoneMeta(fmt.Sprintf("domain.zone-%d", id))
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) CreateZone(z *happydns.Zone) error {
|
||||
key, id, err := s.findInt63Key("domain.zone-")
|
||||
if err != nil {
|
||||
|
@ -88,3 +102,63 @@ func (s *LevelDBStorage) ClearZones() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *LevelDBStorage) TidyZones() error {
|
||||
tx, err := s.db.OpenTransaction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iter := tx.NewIterator(util.BytesPrefix([]byte("domain-")), nil)
|
||||
defer iter.Release()
|
||||
|
||||
var referencedZones []int64
|
||||
|
||||
for iter.Next() {
|
||||
domain, _ := s.getDomain(string(iter.Key()))
|
||||
|
||||
if domain != nil {
|
||||
for _, zh := range domain.ZoneHistory {
|
||||
referencedZones = append(referencedZones, zh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iter = tx.NewIterator(util.BytesPrefix([]byte("domain.zone-")), nil)
|
||||
defer iter.Release()
|
||||
|
||||
for iter.Next() {
|
||||
if zoneId, err := strconv.ParseInt(strings.TrimPrefix(string(iter.Key()), "domain.zone-"), 10, 64); err != nil {
|
||||
// Drop zones with invalid ID
|
||||
log.Printf("Deleting unindentified zone: key=%s\n", iter.Key())
|
||||
err = tx.Delete(iter.Key(), nil)
|
||||
} else {
|
||||
foundZone := false
|
||||
for _, zid := range referencedZones {
|
||||
if zid == zoneId {
|
||||
foundZone = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundZone {
|
||||
// Drop orphan zones
|
||||
log.Printf("Deleting orphan zone: %d\n", zoneId)
|
||||
err = tx.Delete(iter.Key(), nil)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
tx.Discard()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user