getNextDeviceID finds the next free device ID by taking a cursor through the deviceIDBucketName bucket and finding the next sequentially unassigned ID. Device ID state is marked by a byte deviceFree or deviceTaken. Low device IDs will be reused sooner.
(tx *bolt.Tx)
| 162 | // unassigned ID. Device ID state is marked by a byte deviceFree or |
| 163 | // deviceTaken. Low device IDs will be reused sooner. |
| 164 | func getNextDeviceID(tx *bolt.Tx) (uint32, error) { |
| 165 | bucket := tx.Bucket(deviceIDBucketName) |
| 166 | cursor := bucket.Cursor() |
| 167 | |
| 168 | // Check if any device id can be reused. |
| 169 | // Bolt stores its keys in byte-sorted order within a bucket. |
| 170 | // This makes sequential iteration extremely fast. |
| 171 | for key, taken := cursor.First(); key != nil; key, taken = cursor.Next() { |
| 172 | isFree := taken[0] == byte(deviceFree) |
| 173 | if !isFree { |
| 174 | continue |
| 175 | } |
| 176 | |
| 177 | parsedID, err := strconv.ParseUint(string(key), 10, 32) |
| 178 | if err != nil { |
| 179 | return 0, err |
| 180 | } |
| 181 | |
| 182 | id := uint32(parsedID) |
| 183 | if err := markDeviceID(tx, id, deviceTaken); err != nil { |
| 184 | return 0, err |
| 185 | } |
| 186 | |
| 187 | return id, nil |
| 188 | } |
| 189 | |
| 190 | // Try allocate new device ID |
| 191 | seq, err := bucket.NextSequence() |
| 192 | if err != nil { |
| 193 | return 0, err |
| 194 | } |
| 195 | |
| 196 | if seq >= maxDeviceID { |
| 197 | return 0, errors.New("dm-meta: couldn't find free device key") |
| 198 | } |
| 199 | |
| 200 | id := uint32(seq) |
| 201 | if err := markDeviceID(tx, id, deviceTaken); err != nil { |
| 202 | return 0, err |
| 203 | } |
| 204 | |
| 205 | return id, nil |
| 206 | } |
| 207 | |
| 208 | // markDeviceID marks a device as deviceFree or deviceTaken |
| 209 | func markDeviceID(tx *bolt.Tx, deviceID uint32, state deviceIDState) error { |
no test coverage detected
searching dependent graphs…