Skip to content

fatal error: concurrent map writes when using sheriff.Marshal #61

@lucasoares

Description

@lucasoares

Issue: fatal error: concurrent map writes when using sheriff.Marshal

Description

When calling sheriff.Marshal with certain configuration structs, the application panics with a fatal error: concurrent map writes.

In this particular case, my system doesn't call marshal concurrently on the same object. But even if it did, I don't understand how marshal could fail under concurrent calls, since the map being written appears to be newly created within marshal itself.

Full Stack Trace

fatal error: concurrent map writes

goroutine 5078 [running]:
internal/runtime/maps.fatal({0x15be215?, 0x17c?})
    runtime/panic.go:1046 +0x18
github.com/liip/sheriff.Marshal(0x2b3f040, {0x1433720?, 0xc000f217c0?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:124 +0x5fb
github.com/liip/sheriff.marshalValue(0x2b3f040, {0x135b240?, 0xc001793418?, 0x1389738?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:234 +0x1ea
github.com/liip/sheriff.marshalValue(0x2b3f040, {0x12bbea0?, 0xc0017c1f60?, 0x5?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:240 +0x4c5
github.com/liip/sheriff.Marshal(0x2b3f040, {0x1433e00?, 0xc0017c1f60?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:178 +0x84c
github.com/liip/sheriff.marshalValue(0x2b3f040, {0x1433e00?, 0xc001490160?, 0x5?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:234 +0x1ea
github.com/liip/sheriff.Marshal(0x2b3f040, {0x1469f00?, 0xc0013cb890?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:178 +0x84c
github.com/liip/sheriff.marshalValue(0x2b3f040, {0x1294e80?, 0xc000124d98?, 0x13919e0?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:234 +0x1ea
github.com/liip/sheriff.marshalValue(0x2b3f040, {0x12bab80?, 0xc0000e4d00?, 0x5?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:240 +0x4c5
github.com/liip/sheriff.Marshal(0x2b3f040, {0x149fce0?, 0xc0000e4d00?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:178 +0x84c
github.com/liip/sheriff.marshalValue(0x2b3f040, {0x149fce0?, 0xc001571b40?, 0x5?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:234 +0x1ea
github.com/liip/sheriff.Marshal(0x2b3f040, {0x1469fc0?, 0xc0017c1f40?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:178 +0x84c
github.com/liip/sheriff.marshalValue(0x2b3f040, {0x1469fc0?, 0xc001490140?, 0x5?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:234 +0x1ea
github.com/liip/sheriff.Marshal(0x2b3f040, {0x135b480?, 0xc001410d20?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:178 +0x84c
github.com/liip/sheriff.marshalValue(0x2b3f040, {0x1346a20?, 0xc00156c650?, 0x5f44205ea467d39f?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:234 +0x1ea
github.com/liip/sheriff.Marshal(0x2b3f040, {0x1299b80?, 0xc00156c650?})
    github.com/liip/sheriff@v0.11.1/sheriff.go:82 +0x25e
github.com/stilingue-inteligencia-artificial/firehose-sindicator-manager/internal/config.MarshalAll({0x135b480, 0xc001410d20})

Code Snippet

config.go:49

func MarshalAll(config any) (map[string]any, error) {
	data, err := sheriff.Marshal(allOptions, &config)

	if err != nil {
		return nil, fmt.Errorf("parsing all config data: %w", err)
	}

	if data == nil {
		return nil, nil
	}

	if dataMap, ok := data.(map[string]any); ok {
		return dataMap, nil
	}

	return nil, fmt.Errorf("invalid all config data format: %w", err)
}

Options used:

var allOptions = &sheriff.Options{
	Groups:          []string{"public", "secret"},
	IncludeEmptyTag: true,
}

Example Config Struct

type Config struct {
	Id              string            `json:"id,omitempty" bson:"id,omitempty"`
	Name            *string           `json:"name,omitempty" bson:"name"`
	Description     *string           `json:"description,omitempty" bson:"description"`
	Input           *Input            `json:"input,omitempty" bson:"input,omitempty" groups:"public,secret" validate:"required_without=Output"`
	Output          *Output           `json:"output,omitempty" bson:"output,omitempty" groups:"public,secret" validate:"required_without=Input"`
	Filter          *model.FilterData `json:"filter,omitempty" bson:"filter,omitempty" validate:"excluded_with=Input"`
	Index           []*model.Index    `json:"index,omitempty" bson:"index,omitempty" validate:"excluded_with=Output,dive"`
	Transformer     *Transformer      `json:"transformer,omitempty" bson:"transformer,omitempty" groups:"public,secret"`
	Processor       *Processor        `json:"processor,omitempty" bson:"processor,omitempty" groups:"public,secret"`
	Status          string            `json:"status,omitempty" bson:"status,omitempty" validate:"oneof=ACTIVE INACTIVE|isdefault" enums:"ACTIVE,INACTIVE"`
	Groups          []string          `json:"groups,omitempty" bson:"groups,omitempty" validate:"max=1"`
	SharingGroups   []string          `json:"sharing_groups,omitempty" bson:"sharing_groups,omitempty" validate:"unique"`
	VariableUsage   []string          `json:"variable_usage,omitempty" bson:"variable_usage,omitempty"`
	SecretUsage     []string          `json:"secret_usage,omitempty" bson:"secret_usage,omitempty"`
	UpdatedAtMillis int64             `json:"-" bson:"-"`
}

Notes

  • It might be related to sheriff.Marshal modifying internal maps without proper synchronization.

Environment

  • Go version: 1.23
  • Sheriff version: v0.11.1
  • OS: Linux

ps.: I tried to update sheriff to the latest version to check if it would fix it but then I find that it have many breaking changes like #62

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions