Some sync changes, graceful shutdwn

This commit is contained in:
Simon Martens
2024-11-10 21:49:36 +01:00
parent bdd4eeab26
commit 49f2243f0e
8 changed files with 54910 additions and 82 deletions

View File

@@ -3,5 +3,5 @@
"git_branch": "main", "git_branch": "main",
"webhook_endpoint": "/webhook", "webhook_endpoint": "/webhook",
"webhook_secret": "secret", "webhook_secret": "secret",
"debug": false "debug": true
} }

View File

@@ -3,8 +3,10 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"os/signal"
"path/filepath" "path/filepath"
"sync" "sync"
"syscall"
"githib.com/Theodor-Springmann-Stiftung/kgpz_web/helpers" "githib.com/Theodor-Springmann-Stiftung/kgpz_web/helpers"
"githib.com/Theodor-Springmann-Stiftung/kgpz_web/providers" "githib.com/Theodor-Springmann-Stiftung/kgpz_web/providers"
@@ -32,7 +34,6 @@ const (
) )
type Library struct { type Library struct {
smu sync.Mutex
Agents *providers.AgentProvider Agents *providers.AgentProvider
Places *providers.PlaceProvider Places *providers.PlaceProvider
Works *providers.WorkProvider Works *providers.WorkProvider
@@ -42,6 +43,7 @@ type Library struct {
} }
type KGPZ struct { type KGPZ struct {
lmu sync.Mutex
Config *providers.ConfigProvider Config *providers.ConfigProvider
Repo *providers.GitProvider Repo *providers.GitProvider
Library Library
@@ -64,6 +66,8 @@ func (k *KGPZ) IsDebug() bool {
} }
func (k *KGPZ) Pull() { func (k *KGPZ) Pull() {
// TODO: what happens if the application quits mid-pull?
// We need to make sure to exit gracefully
go func(k *KGPZ) { go func(k *KGPZ) {
if k.Repo == nil { if k.Repo == nil {
return return
@@ -102,63 +106,102 @@ func (k *KGPZ) InitRepo() {
// This panics if the data cant be read, and there is no data read // This panics if the data cant be read, and there is no data read
func (k *KGPZ) Serialize() { func (k *KGPZ) Serialize() {
k.smu.Lock()
defer k.smu.Unlock()
// TODO: maybe dont panic if a webhook can be setup, we need to check the requirements only when starting the server // TODO: maybe dont panic if a webhook can be setup, we need to check the requirements only when starting the server
// TODO: do this in parallel goroutines using a waitgroup
agents := k.InitAgents()
if agents == nil && k.Agents != nil {
helpers.LogOnErr(&k.Agents, nil, "Error initializing agents, keeping old state")
} else if agents == nil {
helpers.Panic(nil, "Error initializing agents")
} else {
k.Agents = agents
}
places := k.InitPlaces() new := Library{}
if places == nil && k.Places != nil {
helpers.LogOnErr(&k.Places, nil, "Error initializing places, keeping old state")
} else if places == nil {
helpers.Panic(nil, "Error initializing places")
} else {
k.Places = places
}
works := k.InitWorks() wg := sync.WaitGroup{}
if works == nil && k.Works != nil { wg.Add(6)
helpers.LogOnErr(&k.Works, nil, "Error initializing works, keeping old state")
} else if works == nil {
helpers.Panic(nil, "Error initializing works")
} else {
k.Works = works
}
categories := k.InitCategories() go func() {
if categories == nil && k.Categories != nil { defer wg.Done()
helpers.LogOnErr(&k.Categories, nil, "Error initializing categories, keeping old state") agents := k.InitAgents()
} else if categories == nil { if agents == nil && k.Agents != nil {
helpers.Panic(nil, "Error initializing categories") helpers.LogOnErr(&k.Agents, nil, "Error initializing agents, keeping old state")
} else { new.Agents = k.Agents
k.Categories = categories return
} } else if agents == nil {
helpers.Panic(nil, "Error initializing agents")
return
}
new.Agents = agents
}()
issues := k.InitIssues() go func() {
if issues == nil && k.Issues != nil { defer wg.Done()
helpers.LogOnErr(&k.Issues, nil, "Error initializing issues, keeping old state") places := k.InitPlaces()
} else if issues == nil { if places == nil && k.Places != nil {
helpers.Panic(nil, "Error initializing issues") helpers.LogOnErr(&k.Places, nil, "Error initializing places, keeping old state")
} else { new.Places = k.Places
k.Issues = issues return
} } else if places == nil {
helpers.Panic(nil, "Error initializing places")
return
}
new.Places = places
}()
pieces := k.InitPieces() go func() {
if pieces == nil && k.Pieces != nil { defer wg.Done()
helpers.LogOnErr(&k.Pieces, nil, "Error initializing pieces, keeping old state") works := k.InitWorks()
} else if pieces == nil { if works == nil && k.Works != nil {
helpers.Panic(nil, "Error initializing pieces") helpers.LogOnErr(&k.Works, nil, "Error initializing works, keeping old state")
} else { new.Works = k.Works
k.Pieces = pieces return
} } else if works == nil {
helpers.Panic(nil, "Error initializing works")
return
}
new.Works = works
}()
go func() {
defer wg.Done()
categories := k.InitCategories()
if categories == nil && k.Categories != nil {
helpers.LogOnErr(&k.Categories, nil, "Error initializing categories, keeping old state")
new.Categories = k.Categories
return
} else if categories == nil {
helpers.Panic(nil, "Error initializing categories")
return
}
new.Categories = categories
}()
go func() {
defer wg.Done()
issues := k.InitIssues()
if issues == nil && k.Issues != nil {
helpers.LogOnErr(&k.Issues, nil, "Error initializing issues, keeping old state")
new.Issues = k.Issues
return
} else if issues == nil {
helpers.Panic(nil, "Error initializing issues")
return
}
new.Issues = issues
}()
go func() {
defer wg.Done()
pieces := k.InitPieces()
if pieces == nil && k.Pieces != nil {
helpers.LogOnErr(&k.Pieces, nil, "Error initializing pieces, keeping old state")
new.Pieces = k.Pieces
return
} else if pieces == nil {
helpers.Panic(nil, "Error initializing pieces")
return
}
new.Pieces = pieces
}()
wg.Wait()
k.lmu.Lock()
k.Library = new
k.lmu.Unlock()
} }
func (k *KGPZ) InitAgents() *providers.AgentProvider { func (k *KGPZ) InitAgents() *providers.AgentProvider {
@@ -257,6 +300,10 @@ func (k *KGPZ) InitPieces() *providers.PieceProvider {
return cp return cp
} }
func (k *KGPZ) Shutdown() {
k.Repo.Wait()
}
func getXMLFiles(path string) (*[]string, error) { func getXMLFiles(path string) (*[]string, error) {
if _, err := os.Stat(path); os.IsNotExist(err) { if _, err := os.Stat(path); os.IsNotExist(err) {
return nil, err return nil, err
@@ -276,4 +323,23 @@ func main() {
kgpz := NewKGPZ(cfg) kgpz := NewKGPZ(cfg)
kgpz.InitRepo() kgpz.InitRepo()
kgpz.Serialize() kgpz.Serialize()
Cleanup(kgpz)
}
func Cleanup(k *KGPZ) {
sigs := make(chan os.Signal, 1)
done := make(chan bool, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
_ = <-sigs
// INFO: here we can add a cleanup functions
k.Shutdown()
done <- true
}()
<-done
fmt.Println("Cleanup finished. Exiting.")
} }

54756
out.log Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,7 @@ type Agent struct {
SortName string `xml:"sortiername"` SortName string `xml:"sortiername"`
Life string `xml:"lebensdaten"` Life string `xml:"lebensdaten"`
GND string `xml:"gnd"` GND string `xml:"gnd"`
Org bool `xml:"org,attr"`
Identifier Identifier
AnnotationNote AnnotationNote
} }

View File

@@ -88,25 +88,28 @@ func (g *GitProvider) Pull() (error, bool) {
return err, false return err, false
} }
if err := wt.Checkout(&git.CheckoutOptions{
Branch: branch,
Force: true,
}); err != nil {
return err, false
}
if err := wt.Pull(&git.PullOptions{ if err := wt.Pull(&git.PullOptions{
RemoteName: "origin", RemoteName: "origin",
ReferenceName: branch, ReferenceName: branch,
Progress: os.Stdout, Progress: os.Stdout,
}); err != nil && err != git.NoErrAlreadyUpToDate { }); err != nil {
if err == git.NoErrAlreadyUpToDate {
return nil, false
}
return err, false return err, false
} else if err == git.NoErrAlreadyUpToDate {
return nil, false
} }
defer wt.Clean(&git.CleanOptions{Dir: true}) defer wt.Clean(&git.CleanOptions{Dir: true})
return g.setValues(repo), true oldCommit := g.Commit
if err := g.setValues(repo); err != nil {
return err, false
}
if oldCommit == g.Commit {
return nil, false
}
return nil, true
} }
func (g *GitProvider) Clone() error { func (g *GitProvider) Clone() error {
@@ -233,6 +236,11 @@ func (g *GitProvider) ValidateBranch(repo *git.Repository) error {
return nil return nil
} }
func (g *GitProvider) Wait() {
g.mu.Lock()
defer g.mu.Unlock()
}
func (g *GitProvider) ValidateCommit() error { func (g *GitProvider) ValidateCommit() error {
if g.Commit == "" || g.Date.IsZero() { if g.Commit == "" || g.Date.IsZero() {
return InvalidStateError return InvalidStateError

View File

@@ -23,6 +23,7 @@ type Piece struct {
WorkRefs []WorkRef `xml:"werk"` WorkRefs []WorkRef `xml:"werk"`
PieceRefs []PieceRef `xml:"beitrag"` PieceRefs []PieceRef `xml:"beitrag"`
AdditionalRef []AdditionalRef `xml:"beilage"` AdditionalRef []AdditionalRef `xml:"beilage"`
Datum []KGPZDate `xml:"datum"`
Incipit []string `xml:"incipit"` Incipit []string `xml:"incipit"`
Title []string `xml:"titel"` Title []string `xml:"titel"`
Identifier Identifier

View File

@@ -16,10 +16,11 @@ type URL struct {
type AdditionalRef struct { type AdditionalRef struct {
XMLName xml.Name `xml:"beilage"` XMLName xml.Name `xml:"beilage"`
Reference Reference
Datum string `xml:"datum,attr"` Datum string `xml:"datum,attr"`
Nr string `xml:"nr,attr"` Nr string `xml:"nr,attr"`
Von string `xml:"von,attr"` AdditionalNo string `xml:"beilage,attr"`
Bis string `xml:"bis,attr"` Von string `xml:"von,attr"`
Bis string `xml:"bis,attr"`
} }
type IssueRef struct { type IssueRef struct {
@@ -49,6 +50,7 @@ type WorkRef struct {
type PieceRef struct { type PieceRef struct {
XMLName xml.Name `xml:"beitrag"` XMLName xml.Name `xml:"beitrag"`
Page string `xml:"s,attr"`
Reference Reference
} }

View File

@@ -20,22 +20,16 @@ type XMLProvider[T KGPZXML[T]] struct {
} }
func (p *XMLProvider[T]) Load() error { func (p *XMLProvider[T]) Load() error {
var wg sync.WaitGroup
for _, path := range p.paths { for _, path := range p.paths {
wg.Add(1) var data T
go func(path string) { if err := UnmarshalFile(path, &data); err != nil {
defer wg.Done() fmt.Println(err)
var data T return err
if err := UnmarshalFile(path, &data); err != nil { }
fmt.Println(err) p.mu.Lock()
return p.Items = p.Items.Append(data)
} p.mu.Unlock()
p.mu.Lock()
p.Items = p.Items.Append(data)
p.mu.Unlock()
}(path)
} }
wg.Wait()
return nil return nil
} }