Skip to content

Commit 34f6310

Browse files
committed
add GC for cloudevents work client.
Signed-off-by: morvencao <[email protected]>
1 parent e703fc5 commit 34f6310

File tree

35 files changed

+2680
-151
lines changed

35 files changed

+2680
-151
lines changed

go.mod

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ require (
1818
k8s.io/api v0.29.2
1919
k8s.io/apiextensions-apiserver v0.29.0
2020
k8s.io/apimachinery v0.29.2
21-
k8s.io/apiserver v0.29.0
21+
k8s.io/apiserver v0.29.2
2222
k8s.io/client-go v0.29.2
23-
k8s.io/component-base v0.29.1
23+
k8s.io/component-base v0.29.2
2424
k8s.io/klog/v2 v2.120.1
2525
k8s.io/utils v0.0.0-20240310230437-4693a0247e57
2626
open-cluster-management.io/api v0.13.0
@@ -109,13 +109,13 @@ require (
109109
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
110110
go.uber.org/multierr v1.11.0 // indirect
111111
go.uber.org/zap v1.26.0 // indirect
112-
golang.org/x/crypto v0.19.0 // indirect
112+
golang.org/x/crypto v0.21.0 // indirect
113113
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
114-
golang.org/x/net v0.21.0 // indirect
114+
golang.org/x/net v0.23.0 // indirect
115115
golang.org/x/oauth2 v0.16.0 // indirect
116116
golang.org/x/sync v0.6.0 // indirect
117-
golang.org/x/sys v0.17.0 // indirect
118-
golang.org/x/term v0.17.0 // indirect
117+
golang.org/x/sys v0.18.0 // indirect
118+
golang.org/x/term v0.18.0 // indirect
119119
golang.org/x/text v0.14.0 // indirect
120120
golang.org/x/time v0.3.0 // indirect
121121
google.golang.org/appengine v1.6.8 // indirect
@@ -128,10 +128,13 @@ require (
128128
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
129129
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
130130
gopkg.in/yaml.v3 v3.0.1 // indirect
131-
k8s.io/kms v0.29.0 // indirect
132-
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
131+
k8s.io/controller-manager v0.29.2 // indirect
132+
k8s.io/kms v0.29.2 // indirect
133+
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
133134
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0 // indirect
134135
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
135136
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
136137
sigs.k8s.io/yaml v1.4.0 // indirect
137138
)
139+
140+
replace open-cluster-management.io/sdk-go => github.com/morvencao/ocm-sdk-go v0.0.0-20240506074627-941a4e4cfefb

go.sum

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
180180
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
181181
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
182182
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
183+
github.com/morvencao/ocm-sdk-go v0.0.0-20240506074627-941a4e4cfefb h1:3D3BBH7obOq0AgLqZzbOADJFUsNyFBj5/zQE7a2EH30=
184+
github.com/morvencao/ocm-sdk-go v0.0.0-20240506074627-941a4e4cfefb/go.mod h1:vuhp/e3Y4utj8h+BBt+AauZHDE5zWvwIxprgIqQN7f4=
183185
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
184186
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
185187
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -305,8 +307,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
305307
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
306308
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
307309
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
308-
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
309-
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
310+
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
311+
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
310312
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
311313
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
312314
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -321,8 +323,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
321323
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
322324
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
323325
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
324-
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
325-
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
326+
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
327+
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
326328
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
327329
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
328330
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -347,13 +349,13 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
347349
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
348350
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
349351
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
350-
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
351-
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
352+
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
353+
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
352354
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
353355
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
354356
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
355-
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
356-
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
357+
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
358+
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
357359
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
358360
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
359361
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@@ -369,8 +371,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
369371
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
370372
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
371373
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
372-
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
373-
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
374+
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
375+
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
374376
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
375377
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
376378
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -425,24 +427,24 @@ k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3
425427
k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc=
426428
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
427429
k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
428-
k8s.io/apiserver v0.29.0 h1:Y1xEMjJkP+BIi0GSEv1BBrf1jLU9UPfAnnGGbbDdp7o=
429-
k8s.io/apiserver v0.29.0/go.mod h1:31n78PsRKPmfpee7/l9NYEv67u6hOL6AfcE761HapDM=
430+
k8s.io/apiserver v0.29.2 h1:+Z9S0dSNr+CjnVXQePG8TcBWHr3Q7BmAr7NraHvsMiQ=
431+
k8s.io/apiserver v0.29.2/go.mod h1:B0LieKVoyU7ykQvPFm7XSdIHaCHSzCzQWPFa5bqbeMQ=
430432
k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg=
431433
k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA=
432-
k8s.io/component-base v0.29.1 h1:MUimqJPCRnnHsskTTjKD+IC1EHBbRCVyi37IoFBrkYw=
433-
k8s.io/component-base v0.29.1/go.mod h1:fP9GFjxYrLERq1GcWWZAE3bqbNcDKDytn2srWuHTtKc=
434+
k8s.io/component-base v0.29.2 h1:lpiLyuvPA9yV1aQwGLENYyK7n/8t6l3nn3zAtFTJYe8=
435+
k8s.io/component-base v0.29.2/go.mod h1:BfB3SLrefbZXiBfbM+2H1dlat21Uewg/5qtKOl8degM=
436+
k8s.io/controller-manager v0.29.2 h1:S99UKzjvyFWG4WZWaWQ+iu64X9axwzbi4152tFd73+4=
437+
k8s.io/controller-manager v0.29.2/go.mod h1:xghbiyv5l/SVA5yVvRuGDmNVJEGl7MQqPAD0hvjZLhM=
434438
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
435439
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
436-
k8s.io/kms v0.29.0 h1:KJ1zaZt74CgvgV3NR7tnURJ/mJOKC5X3nwon/WdwgxI=
437-
k8s.io/kms v0.29.0/go.mod h1:mB0f9HLxRXeXUfHfn1A7rpwOlzXI1gIWu86z6buNoYA=
438-
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
439-
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
440+
k8s.io/kms v0.29.2 h1:MDsbp98gSlEQs7K7dqLKNNTwKFQRYYvO4UOlBOjNy6Y=
441+
k8s.io/kms v0.29.2/go.mod h1:s/9RC4sYRZ/6Tn6yhNjbfJuZdb8LzlXhdlBnKizeFDo=
442+
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
443+
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
440444
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 h1:gbqbevonBh57eILzModw6mrkbwM0gQBEuevE/AaBsHY=
441445
k8s.io/utils v0.0.0-20240310230437-4693a0247e57/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
442446
open-cluster-management.io/api v0.13.0 h1:dlcJEZlNlE0DmSDctK2s7iWKg9l+Tgb0V78Z040nMuk=
443447
open-cluster-management.io/api v0.13.0/go.mod h1:CuCPEzXDvOyxBB0H1d1eSeajbHqaeGEKq9c63vQc63w=
444-
open-cluster-management.io/sdk-go v0.13.1-0.20240416062924-20307e6fe090 h1:zFmHuW+ztdfUUNslqNW+H1WEcfdEUQHoRDbmdajX340=
445-
open-cluster-management.io/sdk-go v0.13.1-0.20240416062924-20307e6fe090/go.mod h1:w2OaxtCyegxeyFLU42UQ3oxUz01QdsBQkcHI17T/l48=
446448
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0 h1:TgtAeesdhpm2SGwkQasmbeqDo8th5wOBA5h/AjTKA4I=
447449
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0/go.mod h1:VHVDI/KrK4fjnV61bE2g3sA7tiETLn8sooImelsCx3Y=
448450
sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0=

pkg/addonmanager/cloudevents/manager.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,20 @@ import (
44
"context"
55
"time"
66

7+
"k8s.io/apimachinery/pkg/api/meta"
78
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"k8s.io/apimachinery/pkg/util/wait"
10+
cacheddiscovery "k8s.io/client-go/discovery/cached/memory"
811
"k8s.io/client-go/dynamic"
912
"k8s.io/client-go/dynamic/dynamicinformer"
1013
kubeinformers "k8s.io/client-go/informers"
1114
"k8s.io/client-go/kubernetes"
15+
"k8s.io/client-go/metadata"
16+
"k8s.io/client-go/metadata/metadatainformer"
1217
"k8s.io/client-go/rest"
18+
"k8s.io/client-go/restmapper"
1319
"k8s.io/client-go/tools/cache"
20+
"k8s.io/controller-manager/pkg/informerfactory"
1421
addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
1522
addonv1alpha1client "open-cluster-management.io/api/client/addon/clientset/versioned"
1623
addoninformers "open-cluster-management.io/api/client/addon/informers/externalversions"
@@ -21,6 +28,7 @@ import (
2128
"open-cluster-management.io/addon-framework/pkg/addonmanager"
2229
"open-cluster-management.io/addon-framework/pkg/index"
2330
cloudeventswork "open-cluster-management.io/sdk-go/pkg/cloudevents/work"
31+
"open-cluster-management.io/sdk-go/pkg/cloudevents/work/garbagecollector"
2432
"open-cluster-management.io/sdk-go/pkg/cloudevents/work/source/codec"
2533
)
2634

@@ -83,6 +91,11 @@ func (a *cloudeventsAddonManager) Start(ctx context.Context) error {
8391
return err
8492
}
8593

94+
metadataClient, err := metadata.NewForConfig(config)
95+
if err != nil {
96+
return err
97+
}
98+
8699
dynamicClient, err := dynamic.NewForConfig(config)
87100
if err != nil {
88101
return err
@@ -152,6 +165,25 @@ func (a *cloudeventsAddonManager) Start(ctx context.Context) error {
152165
return err
153166
}
154167

168+
cachedClient := cacheddiscovery.NewMemCacheClient(kubeClient)
169+
restMapper := restmapper.NewDeferredDiscoveryRESTMapper(cachedClient)
170+
go wait.Until(func() {
171+
restMapper.Reset()
172+
}, 30*time.Second, ctx.Done())
173+
174+
// Informer transform to trim ManagedFields for memory efficiency.
175+
trim := func(obj interface{}) (interface{}, error) {
176+
if accessor, err := meta.Accessor(obj); err == nil {
177+
accessor.SetManagedFields(nil)
178+
}
179+
return obj, nil
180+
}
181+
sharedInformers := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 10*time.Minute, kubeinformers.WithTransform(trim))
182+
metadataInformers := metadatainformer.NewSharedInformerFactoryWithOptions(metadataClient, 10*time.Minute, metadatainformer.WithTransform(trim))
183+
informerFactory := informerfactory.NewInformerFactory(sharedInformers, metadataInformers)
184+
garbageCollector := garbagecollector.NewGarbageCollector(workClient.WorkV1(), restMapper, workInformers, informerFactory)
185+
go garbageCollector.Run(ctx, 1)
186+
155187
err = a.StartWithInformers(ctx, workClient, workInformers, kubeInformers, addonInformers, clusterInformers, dynamicInformers)
156188
if err != nil {
157189
return err

vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s

Lines changed: 6 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/golang.org/x/net/http2/frame.go

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/golang.org/x/net/http2/pipe.go

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/golang.org/x/net/http2/server.go

Lines changed: 7 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)