go-sdk icon indicating copy to clipboard operation
go-sdk copied to clipboard

SaveBulkState panic: error saving state: rpc error: code = Internal desc = failed saving state in state store statestore: no item was updated

Open Imhven opened this issue 4 years ago • 1 comments

Describe the bug When I use state.postgresql as statestore,save data failed. When I use redis statestore,save data success.

To Reproduce postgres.yaml statestore config file:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.postgresql
  version: v1
  metadata:
  - name: connectionString
    value: "host=localhost user=root password=password port=5432 connect_timeout=10 database=statestore"
  - name: actorStateStore
    value: "true"

redis.yaml statestore config file:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  version: v1
  metadata:
  - name: redisHost
    value: localhost:6379
  - name: redisPassword
    value: ""
  - name: actorStateStore
    value: "true"

app code is:


func main() {
	client, err := dapr.NewClient()
	if err != nil {
		panic(err)
	}
	defer client.Close()
	//TODO: use the client here, see below for examples
	ctx := context.Background()
	store := "statestore" // defined in the component YAML
	data := []byte("hello")

	item1 := &dapr.SetStateItem{
		Key: "key11",
		Etag: &dapr.ETag{
			Value: "1",
		},
		Metadata: map[string]string{
			"created-on": time.Now().UTC().String(),
		},
		Value: []byte("hello"),
		Options: &dapr.StateOptions{
			Concurrency: dapr.StateConcurrencyLastWrite,
			Consistency: dapr.StateConsistencyStrong,
		},
	}

	item2 := &dapr.SetStateItem{
		Key: "key21",
		Metadata: map[string]string{
			"created-on": time.Now().UTC().String(),
		},
		Value: []byte("hello again"),
	}

	item3 := &dapr.SetStateItem{
		Key: "key31",
		Etag: &dapr.ETag{
			Value: "1",
		},
		Value: []byte("hello again"),
	}

	if err := client.SaveBulkState(ctx, store, item1, item2, item3); err != nil {
		panic(err)
	}
	keys := []string{"key11", "key21", "key31"}
	_, err = client.GetBulkState(ctx, store, keys, nil, 100)
	if err != nil {
		panic(err)
	}
}
error:
== APP == dapr client initializing for: 127.0.0.1:51916
== APP == panic: error saving state: rpc error: code = Internal desc = failed saving state in state store statestore: no item was updated
== APP == 
== APP == goroutine 1 [running]:
== APP == main.main()
== APP ==       I:/MyProjects/main.go:71 +0xafd
== APP == exit status 2
== APP == data [key:key1 etag:775]: hello

**Expected behavior**
Need SaveBulkState success

Imhven avatar Jan 09 '22 15:01 Imhven

I found that Redis and pgSQL behave differently. For pgSQL, creating a State with the wrong ETag will report an error, while Redis does not have this problem.

The following code exposes the problem better. It will report an error in pgSQL, but not in Redis.

item1 := &dapr.SetStateItem{
	Key:   "key1",
	Value: []byte("hello"),
	Etag: &dapr.ETag{
		Value: "1",
	},
}
if err := client.SaveBulkState(ctx, store, item1); err != nil {
        // report an error
	fmt.Printf("create with wrong ETag will failed in pgSQL, err = %s", err)
}

// should create without ETag
item1 = &dapr.SetStateItem{
	Key:   "key1",
	Value: []byte("hello"),
}
if err := client.SaveBulkState(ctx, store, item1); err != nil {
	panic(err)
}


// update with ETag
item1Resp, err := client.GetState(ctx, store, "key1", nil)
if err != nil {
	panic(err)
}
newItem1 := &dapr.SetStateItem{
	Key:   "key1",
	Value: []byte("hello again"),
	Etag:  &dapr.ETag{Value: item1Resp.Etag},
}
if err := client.SaveBulkState(ctx, store, newItem1); err != nil {
	panic(err)
}

Also, I found that in the Python SDK, even Redis can't pass the wrong ETag at creating state. https://github.com/dapr/python-sdk/tree/master/examples/state_store

117503445 avatar Aug 20 '22 17:08 117503445