about summary refs log tree commit diff stats
path: root/src/common.c
diff options
context:
space:
mode:
authorJames Booth <boothj5@gmail.com>2017-03-25 01:48:40 +0000
committerJames Booth <boothj5@gmail.com>2017-03-25 01:48:40 +0000
commit131c98b30521d6a1df86613b03532541c65dae53 (patch)
tree07ef0cf46dcd38ab00b47610b88a8f57a417cebd /src/common.c
parentb5e0106526fc4a22219c4978209df05981e115fd (diff)
downloadprofani-tty-131c98b30521d6a1df86613b03532541c65dae53.tar.gz
Add missing header
Diffstat (limited to 'src/common.c')
0 files changed, 0 insertions, 0 deletions
0 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
package main

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

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

// Checks whether it's time to refresh
// the cache.
func checkCacheTime() bool {
	return time.Since(confObj.lastCache) > confObj.cacheInterval
}

// Checks whether 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() {

	// Iterate over the registry and
	// update each individual user.
	for k := range twtxtCache.Reg {
		err := twtxtCache.UpdateUser(k)
		if err != nil {
			log.Printf("%v\n", err)
			continue
		}
	}

	// Re-scrape all the remote registries
	// to see if they have any new users
	// to add locally.
	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 {
	// Acquire the database from the aether.
	// goleveldb is concurrency-safe, so we
	// can immediately push it back into the
	// channel for other functions to use.
	db := <-dbChan
	dbChan <- db

	// Create a batch write job so it can
	// be done at one time rather than
	// per entry.
	twtxtCache.Mu.RLock()
	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 {
			rfc := i.Format(time.RFC3339)
			dbBasket.Put([]byte(k+"*Status*"+rfc), []byte(e))
		}
	}
	twtxtCache.Mu.RUnlock()

	// 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
	}

	// Update the last push time for
	// our timer/watch function to
	// reference.
	confObj.mu.Lock()
	confObj.lastPush = time.Now()
	confObj.mu.Unlock()

	return nil
}

// Pulls registry data from the DB on startup.
// Iterates over the database one entry at a time.
func pullDatabase() {
	// Acquire the database from the aether.
	// goleveldb is concurrency-safe, so we
	// can immediately push it back into the
	// channel for other functions to use.
	db := <-dbChan
	dbChan <- db

	iter := db.NewIterator(nil, nil)

	// Read the database key-by-key
	for iter.Next() {
		key := iter.Key()
		val := iter.Value()

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

		// Start with an empty Data struct. If
		// there's already one in the cache, pull
		// it and use it instead.
		data := registry.NewUserData()
		twtxtCache.Mu.RLock()
		if _, ok := twtxtCache.Reg[urls]; ok {
			data = twtxtCache.Reg[urls]
		}
		twtxtCache.Mu.RUnlock()
		ref := reflect.ValueOf(data).Elem()

		// Use reflection to find the right field
		// in the Data struct. Once found, assign
		// the value and break so the DB iteration
		// can continue.
		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" {

			// If we're looking at a Status entry in the DB,
			// parse the time then add it to the TimeMap under
			// data.Status
			thetime, err := time.Parse("RFC3339", split[2])
			if err != nil {
				log.Printf("%v\n", err)
			}
			data.Status[thetime] = string(val)

		} else {
			// The third and final possibility is
			// if we've come across an entry for
			// a remote twtxt registry to scrape.
			// If so, add it to our list.
			remoteRegistries.Mu.Lock()
			remoteRegistries.List = append(remoteRegistries.List, string(val))
			remoteRegistries.Mu.Unlock()
			continue
		}

		// Push the data struct (back) into
		// the cache.
		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)
	}
}