github.com/onflow/atree@v0.6.0/cmd/stress/array.go (about) 1 /* 2 * Atree - Scalable Arrays and Ordered Maps 3 * 4 * Copyright 2021 Dapper Labs, Inc. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package main 20 21 import ( 22 "fmt" 23 "os" 24 "runtime" 25 "sync" 26 "time" 27 28 "github.com/onflow/atree" 29 ) 30 31 const ( 32 arrayAppendOp = iota 33 arrayInsertOp 34 arraySetOp 35 arrayRemoveOp 36 maxArrayOp 37 ) 38 39 type arrayStatus struct { 40 lock sync.RWMutex 41 42 startTime time.Time 43 44 count uint64 // number of elements in array 45 46 appendOps uint64 47 insertOps uint64 48 setOps uint64 49 removeOps uint64 50 } 51 52 var _ Status = &arrayStatus{} 53 54 func newArrayStatus() *arrayStatus { 55 return &arrayStatus{startTime: time.Now()} 56 } 57 58 func (status *arrayStatus) String() string { 59 status.lock.RLock() 60 defer status.lock.RUnlock() 61 62 duration := time.Since(status.startTime) 63 64 var m runtime.MemStats 65 runtime.ReadMemStats(&m) 66 67 return fmt.Sprintf("duration %s, heapAlloc %d MiB, %d elements, %d appends, %d sets, %d inserts, %d removes", 68 duration.Truncate(time.Second).String(), 69 m.Alloc/1024/1024, 70 status.count, 71 status.appendOps, 72 status.setOps, 73 status.insertOps, 74 status.removeOps, 75 ) 76 } 77 78 func (status *arrayStatus) incAppend() { 79 status.lock.Lock() 80 defer status.lock.Unlock() 81 82 status.appendOps++ 83 status.count++ 84 } 85 86 func (status *arrayStatus) incSet() { 87 status.lock.Lock() 88 defer status.lock.Unlock() 89 90 status.setOps++ 91 } 92 93 func (status *arrayStatus) incInsert() { 94 status.lock.Lock() 95 defer status.lock.Unlock() 96 97 status.insertOps++ 98 status.count++ 99 } 100 101 func (status *arrayStatus) incRemove() { 102 status.lock.Lock() 103 defer status.lock.Unlock() 104 105 status.removeOps++ 106 status.count-- 107 } 108 109 func (status *arrayStatus) Write() { 110 writeStatus(status.String()) 111 } 112 113 func testArray( 114 storage *atree.PersistentSlabStorage, 115 address atree.Address, 116 typeInfo atree.TypeInfo, 117 maxLength uint64, 118 status *arrayStatus, 119 minHeapAllocMiB uint64, 120 maxHeapAllocMiB uint64, 121 ) { 122 123 // Create new array 124 array, err := atree.NewArray(storage, address, typeInfo) 125 if err != nil { 126 fmt.Fprintf(os.Stderr, "Failed to create new array: %s", err) 127 return 128 } 129 130 // values contains array elements in the same order. It is used to check data loss. 131 values := make([]atree.Value, 0, maxLength) 132 133 reduceHeapAllocs := false 134 135 opCount := uint64(0) 136 137 var m runtime.MemStats 138 139 for { 140 runtime.ReadMemStats(&m) 141 allocMiB := m.Alloc / 1024 / 1024 142 143 if !reduceHeapAllocs && allocMiB > maxHeapAllocMiB { 144 fmt.Printf("\nHeapAlloc is %d MiB, removing elements to reduce allocs...\n", allocMiB) 145 reduceHeapAllocs = true 146 } else if reduceHeapAllocs && allocMiB < minHeapAllocMiB { 147 fmt.Printf("\nHeapAlloc is %d MiB, resuming random operation...\n", allocMiB) 148 reduceHeapAllocs = false 149 } 150 151 if reduceHeapAllocs && array.Count() == 0 { 152 fmt.Printf("\nHeapAlloc is %d MiB while array is empty, drop read/write cache to free mem\n", allocMiB) 153 reduceHeapAllocs = false 154 155 // Commit slabs to storage and drop read and write to reduce mem 156 err = storage.FastCommit(runtime.NumCPU()) 157 if err != nil { 158 fmt.Fprintf(os.Stderr, "Failed to commit to storage: %s", err) 159 return 160 } 161 162 storage.DropDeltas() 163 storage.DropCache() 164 165 // Load root slab from storage and cache it in read cache 166 rootID := array.StorageID() 167 array, err = atree.NewArrayWithRootID(storage, rootID) 168 if err != nil { 169 fmt.Fprintf(os.Stderr, "Failed to create array from root id %s: %s", rootID, err) 170 return 171 } 172 173 runtime.GC() 174 175 // Check if map is using > MaxHeapAlloc while empty. 176 runtime.ReadMemStats(&m) 177 allocMiB = m.Alloc / 1024 / 1024 178 fmt.Printf("\nHeapAlloc is %d MiB after cleanup and forced gc\n", allocMiB) 179 180 // Prevent infinite loop that doesn't do useful work. 181 if allocMiB > maxHeapAllocMiB { 182 // This shouldn't happen unless there's a memory leak. 183 fmt.Fprintf( 184 os.Stderr, 185 "Exiting because allocMiB %d > maxMapHeapAlloMiB %d with empty map\n", 186 allocMiB, 187 maxHeapAllocMiB) 188 return 189 } 190 } 191 192 nextOp := r.Intn(maxArrayOp) 193 194 if array.Count() == maxLength || reduceHeapAllocs { 195 nextOp = arrayRemoveOp 196 } 197 198 switch nextOp { 199 200 case arrayAppendOp: 201 opCount++ 202 203 nestedLevels := r.Intn(maxNestedLevels) 204 v, err := randomValue(storage, address, nestedLevels) 205 if err != nil { 206 fmt.Fprintf(os.Stderr, "Failed to generate random value %s: %s", v, err) 207 return 208 } 209 210 copiedValue, err := copyValue(storage, atree.Address{}, v) 211 if err != nil { 212 fmt.Fprintf(os.Stderr, "Failed to copy random value %s: %s", v, err) 213 return 214 } 215 216 // Append to values 217 values = append(values, copiedValue) 218 219 // Append to array 220 err = array.Append(v) 221 if err != nil { 222 fmt.Fprintf(os.Stderr, "Failed to append %s: %s", v, err) 223 return 224 } 225 226 // Update status 227 status.incAppend() 228 229 case arraySetOp: 230 opCount++ 231 232 if array.Count() == 0 { 233 continue 234 } 235 236 k := r.Intn(int(array.Count())) 237 238 nestedLevels := r.Intn(maxNestedLevels) 239 v, err := randomValue(storage, address, nestedLevels) 240 if err != nil { 241 fmt.Fprintf(os.Stderr, "Failed to generate random value %s: %s", v, err) 242 return 243 } 244 245 copiedValue, err := copyValue(storage, atree.Address{}, v) 246 if err != nil { 247 fmt.Fprintf(os.Stderr, "Failed to copy random value %s: %s", v, err) 248 return 249 } 250 251 oldV := values[k] 252 253 // Update values 254 values[k] = copiedValue 255 256 // Update array 257 existingStorable, err := array.Set(uint64(k), v) 258 if err != nil { 259 fmt.Fprintf(os.Stderr, "Failed to set %s at index %d: %s", v, k, err) 260 return 261 } 262 263 // Compare overwritten value from array with overwritten value from values 264 existingValue, err := existingStorable.StoredValue(storage) 265 if err != nil { 266 fmt.Fprintf(os.Stderr, "Failed to convert %s to value: %s", existingStorable, err) 267 return 268 } 269 270 err = valueEqual(oldV, existingValue) 271 if err != nil { 272 fmt.Fprintf(os.Stderr, "Failed to compare %s and %s: %s", existingValue, oldV, err) 273 return 274 } 275 276 // Delete overwritten element from storage 277 err = removeStorable(storage, existingStorable) 278 if err != nil { 279 fmt.Fprintf(os.Stderr, "Failed to remove storable %s: %s", existingStorable, err) 280 return 281 } 282 283 err = removeValue(storage, oldV) 284 if err != nil { 285 fmt.Fprintf(os.Stderr, "Failed to remove copied overwritten value %s: %s", oldV, err) 286 return 287 } 288 289 // Update status 290 status.incSet() 291 292 case arrayInsertOp: 293 opCount++ 294 295 k := r.Intn(int(array.Count() + 1)) 296 297 nestedLevels := r.Intn(maxNestedLevels) 298 v, err := randomValue(storage, address, nestedLevels) 299 if err != nil { 300 fmt.Fprintf(os.Stderr, "Failed to generate random value %s: %s", v, err) 301 return 302 } 303 304 copiedValue, err := copyValue(storage, atree.Address{}, v) 305 if err != nil { 306 fmt.Fprintf(os.Stderr, "Failed to copy random value %s: %s", v, err) 307 return 308 } 309 310 // Update values 311 if k == int(array.Count()) { 312 values = append(values, copiedValue) 313 } else { 314 values = append(values, nil) 315 copy(values[k+1:], values[k:]) 316 values[k] = copiedValue 317 } 318 319 // Update array 320 err = array.Insert(uint64(k), v) 321 if err != nil { 322 fmt.Fprintf(os.Stderr, "Failed to insert %s into index %d: %s", v, k, err) 323 return 324 } 325 326 // Update status 327 status.incInsert() 328 329 case arrayRemoveOp: 330 if array.Count() == 0 { 331 continue 332 } 333 334 opCount++ 335 336 k := r.Intn(int(array.Count())) 337 338 oldV := values[k] 339 340 // Update values 341 copy(values[k:], values[k+1:]) 342 values[len(values)-1] = nil 343 values = values[:len(values)-1] 344 345 // Update array 346 existingStorable, err := array.Remove(uint64(k)) 347 if err != nil { 348 fmt.Fprintf(os.Stderr, "Failed to remove element at index %d: %s", k, err) 349 return 350 } 351 352 // Compare removed value from array with removed value from values 353 existingValue, err := existingStorable.StoredValue(storage) 354 if err != nil { 355 fmt.Fprintf(os.Stderr, "Failed to convert %s to value: %s", existingStorable, err) 356 return 357 } 358 359 err = valueEqual(oldV, existingValue) 360 if err != nil { 361 fmt.Fprintf(os.Stderr, "Failed to compare %s and %s: %s", existingValue, oldV, err) 362 return 363 } 364 365 // Delete removed element from storage 366 err = removeStorable(storage, existingStorable) 367 if err != nil { 368 fmt.Fprintf(os.Stderr, "Failed to remove element %s: %s", existingStorable, err) 369 return 370 } 371 372 err = removeValue(storage, oldV) 373 if err != nil { 374 fmt.Fprintf(os.Stderr, "Failed to remove copied removed value %s: %s", oldV, err) 375 return 376 } 377 378 // Update status 379 status.incRemove() 380 } 381 382 // Check array elements against values after every op 383 err = checkArrayDataLoss(array, values) 384 if err != nil { 385 fmt.Fprintln(os.Stderr, err) 386 return 387 } 388 389 if opCount >= 100 { 390 opCount = 0 391 rootIDs, err := atree.CheckStorageHealth(storage, -1) 392 if err != nil { 393 fmt.Fprintln(os.Stderr, err) 394 return 395 } 396 ids := make([]atree.StorageID, 0, len(rootIDs)) 397 for id := range rootIDs { 398 // filter out root ids with empty address 399 if id.Address != atree.AddressUndefined { 400 ids = append(ids, id) 401 } 402 } 403 if len(ids) != 1 || ids[0] != array.StorageID() { 404 fmt.Fprintf(os.Stderr, "root storage ids %v in storage, want %s\n", ids, array.StorageID()) 405 return 406 } 407 } 408 } 409 } 410 411 func checkArrayDataLoss(array *atree.Array, values []atree.Value) error { 412 413 // Check array has the same number of elements as values 414 if array.Count() != uint64(len(values)) { 415 return fmt.Errorf("Count() %d != len(values) %d", array.Count(), len(values)) 416 } 417 418 // Check every element 419 for i, v := range values { 420 storable, err := array.Get(uint64(i)) 421 if err != nil { 422 return fmt.Errorf("failed to get element at %d: %w", i, err) 423 } 424 convertedValue, err := storable.StoredValue(array.Storage) 425 if err != nil { 426 return fmt.Errorf("failed to convert storable to value at %d: %w", i, err) 427 } 428 err = valueEqual(v, convertedValue) 429 if err != nil { 430 return fmt.Errorf("failed to compare %s and %s: %w", v, convertedValue, err) 431 } 432 } 433 434 return nil 435 }