From a78e632bd66caff82b89213f4ddd99b0dfa8f107 Mon Sep 17 00:00:00 2001 From: Marty Schoch Date: Thu, 6 Apr 2017 17:49:33 -0400 Subject: [PATCH] fix race condition in incorrectly shared state in MultiSearch When performing a MultiSearch, we create child SearchRequests from the original SearchRequest. In doing so we copy many fields. But, copying of the SortOrder was incorrect, as this contains state, and distint SortOrder objects must be used. This change introduces a Copy() method to the SearchSort interface, and to the SortOrder types. MultiSearch now creates a new copy of the SortOrder for each child request. --- index_alias_impl.go | 2 +- search/sort.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/index_alias_impl.go b/index_alias_impl.go index ed084f62..9e9a3594 100644 --- a/index_alias_impl.go +++ b/index_alias_impl.go @@ -432,7 +432,7 @@ func createChildSearchRequest(req *SearchRequest) *SearchRequest { Fields: req.Fields, Facets: req.Facets, Explain: req.Explain, - Sort: req.Sort, + Sort: req.Sort.Copy(), IncludeLocations: req.IncludeLocations, } return &rv diff --git a/search/sort.go b/search/sort.go index f6a0029b..1987718b 100644 --- a/search/sort.go +++ b/search/sort.go @@ -36,6 +36,8 @@ type SearchSort interface { RequiresDocID() bool RequiresScoring() bool RequiresFields() []string + + Copy() SearchSort } func ParseSearchSortObj(input map[string]interface{}) (SearchSort, error) { @@ -205,6 +207,14 @@ func (so SortOrder) UpdateVisitor(field string, term []byte) { } } +func (so SortOrder) Copy() SortOrder { + rv := make(SortOrder, len(so)) + for i, soi := range so { + rv[i] = soi.Copy() + } + return rv +} + // Compare will compare two document matches using the specified sort order // if both are numbers, we avoid converting back to term func (so SortOrder) Compare(cachedScoring, cachedDesc []bool, i, j *DocumentMatch) int { @@ -475,6 +485,12 @@ func (s *SortField) MarshalJSON() ([]byte, error) { return json.Marshal(sfm) } +func (s *SortField) Copy() SearchSort { + var rv SortField + rv = *s + return &rv +} + // SortDocID will sort results by the document identifier type SortDocID struct { Desc bool @@ -512,6 +528,12 @@ func (s *SortDocID) MarshalJSON() ([]byte, error) { return json.Marshal("_id") } +func (s *SortDocID) Copy() SearchSort { + var rv SortDocID + rv = *s + return &rv +} + // SortScore will sort results by the document match score type SortScore struct { Desc bool @@ -549,6 +571,12 @@ func (s *SortScore) MarshalJSON() ([]byte, error) { return json.Marshal("_score") } +func (s *SortScore) Copy() SearchSort { + var rv SortScore + rv = *s + return &rv +} + var maxDistance = string(numeric.MustNewPrefixCodedInt64(math.MaxInt64, 0)) // NewSortGeoDistance creates SearchSort instance for sorting documents by @@ -675,3 +703,9 @@ func (s *SortGeoDistance) MarshalJSON() ([]byte, error) { return json.Marshal(sfm) } + +func (s *SortGeoDistance) Copy() SearchSort { + var rv SortGeoDistance + rv = *s + return &rv +}