Fix Locking

This commit is contained in:
snoutie 2025-05-23 19:48:22 +02:00
parent f8ab5c8358
commit 3e07e2f06e
6 changed files with 77 additions and 59 deletions

View File

@ -43,7 +43,6 @@ type fuzzyResult struct {
type resultBuffer struct { type resultBuffer struct {
createdAt time.Time createdAt time.Time
hits int hits int
mu sync.Mutex
results []fuzzyResult results []fuzzyResult
} }
@ -56,6 +55,7 @@ var (
var searchTargets map[table.TechnicalName][]string = make(map[table.TechnicalName][]string) var searchTargets map[table.TechnicalName][]string = make(map[table.TechnicalName][]string)
var resultsBuffer map[string]*resultBuffer = make(map[string]*resultBuffer) var resultsBuffer map[string]*resultBuffer = make(map[string]*resultBuffer)
var resultsBufferMu sync.Mutex
func Load() error { func Load() error {
keywords, err := model.GetKeywordsModel() keywords, err := model.GetKeywordsModel()
@ -95,6 +95,9 @@ func GetPage(r *http.Request) int {
} }
func GetBuffer(searchTerm string) (*resultBuffer, error) { func GetBuffer(searchTerm string) (*resultBuffer, error) {
resultsBufferMu.Lock()
defer resultsBufferMu.Unlock()
buffer, exists := resultsBuffer[searchTerm] buffer, exists := resultsBuffer[searchTerm]
if !exists { if !exists {
@ -186,11 +189,13 @@ func BuildBuffer(searchTerm string) error {
return 0 return 0
}) })
resultsBufferMu.Lock()
resultsBuffer[searchTerm] = &resultBuffer{ resultsBuffer[searchTerm] = &resultBuffer{
createdAt: time.Now(), createdAt: time.Now(),
hits: hits, hits: hits,
results: fuzzyResults, results: fuzzyResults,
} }
resultsBufferMu.Unlock()
return nil return nil
} }
@ -212,9 +217,6 @@ func GetPages(buffer *resultBuffer, p int) (int, int) {
} }
func Search(buffer *resultBuffer, page int, maxPage int) *model.ResultsModel { func Search(buffer *resultBuffer, page int, maxPage int) *model.ResultsModel {
buffer.mu.Lock()
defer buffer.mu.Unlock()
resultsModel := model.NewResultsModel() resultsModel := model.NewResultsModel()
for i := page * pageStep; i < page*pageStep+pageStep; i++ { for i := page * pageStep; i < page*pageStep+pageStep; i++ {
if i > len(buffer.results)-1 { if i > len(buffer.results)-1 {
@ -295,11 +297,12 @@ func Search(buffer *resultBuffer, page int, maxPage int) *model.ResultsModel {
} }
*/ */
func ClearOldBuffers() { func ClearOldBuffers() {
resultsBufferMu.Lock()
defer resultsBufferMu.Unlock()
for key, buffer := range resultsBuffer { for key, buffer := range resultsBuffer {
if time.Since(buffer.createdAt) >= time.Minute*10 { if time.Since(buffer.createdAt) >= time.Minute*10 {
buffer.mu.Lock()
delete(resultsBuffer, key) delete(resultsBuffer, key)
buffer.mu.Unlock()
} }
} }
} }

View File

@ -5,8 +5,11 @@ import (
"fmt" "fmt"
"net/http" "net/http"
db_table "api-cds-search/cmd/database/table"
"code.achtarmig.org/pas/ui/element" "code.achtarmig.org/pas/ui/element"
"code.achtarmig.org/pas/ui/element/container" "code.achtarmig.org/pas/ui/element/container"
"code.achtarmig.org/pas/ui/element/form"
"code.achtarmig.org/pas/ui/element/input" "code.achtarmig.org/pas/ui/element/input"
"code.achtarmig.org/pas/ui/element/option" "code.achtarmig.org/pas/ui/element/option"
"code.achtarmig.org/pas/ui/element/table" "code.achtarmig.org/pas/ui/element/table"
@ -21,14 +24,22 @@ func cdsDetails() {
p.OnRequest = onRequestDetails p.OnRequest = onRequestDetails
container.New(p, "Details", func(p *container.Element) { container.New(p, "Details", func(p *container.Element) {
p.Justify = option.JustifyCenter p.Justify = option.JustifyCenter
input.New(p, "q", input.TypeText, func(p *input.Element) { form.New(p, "qForm", func(p *form.Element) {
p.Placeholder = "CDSViewTechnicalName" p.Method = http.MethodGet
p.Shape = option.ShapeRound p.Target = "#DetailsTable"
p.SetHTMX(element.HTMX{ p.URL = "/cds"
Method: http.MethodGet, p.PushURL = true
Target: "#DetailsTable", input.New(p, "q", input.TypeText, func(p *input.Element) {
URL: "/cds", p.ValidateInput = true
PushURL: true, p.RequestDelay = 200
p.Parameter = true
p.Placeholder = "CDSViewTechnicalName"
p.Shape = option.ShapeRound
p.OnValidate = func(e *input.Element, v any) error {
_, err := db_table.GetCDSView(db_table.TechnicalName(v.(string)))
return err
}
}) })
}) })
}) })
@ -43,7 +54,7 @@ func cdsDetails() {
} }
func onRequestDetails(e *view.Element, w http.ResponseWriter, r *http.Request) view.ProcessElements { func onRequestDetails(e *view.Element, w http.ResponseWriter, r *http.Request) view.ProcessElements {
cds, _ := e.GetElement("q").(*input.Element).GetDataText() cds, _ := e.GetElement("q").(*input.Element).GetDataString()
e.Title = fmt.Sprintf("CDSDetails - %s", cds) e.Title = fmt.Sprintf("CDSDetails - %s", cds)

View File

@ -40,7 +40,7 @@ func noResults(v *view.Element) element.ElementInterface {
cont := v.GetElement("SearchResultsContainer") cont := v.GetElement("SearchResultsContainer")
cont.DeleteAllChildren() cont.DeleteAllChildren()
pl, _ := placeholder.New(cont, "", func(p *placeholder.Element) { pl := placeholder.New(cont, "", func(p *placeholder.Element) {
p.Icon = option.IconLoupe p.Icon = option.IconLoupe
p.Title = "No Results" p.Title = "No Results"
p.Subtitle = "Try refining your search term" p.Subtitle = "Try refining your search term"

View File

@ -19,7 +19,7 @@ func noSearch(v *view.Element) element.ElementInterface {
cont := v.GetElement("SearchResultsContainer") cont := v.GetElement("SearchResultsContainer")
cont.DeleteAllChildren() cont.DeleteAllChildren()
pl, _ := placeholder.New(cont, "", func(p *placeholder.Element) { pl := placeholder.New(cont, "", func(p *placeholder.Element) {
p.Icon = option.IconLoupe p.Icon = option.IconLoupe
p.Title = "Search CDS-Views" p.Title = "Search CDS-Views"
p.Subtitle = "Find CDS-Views from the Business Accelerator Hub" p.Subtitle = "Find CDS-Views from the Business Accelerator Hub"
@ -33,7 +33,7 @@ func searchView() {
p.Title = "CDS-Search" p.Title = "CDS-Search"
p.OnRequest = func(v *view.Element, w http.ResponseWriter, r *http.Request) view.ProcessElements { p.OnRequest = func(v *view.Element, w http.ResponseWriter, r *http.Request) view.ProcessElements {
inputQuery := v.GetElement("q").(*input.Element) inputQuery := v.GetElement("q").(*input.Element)
inputQueryValue, _ := v.GetElement("q").(*input.Element).GetDataText() inputQueryValue, _ := v.GetElement("q").(*input.Element).GetDataString()
searchTerm := search.GetSearchTerm(inputQueryValue) searchTerm := search.GetSearchTerm(inputQueryValue)
if inputQueryValue == "" { if inputQueryValue == "" {
@ -43,7 +43,7 @@ func searchView() {
inputPage := v.GetElement("p").(*input.Element) inputPage := v.GetElement("p").(*input.Element)
inputMaxPage := v.GetElement("MaxPage").(*input.Element) inputMaxPage := v.GetElement("MaxPage").(*input.Element)
currentPage, _ := inputPage.GetDataNumber() currentPage, _ := inputPage.GetDataInt()
if inputQuery.GetDataChanged() && inputQueryValue != "" { if inputQuery.GetDataChanged() && inputQueryValue != "" {
buf, err := search.GetBuffer(searchTerm) buf, err := search.GetBuffer(searchTerm)
@ -70,25 +70,24 @@ func searchView() {
return view.ProcessElements{ return view.ProcessElements{
Partial: []element.ElementInterface{v.GetElement("SearchResultsContainer")}, Partial: []element.ElementInterface{v.GetElement("SearchResultsContainer")},
OOBUpdate: []element.ElementInterface{inputPage, inputMaxPage}, OOBUpdate: []element.ElementInterface{v.GetElement("pagerForm")},
} }
} }
container.New(p, "MainContainer", func(p *container.Element) { container.New(p, "MainContainer", func(p *container.Element) {
p.Justify = option.JustifyCenter p.Justify = option.JustifyCenter
form.New(p, "SearchForm", func(p *form.Element) { form.New(p, "SearchForm", func(p *form.Element) {
p.SetHTMX(element.HTMX{ p.Method = http.MethodGet
Method: http.MethodGet, p.Target = "#SearchResultsContainer>*"
Target: "#SearchResultsContainer>*", p.Swap = "outerHTML"
Swap: "outerHTML", p.URL = "/search"
URL: "/search", p.PushURL = true
PushURL: true, p.Params = "q, p"
Params: "q, p",
})
container.New(p, "SearchFormLinked", func(p *container.Element) { container.New(p, "SearchFormLinked", func(p *container.Element) {
p.Justify = option.JustifyCenter p.Justify = option.JustifyCenter
p.LinkedHorizontal = true p.LinkedHorizontal = true
input.New(p, "q", input.TypeText, func(p *input.Element) { input.New(p, "q", input.TypeText, func(p *input.Element) {
p.Parameter = true
p.Placeholder = "Search here" p.Placeholder = "Search here"
p.Shape = option.ShapeRound p.Shape = option.ShapeRound
@ -110,38 +109,43 @@ func searchView() {
container.New(p, "CenterPager", func(p *container.Element) { container.New(p, "CenterPager", func(p *container.Element) {
p.Justify = option.JustifyCenter p.Justify = option.JustifyCenter
container.New(p, "Pager", func(p *container.Element) { container.New(p, "RightCenterPager", func(p *container.Element) {
p.MaxWidth = 42 p.MaxWidth = 42
p.MinWidth = 10 p.MinWidth = 10
p.Justify = option.JustifyRight p.Justify = option.JustifyRight
p.LinkedHorizontal = true form.New(p, "pagerForm", func(p *form.Element) {
input.New(p, "p", input.TypeNumber, func(p *input.Element) { p.MaxWidth = -1
p.Shape = option.ShapeRound p.MinWidth = 8
p.Method = http.MethodGet
p.Include = "[name='q'], [name='p']"
p.Target = "#SearchResultsContainer>*"
p.URL = "/search"
p.Swap = "outerHTML"
p.Trigger = "change from:#p"
p.PushURL = true
container.New(p, "LinkPager", func(p *container.Element) {
p.LinkedHorizontal = true
input.New(p, "p", input.TypeNumber, func(p *input.Element) {
p.Parameter = true
p.Shape = option.ShapeRound
p.SetMin = true p.SetMin = true
p.Min = 0 p.Min = 0
p.SetMax = true p.SetMax = true
p.Max = 0 p.Max = 0
p.MinWidth = 4 p.MinWidth = 4
})
p.SetHTMX(element.HTMX{ input.New(p, "MaxPage", input.TypeNumber, func(p *input.Element) {
Method: http.MethodGet, p.Disabled = true
Include: "[name='q'], [name='p']", p.Shape = option.ShapeRound
Target: "#SearchResultsContainer>*", p.MinWidth = 4
URL: "/search", p.SetMin = true
Swap: "outerHTML", p.SetMax = true
PushURL: true, })
}) })
}) })
input.New(p, "MaxPage", input.TypeNumber, func(p *input.Element) {
p.Disabled = true
p.Shape = option.ShapeRound
p.MinWidth = 4
p.SetMin = true
p.SetMax = true
})
}) })
}) })

2
go.mod
View File

@ -3,7 +3,7 @@ module api-cds-search
go 1.23.7 go 1.23.7
require ( require (
code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa code.achtarmig.org/pas/ui v0.0.0-20250523174346-c02bf81e9cf3
github.com/glebarez/go-sqlite v1.22.0 github.com/glebarez/go-sqlite v1.22.0
github.com/go-chi/chi v1.5.5 github.com/go-chi/chi v1.5.5
github.com/lithammer/fuzzysearch v1.1.8 github.com/lithammer/fuzzysearch v1.1.8

12
go.sum
View File

@ -1,11 +1,11 @@
code.achtarmig.org/pas/ui v0.0.0-20250517190325-787754158160 h1:nhr5BUMdLRJrj/DnO4LZdsPwgIz3vYvaf08LdND7Eu8=
code.achtarmig.org/pas/ui v0.0.0-20250517190325-787754158160/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0=
code.achtarmig.org/pas/ui v0.0.0-20250520192537-02c6f64d9a4f h1:gj8Lq5QgB8LlOxqrPPmj673gW9brX3TsYI9Ijp281Vw=
code.achtarmig.org/pas/ui v0.0.0-20250520192537-02c6f64d9a4f/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0=
code.achtarmig.org/pas/ui v0.0.0-20250520194044-0b9d43111bb8 h1:Zzr3PGFp5qVZxRrZOOWSe5Vua7s3dTjIAZmF00XglN8=
code.achtarmig.org/pas/ui v0.0.0-20250520194044-0b9d43111bb8/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0=
code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa h1:rMY1ksN8wtFO8ago/jMEgz52F/pFQIZLfN1YFSyTeEg= code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa h1:rMY1ksN8wtFO8ago/jMEgz52F/pFQIZLfN1YFSyTeEg=
code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0= code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0=
code.achtarmig.org/pas/ui v0.0.0-20250523170703-92fa955c1776 h1:VM1tir3yk+4iuJbDRVPmsLjrk1rJ+gTRPJh6LFTMMh0=
code.achtarmig.org/pas/ui v0.0.0-20250523170703-92fa955c1776/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo=
code.achtarmig.org/pas/ui v0.0.0-20250523172517-f2c3e6308b7d h1:/vt1BgqVi8fW7cdZWGMOfMarIRkrQuBMBdKhwEziaGI=
code.achtarmig.org/pas/ui v0.0.0-20250523172517-f2c3e6308b7d/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo=
code.achtarmig.org/pas/ui v0.0.0-20250523174346-c02bf81e9cf3 h1:Lsq0qH31XZG5b9j51y9vmXx+ZBmLD5H64aPlNBVjDfc=
code.achtarmig.org/pas/ui v0.0.0-20250523174346-c02bf81e9cf3/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=