github.com/portworx/kvdb@v0.0.0-20241107215734-a185a966f535/zookeeper/kv_zookeeper.go (about) 1 // Package zookeeper implements the KVDB interface based for zookeeper. 2 // Code from docker/libkv was leveraged to build parts of this module. 3 4 package zookeeper 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "fmt" 10 "strconv" 11 "strings" 12 "sync" 13 "time" 14 15 "github.com/portworx/kvdb" 16 "github.com/portworx/kvdb/common" 17 "github.com/samuel/go-zookeeper/zk" 18 "github.com/sirupsen/logrus" 19 ) 20 21 const ( 22 // Name is the name of this kvdb implementation 23 Name = "zookeeper-kv" 24 // SOH control character 25 SOH = "\x01" 26 // defaultSessionTimeout is the client session timeout. If the session times out, 27 // then all ephemeral zk nodes are deleted by the zk server. As all lock keys are 28 // ephemeral nodes, all locks will be released after this timeout when a node 29 // dies or closes the zookeeper connection. 30 defaultSessionTimeout = 16 * time.Second 31 defaultRetryCount = 10 32 ) 33 34 var ( 35 defaultServers = []string{"127.0.0.1:2181"} 36 ) 37 38 type zookeeperKV struct { 39 common.BaseKvdb 40 client *zk.Conn 41 options map[string]string 42 domain string 43 kvdb.Controller 44 } 45 46 // zookeeperLock combines Mutex and channel 47 type zookeeperLock struct { 48 Done chan struct{} 49 Unlocked bool 50 sync.Mutex 51 } 52 53 // LockerIDInfo id of locker 54 type LockerIDInfo struct { 55 LockerID string 56 } 57 58 // New constructs a new kvdb.Kvdb with zookeeper implementation 59 func New( 60 domain string, 61 servers []string, 62 options map[string]string, 63 fatalErrorCb kvdb.FatalErrorCB, 64 ) (kvdb.Kvdb, error) { 65 return newClient(domain, servers, options, fatalErrorCb) 66 } 67 68 // Version returns the supported version of the zookeeper implementation 69 func Version(url string, kvdbOptions map[string]string) (string, error) { 70 return kvdb.ZookeeperVersion1, nil 71 } 72 73 // Used to create a zookeeper client for testing 74 func newClient( 75 domain string, 76 servers []string, 77 options map[string]string, 78 fatalErrorCb kvdb.FatalErrorCB, 79 ) (*zookeeperKV, error) { 80 if len(servers) == 0 { 81 servers = defaultServers 82 } 83 84 if domain != "" { 85 domain = normalize(domain) 86 } 87 88 zkClient, _, err := zk.Connect(servers, defaultSessionTimeout) 89 if err != nil { 90 return nil, err 91 } 92 93 return &zookeeperKV{ 94 BaseKvdb: common.BaseKvdb{ 95 FatalCb: fatalErrorCb, 96 LockTryDuration: kvdb.DefaultLockTryDuration, 97 }, 98 client: zkClient, 99 domain: domain, 100 Controller: kvdb.ControllerNotSupported, 101 }, nil 102 } 103 104 func (z *zookeeperKV) closeClient() { 105 z.client.Close() 106 } 107 108 func (z *zookeeperKV) String() string { 109 return Name 110 } 111 112 func (z *zookeeperKV) Capabilities() int { 113 return 0 114 } 115 116 func (z *zookeeperKV) Get(key string) (*kvdb.KVPair, error) { 117 var ( 118 err error 119 resp []byte 120 meta *zk.Stat 121 ) 122 123 key = z.domain + normalize(key) 124 125 for i := 0; i < z.getRetryCount(); i++ { 126 resp, meta, err = z.client.Get(key) 127 if err == nil && resp != nil { 128 // Rare case where Get returns SOH control character 129 // instead of the actual value 130 if string(resp) == SOH { 131 continue 132 } 133 if len(resp) == 0 { 134 return nil, kvdb.ErrNotFound 135 } 136 return z.resultToKvPair(key, resp, "get", meta), nil 137 } 138 if err == zk.ErrNoNode { 139 return nil, kvdb.ErrNotFound 140 } 141 return nil, err 142 } 143 144 return nil, err 145 } 146 147 func (z *zookeeperKV) GetVal(key string, val interface{}) (*kvdb.KVPair, error) { 148 kvp, err := z.Get(key) 149 if err != nil { 150 return nil, err 151 } 152 if err := json.Unmarshal(kvp.Value, val); err != nil { 153 return kvp, kvdb.ErrUnmarshal 154 } 155 return kvp, nil 156 } 157 158 func (z *zookeeperKV) GetWithCopy( 159 key string, 160 copySelect kvdb.CopySelect, 161 ) (interface{}, error) { 162 return nil, kvdb.ErrNotSupported 163 } 164 165 // Put creates a node if it does not exist and sets the given value 166 func (z *zookeeperKV) Put( 167 key string, 168 val interface{}, 169 ttl uint64, 170 ) (*kvdb.KVPair, error) { 171 if ttl != 0 { 172 return nil, kvdb.ErrTTLNotSupported 173 } 174 175 bval, err := common.ToBytes(val) 176 if err != nil { 177 return nil, err 178 } 179 if len(bval) == 0 { 180 return nil, kvdb.ErrEmptyValue 181 } 182 183 err = z.createFullPath(key, false) 184 if err != nil && err != zk.ErrNodeExists { 185 return nil, err 186 } 187 188 key = z.domain + normalize(key) 189 meta, err := z.client.Set(key, bval, -1) 190 if err != nil { 191 return nil, err 192 } 193 return z.resultToKvPair(key, bval, "set", meta), nil 194 } 195 196 // Creates a zk node only if it does not exist. 197 func (z *zookeeperKV) Create( 198 key string, 199 val interface{}, 200 ttl uint64, 201 ) (*kvdb.KVPair, error) { 202 if ttl != 0 { 203 return nil, kvdb.ErrTTLNotSupported 204 } 205 206 bval, err := common.ToBytes(val) 207 if err != nil { 208 return nil, err 209 } 210 if len(bval) == 0 { 211 return nil, kvdb.ErrEmptyValue 212 } 213 214 err = z.createFullPath(key, false) 215 if err == zk.ErrNodeExists { 216 return nil, kvdb.ErrExist 217 } else if err != nil { 218 return nil, err 219 } 220 221 key = z.domain + normalize(key) 222 meta, err := z.client.Set(key, bval, -1) 223 if err != nil { 224 return nil, err 225 } 226 return z.resultToKvPair(key, bval, "create", meta), nil 227 } 228 229 func (z *zookeeperKV) createEphemeral( 230 key string, 231 val interface{}, 232 ) (*kvdb.KVPair, error) { 233 bval, err := common.ToBytes(val) 234 if err != nil { 235 return nil, err 236 } 237 if len(bval) == 0 { 238 return nil, kvdb.ErrEmptyValue 239 } 240 241 err = z.createFullPath(key, true) 242 if err == zk.ErrNodeExists { 243 return nil, kvdb.ErrExist 244 } else if err != nil { 245 return nil, err 246 } 247 248 key = z.domain + normalize(key) 249 meta, err := z.client.Set(key, bval, -1) 250 if err != nil { 251 return nil, err 252 } 253 return z.resultToKvPair(key, bval, "create", meta), nil 254 } 255 256 func (z *zookeeperKV) Update( 257 key string, 258 val interface{}, 259 ttl uint64, 260 ) (*kvdb.KVPair, error) { 261 exists, err := z.exists(key) 262 if err != nil { 263 return nil, err 264 } 265 if !exists { 266 return nil, kvdb.ErrNotFound 267 } 268 return z.Put(key, val, ttl) 269 } 270 271 func (z *zookeeperKV) Enumerate(prefix string) (kvdb.KVPairs, error) { 272 prefix = normalize(prefix) 273 fullPrefix := z.domain + prefix 274 keys, _, err := z.client.Children(fullPrefix) 275 if err != nil { 276 return nil, err 277 } 278 279 kvs := []*kvdb.KVPair{} 280 for _, key := range keys { 281 kvp, err := z.Get(prefix + normalize(key)) 282 if err != nil { 283 return nil, err 284 } 285 kvs = append(kvs, kvp) 286 } 287 return kvs, nil 288 } 289 290 func (z *zookeeperKV) EnumerateWithSelect( 291 prefix string, 292 enumerateSelect kvdb.EnumerateSelect, 293 copySelect kvdb.CopySelect, 294 ) ([]interface{}, error) { 295 return nil, kvdb.ErrNotSupported 296 } 297 298 func (z *zookeeperKV) EnumerateKVPWithSelect( 299 prefix string, 300 enumerateSelect kvdb.EnumerateKVPSelect, 301 copySelect kvdb.CopyKVPSelect, 302 ) (kvdb.KVPairs, error) { 303 return nil, kvdb.ErrNotSupported 304 } 305 306 func (z *zookeeperKV) Delete(key string) (*kvdb.KVPair, error) { 307 kvp, err := z.Get(key) 308 if err != nil { 309 return nil, err 310 } 311 312 key = z.domain + normalize(key) 313 err = z.client.Delete(key, -1) 314 if err != nil { 315 return nil, err 316 } 317 318 kvp.Action = kvdb.KVDelete 319 return kvp, nil 320 } 321 322 func (z *zookeeperKV) DeleteTree(prefix string) error { 323 fullPrefix := z.domain + normalize(prefix) 324 keys, _, err := z.client.Children(fullPrefix) 325 if err != nil { 326 return err 327 } 328 329 var requests []interface{} 330 for _, key := range keys { 331 requests = append(requests, &zk.DeleteRequest{ 332 Path: fullPrefix + normalize(key), 333 Version: -1, 334 }) 335 } 336 337 _, err = z.client.Multi(requests...) 338 return err 339 } 340 341 func (z *zookeeperKV) Keys(prefix, sep string) ([]string, error) { 342 prefix = normalize(prefix) 343 fullPrefix := z.domain + prefix 344 keys, _, err := z.client.Children(fullPrefix) 345 if err != nil { 346 return nil, err 347 } 348 return keys, nil 349 } 350 351 func (z *zookeeperKV) CompareAndSet( 352 kvp *kvdb.KVPair, 353 flags kvdb.KVFlags, 354 prevValue []byte, 355 ) (*kvdb.KVPair, error) { 356 action := "create" 357 modifiedIndex := int32(-1) 358 359 prevPair, err := z.Get(kvp.Key) 360 if err == nil { 361 if (flags & kvdb.KVModifiedIndex) != 0 { 362 if kvp.ModifiedIndex != prevPair.ModifiedIndex { 363 return nil, kvdb.ErrModified 364 } 365 } else { 366 if bytes.Compare(prevValue, prevPair.Value) != 0 { 367 return nil, kvdb.ErrValueMismatch 368 } 369 } 370 action = "set" 371 modifiedIndex = int32(prevPair.ModifiedIndex) 372 } else if err != kvdb.ErrNotFound { 373 return nil, err 374 } 375 376 err = z.createFullPath(kvp.Key, false) 377 if err != nil && err != zk.ErrNodeExists { 378 return nil, err 379 } 380 381 key := z.domain + normalize(kvp.Key) 382 // In case of two nodes trying to call CAS at the same time, setting 383 // modified index in Set() will ensure that only one update succeeds. 384 // Zookeeper will reject an update if the ModifiedIndex does not 385 // match the previous version, unless it is -1. 386 meta, err := z.client.Set(key, kvp.Value, modifiedIndex) 387 if err == zk.ErrBadVersion { 388 return nil, kvdb.ErrModified 389 } else if err != nil { 390 return nil, err 391 } 392 return z.resultToKvPair(kvp.Key, kvp.Value, action, meta), nil 393 } 394 395 func (z *zookeeperKV) CompareAndDelete( 396 kvp *kvdb.KVPair, 397 flags kvdb.KVFlags, 398 ) (*kvdb.KVPair, error) { 399 prevPair, err := z.Get(kvp.Key) 400 if err != nil { 401 return nil, err 402 } 403 404 if (flags & kvdb.KVModifiedIndex) != 0 { 405 if kvp.ModifiedIndex != prevPair.ModifiedIndex { 406 return nil, kvdb.ErrModified 407 } 408 } else { 409 if bytes.Compare(kvp.Value, prevPair.Value) != 0 { 410 return nil, kvdb.ErrValueMismatch 411 } 412 } 413 414 key := z.domain + normalize(kvp.Key) 415 // In case of two nodes trying to call CompareAndDelete at the same time, 416 // setting modified index in Delete() will ensure that only one call succeeds. 417 // Zookeeper will reject an update if the ModifiedIndex does not match the 418 // previous version, unless it is -1. 419 err = z.client.Delete(key, int32(prevPair.ModifiedIndex)) 420 if err == zk.ErrNoNode { 421 return nil, kvdb.ErrNotFound 422 } else if err == zk.ErrBadVersion { 423 return nil, kvdb.ErrModified 424 } else if err != nil { 425 return nil, err 426 } 427 prevPair.Action = kvdb.KVDelete 428 return prevPair, nil 429 } 430 431 func (z *zookeeperKV) WatchKey( 432 key string, 433 waitIndex uint64, 434 opaque interface{}, 435 watchCB kvdb.WatchCB, 436 ) error { 437 return kvdb.ErrNotSupported 438 } 439 440 func (z *zookeeperKV) WatchTree( 441 prefix string, 442 waitIndex uint64, 443 opaque interface{}, 444 watchCB kvdb.WatchCB, 445 ) error { 446 return kvdb.ErrNotSupported 447 } 448 449 func (z *zookeeperKV) Compact( 450 index uint64, 451 ) error { 452 return kvdb.ErrNotSupported 453 } 454 455 func (z *zookeeperKV) Snapshot(prefixes []string, consistent bool) (kvdb.Kvdb, uint64, error) { 456 return nil, 0, kvdb.ErrNotSupported 457 } 458 459 func (z *zookeeperKV) SnapPut(kvp *kvdb.KVPair) (*kvdb.KVPair, error) { 460 return nil, kvdb.ErrNotSupported 461 } 462 463 func (z *zookeeperKV) Lock(key string) (*kvdb.KVPair, error) { 464 return z.LockWithID(key, "locked") 465 } 466 467 func (z *zookeeperKV) LockWithID(key, lockerID string) (*kvdb.KVPair, error) { 468 return z.LockWithTimeout(key, lockerID, z.LockTryDuration, z.GetLockHoldDuration()) 469 } 470 471 func (z *zookeeperKV) LockWithTimeout( 472 key string, 473 lockerID string, 474 lockTryDuration time.Duration, 475 lockHoldDuration time.Duration, 476 ) (*kvdb.KVPair, error) { 477 key = normalize(key) 478 lockTag := LockerIDInfo{LockerID: lockerID} 479 480 kvPair, err := z.createEphemeral(key, lockTag) 481 startTime := time.Now() 482 483 for count := 0; err != nil; count++ { 484 time.Sleep(time.Second) 485 kvPair, err = z.createEphemeral(key, lockTag) 486 if count > 0 && count%15 == 0 && err != nil { 487 currLockerTag := LockerIDInfo{} 488 if _, errGet := z.GetVal(key, &currLockerTag); errGet == nil { 489 logrus.Warnf("Lock %v locked for %v seconds, tag: %v, err: %v", 490 key, count, currLockerTag, err) 491 } 492 } 493 if err != nil && time.Since(startTime) > lockTryDuration { 494 return nil, err 495 } 496 } 497 if err != nil { 498 return nil, err 499 } 500 501 kvPair.Lock = &zookeeperLock{Done: make(chan struct{})} 502 go z.waitForUnlock(kvPair, lockerID, lockHoldDuration) 503 return kvPair, err 504 } 505 506 func (z *zookeeperKV) IsKeyLocked(key string) (bool, string, error) { 507 return false, "", kvdb.ErrNotSupported 508 } 509 510 func (z *zookeeperKV) waitForUnlock( 511 kvp *kvdb.KVPair, 512 lockerID string, 513 lockHoldDuration time.Duration, 514 ) { 515 l := kvp.Lock.(*zookeeperLock) 516 timer := time.NewTimer(lockHoldDuration) 517 defer timer.Stop() 518 519 lockMsgString := kvp.Key + ",tag=" + lockerID 520 521 for { 522 select { 523 case <-timer.C: 524 if lockHoldDuration > 0 { 525 l.Lock() 526 defer l.Unlock() 527 if !l.Unlocked { 528 if _, err := z.Delete(kvp.Key); err != nil { 529 logrus.Errorf("Error deleting lock %s after timeout. %v", 530 lockMsgString, err) 531 } 532 l.Unlocked = true 533 } 534 z.LockTimedout(lockMsgString, lockHoldDuration) 535 return 536 } 537 case <-l.Done: 538 return 539 } 540 } 541 } 542 543 func (z *zookeeperKV) Unlock(kvp *kvdb.KVPair) error { 544 l, ok := kvp.Lock.(*zookeeperLock) 545 if !ok { 546 return fmt.Errorf("Invalid lock structure for key: %s", kvp.Key) 547 } 548 l.Lock() 549 if _, err := z.Delete(kvp.Key); err != nil { 550 l.Unlock() 551 return err 552 } 553 l.Unlocked = true 554 l.Unlock() 555 l.Done <- struct{}{} 556 return nil 557 } 558 559 func (z *zookeeperKV) TxNew() (kvdb.Tx, error) { 560 return nil, kvdb.ErrNotSupported 561 } 562 563 func (z *zookeeperKV) AddUser(username string, password string) error { 564 return kvdb.ErrNotSupported 565 } 566 567 func (z *zookeeperKV) RemoveUser(username string) error { 568 return kvdb.ErrNotSupported 569 } 570 571 func (z *zookeeperKV) GrantUserAccess( 572 username string, 573 permType kvdb.PermissionType, 574 subtree string, 575 ) error { 576 return kvdb.ErrNotSupported 577 } 578 579 func (z *zookeeperKV) RevokeUsersAccess( 580 username string, 581 permType kvdb.PermissionType, 582 subtree string, 583 ) error { 584 return kvdb.ErrNotSupported 585 } 586 587 func (z *zookeeperKV) Serialize() ([]byte, error) { 588 return nil, kvdb.ErrNotSupported 589 } 590 591 func (z *zookeeperKV) Deserialize(b []byte) (kvdb.KVPairs, error) { 592 return nil, kvdb.ErrNotSupported 593 } 594 595 // normalize converts a given path to the form /a/b/c 596 func normalize(key string) string { 597 if key == "" { 598 return "" 599 } 600 path := strings.Split(strings.Trim(key, "/"), "/") 601 return "/" + strings.Join(path, "/") 602 } 603 604 // createFullPath creates the entire path for a directory 605 func (z *zookeeperKV) createFullPath(key string, ephemeral bool) error { 606 key = z.domain + normalize(key) 607 path := strings.Split(strings.TrimPrefix(key, "/"), "/") 608 609 for i := 1; i <= len(path); i++ { 610 newPath := "/" + strings.Join(path[:i], "/") 611 612 if i == len(path) { 613 flags := int32(0) 614 if ephemeral { 615 flags = zk.FlagEphemeral 616 } 617 _, err := z.client.Create(newPath, []byte{}, 618 flags, zk.WorldACL(zk.PermAll)) 619 return err 620 } 621 622 _, err := z.client.Create(newPath, []byte{}, 623 0, zk.WorldACL(zk.PermAll)) 624 if err != nil && err != zk.ErrNodeExists { 625 return err 626 } 627 } 628 return nil 629 } 630 631 // exists checks if the key exists 632 func (z *zookeeperKV) exists(key string) (bool, error) { 633 key = z.domain + normalize(key) 634 exists, _, err := z.client.Exists(key) 635 if err != nil { 636 return false, err 637 } 638 return exists, nil 639 } 640 641 func (z *zookeeperKV) getRetryCount() int { 642 retryCount, ok := z.options[kvdb.RetryCountKey] 643 if !ok { 644 return defaultRetryCount 645 } 646 retry, err := strconv.ParseInt(retryCount, 10, 0) 647 if err != nil { 648 return defaultRetryCount 649 } 650 return int(retry) 651 } 652 653 func (z *zookeeperKV) getAction(action string) kvdb.KVAction { 654 switch action { 655 case "set": 656 return kvdb.KVSet 657 case "create": 658 return kvdb.KVCreate 659 case "get": 660 return kvdb.KVGet 661 default: 662 return kvdb.KVUknown 663 } 664 } 665 666 func (z *zookeeperKV) resultToKvPair( 667 key string, 668 value []byte, 669 action string, 670 stat *zk.Stat, 671 ) *kvdb.KVPair { 672 return &kvdb.KVPair{ 673 Key: strings.TrimPrefix(key, z.domain+"/"), 674 Value: value, 675 Action: z.getAction(action), 676 ModifiedIndex: uint64(stat.Version), 677 CreatedIndex: uint64(stat.Version), 678 } 679 } 680 681 func init() { 682 if err := kvdb.Register(Name, New, Version); err != nil { 683 panic(err.Error()) 684 } 685 }