contentserver/server/repo/loader.go

227 lines
6.3 KiB
Go

package repo
import (
"encoding/json"
"errors"
"fmt"
"github.com/foomo/contentserver/server/log"
"github.com/foomo/contentserver/server/repo/content"
"github.com/foomo/contentserver/server/responses"
"github.com/foomo/contentserver/server/utils"
"time"
//golog "log"
)
func (repo *Repo) updateRoutine() {
repo.updateChannel = make(chan *RepoDimension)
repo.updateDoneChannel = make(chan error)
go func() {
for {
select {
case newDimension := <-repo.updateChannel:
repo.updateDoneChannel <- repo.updateDimension(newDimension.Dimension, newDimension.Node)
}
}
}()
}
func (repo *Repo) UpdateDimension(dimension string, node *content.RepoNode) error {
repo.updateChannel <- &RepoDimension{
Dimension: dimension,
Node: node,
}
return <-repo.updateDoneChannel
}
func (repo *Repo) updateDimension(dimension string, newNode *content.RepoNode) error {
newNode.WireParents()
newDirectory := make(map[string]*content.RepoNode)
newURIDirectory := make(map[string]*content.RepoNode)
err := builDirectory(newNode, newDirectory, newURIDirectory)
if err != nil {
return err
}
err = wireAliases(newDirectory)
if err != nil {
return err
}
repo.Directory[dimension] = &Dimension{
Node: newNode,
Directory: newDirectory,
URIDirectory: newURIDirectory,
}
return nil
}
func builDirectory(dirNode *content.RepoNode, directory map[string]*content.RepoNode, uRIDirectory map[string]*content.RepoNode) error {
log.Debug("repo.buildDirectory: " + dirNode.Id)
existingNode, ok := directory[dirNode.Id]
if ok {
return errors.New("duplicate node with id:" + existingNode.Id)
}
directory[dirNode.Id] = dirNode
//todo handle duplicate uris
if _, thereIsAnExistingUriNode := uRIDirectory[dirNode.URI]; thereIsAnExistingUriNode {
return errors.New("duplicate node with uri: " + dirNode.URI)
}
uRIDirectory[dirNode.URI] = dirNode
for _, childNode := range dirNode.Nodes {
err := builDirectory(childNode, directory, uRIDirectory)
if err != nil {
return err
}
}
return nil
}
func wireAliases(directory map[string]*content.RepoNode) error {
for _, repoNode := range directory {
if len(repoNode.LinkId) > 0 {
if destinationNode, ok := directory[repoNode.LinkId]; ok {
repoNode.URI = destinationNode.URI
} else {
return errors.New("that link id points nowhere " + repoNode.LinkId + " from " + repoNode.Id)
}
}
}
return nil
}
func loadNodesFromJSON(jsonBytes []byte) (nodes map[string]*content.RepoNode, err error) {
nodes = make(map[string]*content.RepoNode)
err = json.Unmarshal(jsonBytes, &nodes)
return nodes, err
}
func (repo *Repo) Update() (updateResponse *responses.Update) {
floatSeconds := func(nanoSeconds int64) float64 {
return float64(nanoSeconds / 1000000)
}
startTime := time.Now().UnixNano()
updateRepotime, updateErr := repo.update()
updateResponse = responses.NewUpdate()
updateResponse.Stats.RepoRuntime = floatSeconds(updateRepotime)
if updateErr != nil {
// let us try to restore the world from a file
log.Error("could not update repository:" + updateErr.Error())
restoreErr := repo.tryToRestoreCurrent()
if restoreErr == nil {
log.Error("failed to restore preceding repo version")
} else {
log.Record("restored current repo from local history")
}
} else {
// add some stats
for dimension, _ := range repo.Directory {
updateResponse.Stats.NumberOfNodes += len(repo.Directory[dimension].Directory)
updateResponse.Stats.NumberOfURIs += len(repo.Directory[dimension].URIDirectory)
}
}
updateResponse.Stats.OwnRuntime = floatSeconds(startTime-time.Now().UnixNano()) - updateResponse.Stats.RepoRuntime
return updateResponse
}
func (repo *Repo) tryToRestoreCurrent() error {
currentJsonBytes, err := repo.history.getCurrent()
if err != nil {
return err
}
return repo.loadJSONBytes(currentJsonBytes)
}
func (repo *Repo) update() (repoRuntime int64, err error) {
startTimeRepo := time.Now().UnixNano()
jsonBytes, err := utils.Get(repo.server)
repoRuntime = time.Now().UnixNano() - startTimeRepo
if err != nil {
// we have no json to load - the repo server did not reply
return
} else {
nodes, err := loadNodesFromJSON(jsonBytes)
if err != nil {
// could not load nodes from json
return repoRuntime, err
}
err = repo.loadNodes(nodes)
if err != nil {
// repo failed to load nodes
return repoRuntime, err
}
}
return repoRuntime, nil
/*
log.Debug("loaded nodes for dimension " + dimension)
_, dimensionOk := repo.Directory[dimension]
if dimensionOk {
updateResponse.Stats.NumberOfNodes += len(repo.Directory[dimension].Directory)
updateResponse.Stats.NumberOfURIs += len(repo.Directory[dimension].URIDirectory)
}
jsonBytes
newNodes := loadNodesFromJSON(jsonBytes)
data, err := utils.GetRepo(repo.server, newNodes)
updateResponse.Stats.RepoRuntime =
startTimeOwn := time.Now()
if err == nil {
}
updateResponse.Success = (err != nil)
doneHandler := func() *responses.Update {
updateResponse.Stats.OwnRuntime = time.Now().Sub(startTimeOwn).Seconds()
return updateResponse
}
if updateResponse.Success {
log.Debug("going to load dimensions from" + utils.ToJSON(newNodes))
*/
}
func updateErrorHandler(err error, updateResponse *responses.Update) *responses.Update {
log.Error(fmt.Sprintf("update error: %", err))
if updateResponse == nil {
updateResponse = responses.NewUpdate()
}
updateResponse.Success = false
updateResponse.ErrorMessage = fmt.Sprintf("%", err)
updateResponse.Stats.NumberOfNodes = -1
updateResponse.Stats.NumberOfURIs = -1
return updateResponse
}
func (repo *Repo) loadJSONBytes(jsonBytes []byte) error {
nodes, err := loadNodesFromJSON(jsonBytes)
if err != nil {
return err
}
err = repo.loadNodes(nodes)
if err == nil {
historyErr := repo.history.add(jsonBytes)
if historyErr != nil {
log.Warning("could not add valid json to history:" + historyErr.Error())
} else {
log.Record("added valid json to history")
}
}
return err
}
func (repo *Repo) loadNodes(newNodes map[string]*content.RepoNode) error {
for dimension, newNode := range newNodes {
log.Debug("loading nodes for dimension " + dimension)
loadErr := repo.UpdateDimension(dimension, newNode)
if loadErr != nil {
log.Debug(" failed to load " + dimension + ": " + loadErr.Error())
return loadErr
}
}
// we need to throw away orphaned nodes
return nil
}