Created
June 17, 2016 16:33
-
-
Save algrebe/d461d7341613537b8c3bfb6791aa9111 to your computer and use it in GitHub Desktop.
a resource allocation server using bleve.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// resource allocation server | |
/* | |
put in resources with tags such as | |
{ "ID": "some-id", "tags": []string{ "computer", "windows" } } | |
and if you want a computer that does not use windows | |
query for a resource using a string such as "+computer -windows" | |
*/ | |
package resalloc | |
import ( | |
"github.com/blevesearch/bleve" | |
"github.com/blevesearch/bleve/analysis/analyzers/keyword_analyzer" | |
"sync" | |
) | |
type Resource struct { | |
ID string | |
Tags []string | |
} | |
// Server adds resources to its index and allows search based on its tags. | |
type Server struct { | |
// a mapping of resourceID to resource | |
resourceMap map[string]*Resource | |
// bleve | |
index bleve.Index | |
mapping *bleve.IndexMapping | |
batch *bleve.Batch | |
// makes it safe for concurrent use | |
sync.RWMutex | |
} | |
// Init initializes the server. | |
func (s *Server) Init() error { | |
s.resourceMap = make(map[string]*Resource) | |
s.mapping = bleve.NewIndexMapping() | |
s.mapping.DefaultAnalyzer = keyword_analyzer.Name | |
index, err := bleve.New("", s.mapping) | |
if err != nil { | |
return err | |
} | |
s.index = index | |
s.batch = s.index.NewBatch() | |
s.batch.Reset() | |
return nil | |
} | |
// AddResource adds a resource to the index. | |
func (s *Server) AddResource(r *Resource) error { | |
s.Lock() | |
// I dont want the entire struct, just the ID to tags is enough | |
err := s.index.Index(r.ID, r.Tags) | |
s.resourceMap[r.ID] = r | |
s.Unlock() | |
return err | |
} | |
// AddResources adds many resources to the index. | |
func (s *Server) AddResources(resources []*Resource) error { | |
s.Lock() | |
s.batch.Reset() | |
for _, r := range resources { | |
s.batch.Index(r.ID, r.Tags) | |
s.resourceMap[r.ID] = r | |
} | |
err := s.index.Batch(s.batch) | |
s.batch.Reset() | |
s.Unlock() | |
return err | |
} | |
// GetResource gets a resource based on the tag query string. | |
func (s *Server) GetResource(qs string) (*Resource, error) { | |
query := bleve.NewQueryStringQuery(qs) | |
searchRequest := bleve.NewSearchRequest(query) | |
// NOTE i only need one that matches the tag query. | |
// I don't even need the "top score" . any that satisfy the tag query will do. | |
searchRequest.Size = 1 | |
s.RLock() | |
searchResults, err := s.index.Search(searchRequest) | |
defer s.RUnlock() | |
if err != nil { | |
return nil, err | |
} | |
if searchResults.Total == 0 { | |
return nil, nil | |
} | |
resourceID := searchResults.Hits[0].ID | |
return s.resourceMap[resourceID], nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package resalloc | |
import ( | |
"fmt" | |
"testing" | |
) | |
// GenerateResources generates multiple resources having same tags but different ids. | |
func GenerateResources(num int, tags []string) []*Resource { | |
resources := make([]*Resource, num, num) | |
for i := 0; i < num; i++ { | |
resources[i] = &Resource{ | |
ID: fmt.Sprintf("resource-%d", i), | |
Tags: tags, | |
} | |
} | |
return resources | |
} | |
// BenchmarkSearchUnknownTagIn10kResources searches for an unkown tag across 10k resources. | |
func BenchmarkSearchUnknownTagIn10kResources(b *testing.B) { | |
s := &Server{} | |
if err := s.Init(); err != nil { | |
b.Fatalf("failed to initialize server, err=%s", err) | |
} | |
// Generating 10k resources all having the tag "computer" | |
resources := GenerateResources(10000, []string{"computer"}) | |
if err := s.AddResources(resources); err != nil { | |
b.Fatalf("failed to add resources, err=%s", err) | |
} | |
// I want any laptop that isn't occupied. But there are no laptops, only computers. | |
qs := "+laptop -occupied" | |
b.ResetTimer() | |
for i := 0; i < b.N; i++ { | |
s.GetResource(qs) | |
} | |
} | |
// BenchmarkSearchOnlyTagIn10kResources searches for a tag that is present in | |
// all elements of the index. | |
func BenchmarkSearchOnlyTagIn10kResources(b *testing.B) { | |
s := &Server{} | |
if err := s.Init(); err != nil { | |
b.Fatalf("failed to initialize server, err=%s", err) | |
} | |
// Generating 10k resources all having the tag "computer" | |
resources := GenerateResources(10000, []string{"computer"}) | |
if err := s.AddResources(resources); err != nil { | |
b.Fatalf("failed to add resources, err=%s", err) | |
} | |
// I want one of those computer resources that isn't occupied | |
// searching for a computer from all computers | |
qs := "+computer -occupied" | |
b.ResetTimer() | |
for i := 0; i < b.N; i++ { | |
s.GetResource(qs) | |
} | |
} | |
// BenchmarkSearchRareTagIn10kResources searches for a tag that is present | |
// in minority of the resources in the index. | |
func BenchmarkSearchRareTagIn10kResources(b *testing.B) { | |
s := &Server{} | |
if err := s.Init(); err != nil { | |
b.Fatalf("failed to initaialize server, err=%s", err) | |
} | |
// Generating 9990 resources all having the tag "computer" | |
computers := GenerateResources(9990, []string{"computer"}) | |
// Generating 10 resources all having the tag "laptop" | |
laptops := GenerateResources(10, []string{"laptop"}) | |
if err := s.AddResources(computers); err != nil { | |
b.Fatalf("failed to add computers, err=%s", err) | |
} | |
if err := s.AddResources(laptops); err != nil { | |
b.Fatalf("failed to add laptops, err=%s", err) | |
} | |
// searching for a laptop out of 500 laptops | |
qs := "+laptop -occupied" | |
b.ResetTimer() | |
for i := 0; i < b.N; i++ { | |
s.GetResource(qs) | |
} | |
} | |
// BenchmarkSearchCommonTagIn10kResources searches for a tag that is present | |
// in majority of the resources in the index. | |
func BenchmarkSearchCommonTagIn10kResources(b *testing.B) { | |
s := &Server{} | |
if err := s.Init(); err != nil { | |
b.Fatalf("failed to initaialize server, err=%s", err) | |
} | |
// Generating 9.5k resources all having the tag "computer" | |
computers := GenerateResources(9500, []string{"computer"}) | |
// Generating .5k resources all having the tag "laptop" | |
laptops := GenerateResources(500, []string{"laptop"}) | |
if err := s.AddResources(computers); err != nil { | |
b.Fatalf("failed to add computers, err=%s", err) | |
} | |
if err := s.AddResources(laptops); err != nil { | |
b.Fatalf("failed to add laptops, err=%s", err) | |
} | |
// searching for a computer out of 9500 computers | |
qs := "+computer -occupied" | |
b.ResetTimer() | |
for i := 0; i < b.N; i++ { | |
s.GetResource(qs) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
sample output