Keyboard shortcuts

/ or ⌘/Ctrl K
Find a note
j / k
Next / previous section or linked note
h / l
Collapse or go to parent / expand or enter
e or Alt-click
Read a linked note here
o
Open focused note on its own
g g / G
First / last section or linked note
g h / g a
Home / all notes
g b / g t
Backlinks / table of contents
t
Cycle System, Light, Dark
? / Esc
Show / close this reference

Search: ↑/↓ or Ctrl N/P, Enter to open. Shortcuts pause while typing.

sectionZookeeper [c159b3c2]

1. API

  • create(path, data[], mode, flag)

    • Creates a znode, like an inode, but a unit of abstraction to lock on it
  • setData(path, data[], version)

    • sets the data[] in the znode at the specified flag
  • getData(path, watch)

    • returns data[], also allows clients to watch
  • getChildren(path, watch)

    • Returns all the children names of the znode at the specified path
  • exists(path, watch)

    • Checks whether a znode exists
  • delete(path, version)

    • Deletes a znode. Must match the monotonically increasing versions

2. Server

  • Fully replicated, like etcd, into a set called a ZooKeeper ensemble
  • Elected leaders and others become followers
  • Unlike chubby, clients can connect to any

    • Higher availablity, not as strong consistency
    • Use sync() to assure that you have the highest consistency
  • Leader broadcasts operations to the followers, and performs write operation on the coordination data placed in its memory
  • Follower can also recieve and respond to write requests. Multiple writes can be batched, but only the request needs to forwarded to the leader, the leader broadcasts all the requests to other followers

    • Basically a follower gets a write, sends a request to the leader, the leader broadcasts the state to everyone after it's done

3. Replicated Database

  • In memory copy of the database so that reads and writes can be done locally
  • ZK takes periodic snapshots of all the delivered messages as a WAL

    • Snapshots are fuzzy
    • Enables at-most-once execution
    • If a server dies before the next snapshot is taken, it does a depth first scan of the tree to read every znode's metadata and data atomically then write that metadata and data from disk, extracted from the WAL

      • This is why ZK recovery is slow

4. Atomic Broadcast

  • Used by ZK to broadcast the write request to the replicated database on all servers

    • Followers forward writes to leaders
  • Uses the ZAB protocol

    • Two modes

      1. Broadcast is used to send messages
      2. Recovery is used to syncronize
    • Default is $2f + 1$ for quorum where f is the number of faults
  • When a leader dies, the new leader is elected and ensures all the updates from the previous leader are incorporated into its replicated db before it broadcasts its own requests
  • At every transaction, the leader is broadcasting what it is working on into a replicated queue

5. Request Processor

  • Manager that keeps the transactions atomic, only the leader uses this
  • Transactions within ZK are idempotent
  • All requests are linearized on the leader, so the leader converts everything into a setDataTXN

6. Client-server interactino

  • getData() and getChildren() can both be performed locally without notifying others
  • Servers generate zxid for every read request and retrieves the most recently updated state of the server's data
  • Writes have multistep

    1. Write the data
    2. Notify the client(s) who have set watches on that data
    3. Sent the write request to the leader so it can be updated in the replicated database
    4. Data is replicated among all connected servers in the ensemble
  • Everything is FIFO, including read requests when there's a write
  • However if a read is in progress, multiple readers can read in parallel
  • Like a giant R/W lock
  • Default is async transmission of data, which comes at the cost of insconsistent or stale reads
  • the zxid allows the client to maintain a consistent view across different servers
  • Session timeouts and heartbeats are used to maintain connections from servers to clients for watches, which is decided by the leader

7. Primitives

  • zookeeper.apache.org/doc/r3.4.2/recipes.pdf
  • Config management and service discovery

      newZnode = create("/config/port", 8090, EMPHEMERAL) # set a port
      getData("/config/port", true) # get a watch
      setData("/config/port", value, version) # new version
    
  • Rendezvous

      newZnode = create("/rendezvous/candidateOne", "", EMPHEMERAL) # create a znode to write
      getData("/rendezvous/candidateOne", true) # get a watch
      setData("/rendevous/candidateOne", {"10.0.0.1", 8080})
    
  • Group Membership

      newZnode = create("/groups/member0001", EMPHEMERAL)
      newZnode = create("/groups/member0001/processOne", EMPHERAL,SEQUENTIAL) # set a sequential flag to generate a unique name
      childrenList = getChildren(/groups/member0001", true)
    
  • Locks

      newLock = create("/locks/lock-1", EMPHEMERAL)
      exists("/locks", true) # watch the lock tree
      delete(newLock) # to release the lock
    
    • issues

      • herding: when a lock is released, many clients stampede to get the lock

        • Solved with using SEQUENTIAL, at which point the lowest sequence number will hold the lock

            newZnode = create(path + "/lock-", EPHEMERAL, SEQUENTIAL)
            do
                childrenList = getChildren(path, false)
                if newZnode is lowest znode in children
                    exit
                newPath = znode in childrenList ordered just before newZnode
                if exists(newPath, true)
                    wait for event
            while true
          
      • exclusive lock only

        • Can implement R/W locks with

            newZnode = create(path + "/write-", EPHEMERAL, SEQUENTIAL)
            do
                childrenList = getChildren(path, false)
                if newZnode is lowest znode in children
                    exit
                newPath = znode in childrenList ordered just before newZnode
                if exists(newPath, true)
                    wait for event
            while true
          
        • create a read lock and then reads can happen with other reads, but blocks on writes, and writes block on reads
    • Barriers

        newZnode = create(path + "/" + processName)
        exists(path + "/ready", true)
        newZnodeChild = create(newZnode, EPHEMERAL)
        childrenList = getChildren(path, false)
        if fewer children in childrenList than barrierThreshold
          wait for watch event
        else
          create(path + "/ready", REGULAR)
      

8. Performance

  • Better load distribution than chubby

    2024-03-04_20-09-16_screenshot.png

  • Atomic broadcast decreases performance, has a CPU cost
  • Failures of leaders will grind application to zero while it recovers
  • Solves chubby like problems with different tradeoffs, gives up consistency for a bit of throughput

Links from this note 1