summary refs log blame commit diff stats
path: root/cache.go
blob: 253de9883c3907f0ac60742dcd1ebe3dd14b81e5 (plain) (tree)






























































































































































                                                                                                               
package main

import (
	"log"
	"reflect"
	"strings"
	"time"

	"github.com/getwtxt/registry"
	"github.com/syndtr/goleveldb/leveldb"
)

// checks if it's time to refresh the cache or not
func checkCacheTime() bool {
	return time.Since(confObj.lastCache) > confObj.cacheInterval
}

// checks if it's time to push the cache to the database
func checkDBtime() bool {
	return time.Since(confObj.lastPush) > confObj.dbInterval
}

// launched by init as a goroutine to constantly watch
// for the update interval to pass
func cacheAndPush() {
	for {
		if checkCacheTime() {
			refreshCache()
		}
		if checkDBtime() {
			if err := pushDatabase(); err != nil {
				log.Printf("Error pushing cache to database: %v\n", err)
			}
		}
	}
}

// refreshes the cache
func refreshCache() {

	for k := range twtxtCache.Reg {
		err := twtxtCache.UpdateUser(k)
		if err != nil {
			log.Printf("%v\n", err)
			continue
		}
	}

	for _, v := range remoteRegistries.List {
		err := twtxtCache.ScrapeRemoteRegistry(v)
		if err != nil {
			log.Printf("Error while refreshing local copy of remote registry user data: %v\n", err)
		}
	}
	confObj.mu.Lock()
	confObj.lastCache = time.Now()
	confObj.mu.Unlock()
}

// pushes the registry's cache data to a local
// database for safe keeping
func pushDatabase() error {
	db := <-dbChan
	twtxtCache.Mu.RLock()

	// create a batch write job so it can
	// be done at one time rather than
	// per value
	var dbBasket *leveldb.Batch
	for k, v := range twtxtCache.Reg {
		dbBasket.Put([]byte(k+".Nick"), []byte(v.Nick))
		dbBasket.Put([]byte(k+".URL"), []byte(v.URL))
		dbBasket.Put([]byte(k+".IP"), []byte(v.IP))
		dbBasket.Put([]byte(k+".Date"), []byte(v.Date))
		for i, e := range v.Status {
			dbBasket.Put([]byte(k+".Status."+i.String()), []byte(e))
		}
	}

	// save our list of remote registries to scrape
	for k, v := range remoteRegistries.List {
		dbBasket.Put([]byte("remote."+string(k)), []byte(v))
	}

	// execute the batch job
	if err := db.Write(dbBasket, nil); err != nil {
		return err
	}

	twtxtCache.Mu.RUnlock()
	dbChan <- db

	// update the last push time
	confObj.mu.Lock()
	confObj.lastPush = time.Now()
	confObj.mu.Unlock()

	return nil
}

// pulls registry data from the DB on startup
func pullDatabase() {
	db := <-dbChan

	iter := db.NewIterator(nil, nil)

	for iter.Next() {
		key := iter.Key()
		val := iter.Value()

		split := strings.Split(string(key), ".")
		urls := string(split[0])
		field := string(split[1])
		data := registry.NewUserData()

		twtxtCache.Mu.RLock()
		if _, ok := twtxtCache.Reg[urls]; ok {
			data = twtxtCache.Reg[urls]
		}
		twtxtCache.Mu.RUnlock()

		ref := reflect.ValueOf(data).Elem()

		if field != "Status" && urls != "remote" {
			for i := 0; i < ref.NumField(); i++ {

				f := ref.Field(i)
				if f.String() == field {
					f.Set(reflect.ValueOf(val))
					break
				}
			}
		} else if field == "Status" && urls != "remote" {

			thetime, err := time.Parse("RFC3339", split[2])
			if err != nil {
				log.Printf("%v\n", err)
			}
			data.Status[thetime] = string(val)

		} else {
			remoteRegistries.Mu.Lock()
			remoteRegistries.List = append(remoteRegistries.List, string(val))
			remoteRegistries.Mu.Unlock()
		}

		twtxtCache.Mu.Lock()
		twtxtCache.Reg[urls] = data
		twtxtCache.Mu.Unlock()
	}

	iter.Release()
	err := iter.Error()
	if err != nil {
		log.Printf("Error while pulling DB into registry cache: %v\n", err)
	}

	dbChan <- db
}