Skip to content

Commit 0b65cca

Browse files
authored
Add distributed lock api support (dapr#297)
* add distributed lock api support Signed-off-by: yaron2 <schneider.yaron@live.com> * go mod tidy for pub/sub Signed-off-by: yaron2 <schneider.yaron@live.com> * go mod tidy for state Signed-off-by: yaron2 <schneider.yaron@live.com> * go mod tidy for service invocation Signed-off-by: yaron2 <schneider.yaron@live.com> * linter Signed-off-by: yaron2 <schneider.yaron@live.com> * added unlock tests Signed-off-by: yaron2 <schneider.yaron@live.com>
1 parent a1648db commit 0b65cca

File tree

13 files changed

+388
-182
lines changed

13 files changed

+388
-182
lines changed

Readme.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,31 @@ opt := map[string]string{
214214
secret, err := client.GetSecret(ctx, "store-name", "secret-name", opt)
215215
```
216216

217+
##### Distributed Lock
218+
219+
The Dapr client provides methods to grab a distributed lock and unlock it.
220+
221+
Grab a lock:
222+
223+
```go
224+
ctx := context.Background()
225+
store := "my-store" // defined in the component YAML
226+
227+
r, err := testClient.TryLockAlpha1(ctx, testLockStore, &LockRequest{
228+
OwnerID: "owner1",
229+
ResourceID: "resource1",
230+
ExpiryInSeconds: 5,
231+
})
232+
```
233+
234+
Unlock a lock:
235+
236+
```go
237+
r, err := testClient.UnlockAlpha1(ctx, testLockStore, &UnlockRequest{
238+
OwnerID: "owner1",
239+
ResourceID: "resource1",
240+
})
241+
```
217242

218243
##### Authentication
219244

client/client.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727

2828
"github.com/pkg/errors"
2929
"google.golang.org/grpc"
30+
"google.golang.org/grpc/credentials/insecure"
3031
"google.golang.org/grpc/metadata"
3132
"google.golang.org/protobuf/types/known/emptypb"
3233

@@ -128,6 +129,12 @@ type Client interface {
128129
// DeleteBulkStateItems deletes content for multiple items from store.
129130
DeleteBulkStateItems(ctx context.Context, storeName string, items []*DeleteStateItem) error
130131

132+
// TryLockAlpha1 attempts to grab a lock from a lock store.
133+
TryLockAlpha1(ctx context.Context, storeName string, request *LockRequest) (*LockResponse, error)
134+
135+
// UnlockAlpha1 deletes unlocks a lock from a lock store.
136+
UnlockAlpha1(ctx context.Context, storeName string, request *UnlockRequest) (*UnlockResponse, error)
137+
131138
// Shutdown the sidecar.
132139
Shutdown(ctx context.Context) error
133140

@@ -213,7 +220,7 @@ func NewClientWithAddress(address string) (client Client, err error) {
213220
conn, err := grpc.DialContext(
214221
ctx,
215222
address,
216-
grpc.WithInsecure(),
223+
grpc.WithTransportCredentials(insecure.NewCredentials()),
217224
grpc.WithBlock(),
218225
)
219226
if err != nil {
@@ -234,7 +241,7 @@ func NewClientWithSocket(socket string) (client Client, err error) {
234241
}
235242
logger.Printf("dapr client initializing for: %s", socket)
236243
addr := fmt.Sprintf("unix://%s", socket)
237-
conn, err := grpc.Dial(addr, grpc.WithInsecure())
244+
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
238245
if err != nil {
239246
return nil, errors.Wrapf(err, "error creating connection to '%s': %v", addr, err)
240247
}

client/client_test.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/pkg/errors"
3030
"github.com/stretchr/testify/assert"
3131
"google.golang.org/grpc"
32+
"google.golang.org/grpc/credentials/insecure"
3233
"google.golang.org/grpc/test/bufconn"
3334
"google.golang.org/protobuf/types/known/anypb"
3435

@@ -149,7 +150,7 @@ func getTestClient(ctx context.Context) (client Client, closer func()) {
149150
return l.Dial()
150151
})
151152

152-
c, err := grpc.DialContext(ctx, "", d, grpc.WithInsecure())
153+
c, err := grpc.DialContext(ctx, "", d, grpc.WithTransportCredentials(insecure.NewCredentials()))
153154
if err != nil {
154155
logger.Fatalf("failed to dial test context: %v", err)
155156
}
@@ -202,6 +203,18 @@ type testDaprServer struct {
202203
configurationSubscriptionID map[string]chan struct{}
203204
}
204205

206+
func (s *testDaprServer) TryLockAlpha1(ctx context.Context, req *pb.TryLockRequest) (*pb.TryLockResponse, error) {
207+
return &pb.TryLockResponse{
208+
Success: true,
209+
}, nil
210+
}
211+
212+
func (s *testDaprServer) UnlockAlpha1(ctx context.Context, req *pb.UnlockRequest) (*pb.UnlockResponse, error) {
213+
return &pb.UnlockResponse{
214+
Status: pb.UnlockResponse_SUCCESS,
215+
}, nil
216+
}
217+
205218
func (s *testDaprServer) InvokeService(ctx context.Context, req *pb.InvokeServiceRequest) (*commonv1pb.InvokeResponse, error) {
206219
if req.Message == nil {
207220
return &commonv1pb.InvokeResponse{

client/lock.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
Copyright 2022 The Dapr Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package client
15+
16+
import (
17+
"context"
18+
19+
"github.com/pkg/errors"
20+
21+
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
22+
)
23+
24+
// LockRequest is the lock request object.
25+
type LockRequest struct {
26+
ResourceID string
27+
OwnerID string
28+
ExpiryInSeconds int32
29+
}
30+
31+
// UnlockRequest is the unlock request object.
32+
type UnlockRequest struct {
33+
ResourceID string
34+
OwnerID string
35+
}
36+
37+
// LockResponse is the lock operation response object.
38+
type LockResponse struct {
39+
Success bool
40+
}
41+
42+
// UnlockResponse is the unlock operation response object.
43+
type UnlockResponse struct {
44+
StatusCode int32
45+
Status string
46+
}
47+
48+
// TryLockAlpha1 attempts to grab a lock from a lock store.
49+
func (c *GRPCClient) TryLockAlpha1(ctx context.Context, storeName string, request *LockRequest) (*LockResponse, error) {
50+
if storeName == "" {
51+
return nil, errors.New("storeName is empty")
52+
}
53+
54+
if request == nil {
55+
return nil, errors.New("request is nil")
56+
}
57+
58+
req := pb.TryLockRequest{
59+
ResourceId: request.ResourceID,
60+
LockOwner: request.OwnerID,
61+
ExpiryInSeconds: request.ExpiryInSeconds,
62+
StoreName: storeName,
63+
}
64+
65+
resp, err := c.protoClient.TryLockAlpha1(ctx, &req)
66+
if err != nil {
67+
return nil, errors.Wrap(err, "error getting lock")
68+
}
69+
70+
return &LockResponse{
71+
Success: resp.Success,
72+
}, nil
73+
}
74+
75+
// UnlockAlpha1 deletes unlocks a lock from a lock store.
76+
func (c *GRPCClient) UnlockAlpha1(ctx context.Context, storeName string, request *UnlockRequest) (*UnlockResponse, error) {
77+
if storeName == "" {
78+
return nil, errors.New("storeName is empty")
79+
}
80+
81+
if request == nil {
82+
return nil, errors.New("request is nil")
83+
}
84+
85+
req := pb.UnlockRequest{
86+
ResourceId: request.ResourceID,
87+
LockOwner: request.OwnerID,
88+
StoreName: storeName,
89+
}
90+
91+
resp, err := c.protoClient.UnlockAlpha1(ctx, &req)
92+
if err != nil {
93+
return nil, errors.Wrap(err, "error getting lock")
94+
}
95+
96+
return &UnlockResponse{
97+
StatusCode: int32(resp.Status),
98+
Status: pb.UnlockResponse_Status_name[int32(resp.Status)],
99+
}, nil
100+
}

client/lock_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
Copyright 2021 The Dapr Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package client
15+
16+
import (
17+
"context"
18+
"testing"
19+
20+
"github.com/stretchr/testify/assert"
21+
22+
pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
23+
)
24+
25+
const (
26+
testLockStore = "store"
27+
)
28+
29+
func TestLock(t *testing.T) {
30+
ctx := context.Background()
31+
32+
t.Run("try lock invalid store name", func(t *testing.T) {
33+
r, err := testClient.TryLockAlpha1(ctx, "", &LockRequest{})
34+
assert.Nil(t, r)
35+
assert.Error(t, err)
36+
})
37+
38+
t.Run("try lock invalid request", func(t *testing.T) {
39+
r, err := testClient.TryLockAlpha1(ctx, testLockStore, nil)
40+
assert.Nil(t, r)
41+
assert.Error(t, err)
42+
})
43+
44+
t.Run("try lock", func(t *testing.T) {
45+
r, err := testClient.TryLockAlpha1(ctx, testLockStore, &LockRequest{
46+
OwnerID: "owner1",
47+
ResourceID: "resource1",
48+
ExpiryInSeconds: 5,
49+
})
50+
assert.NotNil(t, r)
51+
assert.NoError(t, err)
52+
assert.True(t, r.Success)
53+
})
54+
55+
t.Run("unlock invalid store name", func(t *testing.T) {
56+
r, err := testClient.UnlockAlpha1(ctx, "", &UnlockRequest{
57+
OwnerID: "owner1",
58+
ResourceID: "resource1",
59+
})
60+
assert.Nil(t, r)
61+
assert.Error(t, err)
62+
})
63+
64+
t.Run("unlock invalid request", func(t *testing.T) {
65+
r, err := testClient.UnlockAlpha1(ctx, "testLockStore", nil)
66+
assert.Nil(t, r)
67+
assert.Error(t, err)
68+
})
69+
70+
t.Run("unlock", func(t *testing.T) {
71+
r, err := testClient.UnlockAlpha1(ctx, testLockStore, &UnlockRequest{
72+
OwnerID: "owner1",
73+
ResourceID: "resource1",
74+
})
75+
assert.NotNil(t, r)
76+
assert.NoError(t, err)
77+
assert.Equal(t, pb.UnlockResponse_SUCCESS.String(), r.Status)
78+
})
79+
}

examples/hello-world/go.mod

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ require (
1010
)
1111

1212
require (
13-
github.com/dapr/dapr v1.6.1-0.20220223052122-c503710ab2d1 // indirect
13+
github.com/dapr/dapr v1.7.4-0.20220620022343-b22c67f67b3c // indirect
1414
github.com/golang/protobuf v1.5.2 // indirect
1515
github.com/pkg/errors v0.9.1 // indirect
16-
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
17-
golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect
16+
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect
17+
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
1818
golang.org/x/text v0.3.7 // indirect
19-
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2 // indirect
20-
google.golang.org/grpc v1.40.0 // indirect
21-
google.golang.org/protobuf v1.27.1 // indirect
19+
google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf // indirect
20+
google.golang.org/grpc v1.47.0 // indirect
21+
google.golang.org/protobuf v1.28.0 // indirect
2222
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
2323
)
2424

0 commit comments

Comments
 (0)