bxcodec/faker

fatal error: concurrent map writes

pioz opened this issue · 2 comments

pioz commented

I have a goroutine in a for loop that runs faker.FakeData, here a sample code:

package main

import (
	"reflect"
	"github.com/bxcodec/faker"
)

type User struct {
	Username string `faker:"first_name"`
	Email    string `faker:"email,unique"`
}

func main() {
	user := User{}
	modelType := reflect.TypeOf(user)
	for i := 0; i < 1000; i++ {
		go func() {
			for j := 0; j < 1000; j++ {
				modelCopy := reflect.New(modelType).Interface() // copy model
				faker.FakeData(modelCopy)
			}
		}()
	}
}

I get the follow error:

$ go run bin/faker.go 
fatal error: concurrent map read and map write

goroutine 19 [running]:
runtime.throw(0x1122ca3, 0x21)
	/usr/local/Cellar/go/1.14.1/libexec/src/runtime/panic.go:1114 +0x72 fp=0xc000221760 sp=0xc000221730 pc=0x1030a22
runtime.mapaccess1_faststr(0x10f41a0, 0xc00009a600, 0x10e3609, 0x5, 0x10eb480)
	/usr/local/Cellar/go/1.14.1/libexec/src/runtime/map_faststr.go:21 +0x43c fp=0xc0002217d0 sp=0xc000221760 pc=0x10120fc
github.com/bxcodec/faker.getValue(0x10fb380, 0xc00020e580, 0x199, 0x10fb301, 0x10fb380, 0xc00020e580, 0xc00020e500)
	/Users/pioz/Code/go/src/github.com/bxcodec/faker/faker.go:422 +0x1e2e fp=0xc000221b30 sp=0xc0002217d0 pc=0x10cbade
github.com/bxcodec/faker.getValue(0x10e58e0, 0xc00020e540, 0xc000221f48, 0x107ab78, 0x10fb380, 0xb7a0, 0x10e58e0)
	/Users/pioz/Code/go/src/github.com/bxcodec/faker/faker.go:356 +0x174a fp=0xc000221e90 sp=0xc000221b30 pc=0x10cb3fa
github.com/bxcodec/faker.FakeData(0x10e58e0, 0xc00020e540, 0x16, 0xc00020e501)
	/Users/pioz/Code/go/src/github.com/bxcodec/faker/faker.go:281 +0x1c6 fp=0xc000221f88 sp=0xc000221e90 pc=0x10c9896
main.main.func1(0x1145580, 0x10fb380)
	/Users/pioz/Code/go/src/github.com/pioz/ct/bin/faker.go:23 +0x81 fp=0xc000221fd0 sp=0xc000221f88 pc=0x10da051
runtime.goexit()
	/usr/local/Cellar/go/1.14.1/libexec/src/runtime/asm_amd64.s:1373 +0x1 fp=0xc000221fd8 sp=0xc000221fd0 pc=0x105d6a1
created by main.main
	/Users/pioz/Code/go/src/github.com/pioz/ct/bin/faker.go:20 +0xaf

goroutine 1 [runnable]:
runtime.gopark(0x0, 0x0, 0xc000001008, 0x1)
	/usr/local/Cellar/go/1.14.1/libexec/src/runtime/proc.go:287 +0x130
runtime.main()
	/usr/local/Cellar/go/1.14.1/libexec/src/runtime/proc.go:222 +0x28c
runtime.goexit()
	/usr/local/Cellar/go/1.14.1/libexec/src/runtime/asm_amd64.s:1373 +0x1

goroutine 20 [runnable]:
github.com/bxcodec/faker/support/slice.ContainsValue(0xc000226000, 0xaf, 0x100, 0x10eb480, 0xc00010d5b0, 0xc00010d5b0)
	/Users/pioz/Code/go/src/github.com/bxcodec/faker/support/slice/helpers.go:21 +0x82
github.com/bxcodec/faker.getValue(0x10fb380, 0xc000115240, 0x199, 0x10fb301, 0x10fb380, 0xc000115240, 0xc0001151c0)
	/Users/pioz/Code/go/src/github.com/bxcodec/faker/faker.go:422 +0x1e6b
github.com/bxcodec/faker.getValue(0x10e58e0, 0xc000115200, 0xc000195f48, 0x107ab78, 0x10fb380, 0xb7a0, 0x10e58e0)
	/Users/pioz/Code/go/src/github.com/bxcodec/faker/faker.go:356 +0x174a
github.com/bxcodec/faker.FakeData(0x10e58e0, 0xc000115200, 0x16, 0xc000115201)
	/Users/pioz/Code/go/src/github.com/bxcodec/faker/faker.go:281 +0x1c6
main.main.func1(0x1145580, 0x10fb380)
	/Users/pioz/Code/go/src/github.com/pioz/ct/bin/faker.go:23 +0x81
created by main.main
	/Users/pioz/Code/go/src/github.com/pioz/ct/bin/faker.go:20 +0xaf
...

If I remove the go keyword all works fine.
If I remove the unique option in the Email field tag all works fine.

We haven't try to test this in a concurrent way.

My suggestion, I think you can add mutex on inside your goroutine.

mutex.Lock()
for j := 0; j < 1000; j++ {
    faker.FakeData(modelCopy)
}
mutex.Unlock()

How about it?

pioz commented

This is the playground: https://play.golang.org/p/1T1aAsWgmhu

It would be nice if faker managed concurrency internally.