cleanup: example refactoring out map[string]struct{} using set.Set

This PR is a little demo of using github.com/hashicorp/go-set to
replace the use of map[T]struct{} as a make-shift set.
This commit is contained in:
Seth Hoenig 2022-07-19 22:26:26 -05:00
parent ea38582b40
commit 93cfeb177b
3 changed files with 52 additions and 78 deletions

1
go.mod
View File

@ -65,6 +65,7 @@ require (
github.com/hashicorp/go-plugin v1.4.3
github.com/hashicorp/go-secure-stdlib/listenerutil v0.1.4
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2
github.com/hashicorp/go-set v0.1.2
github.com/hashicorp/go-sockaddr v1.0.2
github.com/hashicorp/go-syslog v1.0.0
github.com/hashicorp/go-uuid v1.0.2

2
go.sum
View File

@ -755,6 +755,8 @@ github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1 h1:Yc026VyMyIpq1UWRnakHRG01U8fJm+nEfEmjoAb00n8=
github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs=
github.com/hashicorp/go-set v0.1.2 h1:WqFkeT32zKiD/l7zwO1RLF4YwctJwp6IByML0LLa0os=
github.com/hashicorp/go-set v0.1.2/go.mod h1:0jTQeDo6GKX0WMFUV4IicFkxXo9DuoRnUODngpsoYCk=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=

View File

@ -10,6 +10,7 @@ import (
"github.com/armon/go-metrics"
"github.com/hashicorp/go-memdb"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-set"
"github.com/hashicorp/nomad/acl"
"github.com/hashicorp/nomad/helper"
"github.com/hashicorp/nomad/nomad/state"
@ -153,6 +154,27 @@ func (s *ServiceRegistration) DeleteByID(
return nil
}
// ServiceTagSet maps from a service name to a union of tags associated with that service.
type ServiceTagSet map[string]*set.Set[string]
func (s ServiceTagSet) add(service string, tags []string) {
if _, exists := s[service]; !exists {
s[service] = set.From[string](tags)
} else {
s[service].InsertAll(tags)
}
}
// NamespaceServiceTagSet maps from a namespace to a ServiceTagSet
type NamespaceServiceTagSet map[string]ServiceTagSet
func (s NamespaceServiceTagSet) add(namespace, service string, tags []string) {
if _, exists := s[namespace]; !exists {
s[namespace] = make(ServiceTagSet)
}
s[namespace].add(service, tags)
}
// List is used to list service registration held within state. It supports
// single and wildcard namespace listings.
func (s *ServiceRegistration) List(
@ -187,43 +209,21 @@ func (s *ServiceRegistration) List(
return err
}
// Track the unique tags found per service registration name.
serviceTags := make(map[string]map[string]struct{})
// Accumulate the set of tags associated with a particular service name.
tagSet := make(ServiceTagSet)
for raw := iter.Next(); raw != nil; raw = iter.Next() {
serviceReg := raw.(*structs.ServiceRegistration)
// Identify and add any tags for the current service being
// iterated into the map. If the tag has already been seen for
// the same service, it will be overwritten ensuring no
// duplicates.
tags, ok := serviceTags[serviceReg.ServiceName]
if !ok {
serviceTags[serviceReg.ServiceName] = make(map[string]struct{})
tags = serviceTags[serviceReg.ServiceName]
}
for _, tag := range serviceReg.Tags {
tags[tag] = struct{}{}
}
tagSet.add(serviceReg.ServiceName, serviceReg.Tags)
}
// Set the output result with the accumulated set of tags for each service.
var serviceList []*structs.ServiceRegistrationStub
// Iterate the serviceTags map and populate our output result. This
// endpoint handles a single namespace, so we do not need to
// account for multiple.
for service, tags := range serviceTags {
serviceStub := structs.ServiceRegistrationStub{
for service, tags := range tagSet {
serviceList = append(serviceList, &structs.ServiceRegistrationStub{
ServiceName: service,
Tags: make([]string, 0, len(tags)),
}
for tag := range tags {
serviceStub.Tags = append(serviceStub.Tags, tag)
}
serviceList = append(serviceList, &serviceStub)
Tags: tags.List(),
})
}
// Correctly handle situations where a namespace was passed that
@ -292,15 +292,11 @@ func (s *ServiceRegistration) listAllServiceRegistrations(
return err
}
// Track the unique tags found per namespace per service
// registration name.
namespacedServiceTags := make(map[string]map[string]map[string]struct{})
// Accumulate the union of tags per service in each namespace.
nsSvcTagSet := make(NamespaceServiceTagSet)
// Iterate all service registrations.
for raw := iter.Next(); raw != nil; raw = iter.Next() {
// We need to assert the type here in order to check the
// namespace.
serviceReg := raw.(*structs.ServiceRegistration)
// Check whether the service registration is within a namespace
@ -310,55 +306,30 @@ func (s *ServiceRegistration) listAllServiceRegistrations(
continue
}
// Identify and add any tags for the current namespaced service
// being iterated into the map. If the tag has already been
// seen for the same service, it will be overwritten ensuring
// no duplicates.
namespace, ok := namespacedServiceTags[serviceReg.Namespace]
if !ok {
namespacedServiceTags[serviceReg.Namespace] = make(map[string]map[string]struct{})
namespace = namespacedServiceTags[serviceReg.Namespace]
}
tags, ok := namespace[serviceReg.ServiceName]
if !ok {
namespace[serviceReg.ServiceName] = make(map[string]struct{})
tags = namespace[serviceReg.ServiceName]
}
for _, tag := range serviceReg.Tags {
tags[tag] = struct{}{}
}
// Accumulate the set of tags associated with a particular service name in a particular namespace
namespace, service, tags := serviceReg.Namespace, serviceReg.ServiceName, serviceReg.Tags
nsSvcTagSet.add(namespace, service, tags)
}
// Set up our output object. Start with zero size but allocate the
// know length as we wil need to append whilst avoid slice growing.
servicesOutput := make([]*structs.ServiceRegistrationListStub, 0, len(namespacedServiceTags))
for ns, serviceTags := range namespacedServiceTags {
var serviceList []*structs.ServiceRegistrationStub
// Iterate the serviceTags map and populate our output result.
for service, tags := range serviceTags {
serviceStub := structs.ServiceRegistrationStub{
// Create the service stubs, one per namespace, containing each service
// in that namespace, and append that to the final tally of registrations.
var registrations []*structs.ServiceRegistrationListStub
for namespace, tagSet := range nsSvcTagSet {
var stubs []*structs.ServiceRegistrationStub
for service, tags := range tagSet {
stubs = append(stubs, &structs.ServiceRegistrationStub{
ServiceName: service,
Tags: make([]string, 0, len(tags)),
}
for tag := range tags {
serviceStub.Tags = append(serviceStub.Tags, tag)
}
serviceList = append(serviceList, &serviceStub)
Tags: tags.List(),
})
}
servicesOutput = append(servicesOutput, &structs.ServiceRegistrationListStub{
Namespace: ns,
Services: serviceList,
registrations = append(registrations, &structs.ServiceRegistrationListStub{
Namespace: namespace,
Services: stubs,
})
}
// Add the output to the reply object.
reply.Services = servicesOutput
// Set the output on the reply object.
reply.Services = registrations
// Use the index table to populate the query meta as we have no way
// of tracking the max index on deletes.