| 1420 | } |
| 1421 | |
| 1422 | func (f *VFSFile) ReadAt(p []byte, off int64) (n int, err error) { |
| 1423 | f.logger.Debug("reading at", "off", off, "len", len(p)) |
| 1424 | pageSize, err := f.pageSizeBytes() |
| 1425 | if err != nil { |
| 1426 | return 0, err |
| 1427 | } |
| 1428 | |
| 1429 | pgno := uint32(off/int64(pageSize)) + 1 |
| 1430 | pageOffset := int(off % int64(pageSize)) |
| 1431 | |
| 1432 | // Check dirty pages first (takes priority over cache and remote) |
| 1433 | f.mu.Lock() |
| 1434 | if f.writeEnabled { |
| 1435 | if bufferOff, ok := f.dirty[pgno]; ok { |
| 1436 | // Read page from buffer file |
| 1437 | data := make([]byte, pageSize) |
| 1438 | if _, err := f.bufferFile.ReadAt(data, bufferOff); err != nil { |
| 1439 | f.mu.Unlock() |
| 1440 | return 0, fmt.Errorf("read dirty page from buffer: %w", err) |
| 1441 | } |
| 1442 | n = copy(p, data[pageOffset:]) |
| 1443 | f.mu.Unlock() |
| 1444 | f.logger.Debug("dirty page hit", "page", pgno, "n", n) |
| 1445 | |
| 1446 | // Update the first page to pretend like we are in journal mode. |
| 1447 | if off == 0 && len(p) >= 28 { |
| 1448 | p[18], p[19] = 0x01, 0x01 |
| 1449 | _, _ = rand.Read(p[24:28]) |
| 1450 | } |
| 1451 | |
| 1452 | return n, nil |
| 1453 | } |
| 1454 | } |
| 1455 | f.mu.Unlock() |
| 1456 | |
| 1457 | // If hydration complete, read from local file |
| 1458 | if f.hydrator != nil && f.hydrator.Complete() { |
| 1459 | return f.hydrator.ReadAt(p, off) |
| 1460 | } |
| 1461 | |
| 1462 | // Check cache (cache is thread-safe) |
| 1463 | if data, ok := f.cache.Get(pgno); ok { |
| 1464 | n = copy(p, data[pageOffset:]) |
| 1465 | f.logger.Debug("cache hit", "page", pgno, "n", n) |
| 1466 | |
| 1467 | // Update the first page to pretend like we are in journal mode. |
| 1468 | if off == 0 { |
| 1469 | p[18], p[19] = 0x01, 0x01 |
| 1470 | _, _ = rand.Read(p[24:28]) |
| 1471 | } |
| 1472 | |
| 1473 | return n, nil |
| 1474 | } |
| 1475 | |
| 1476 | // Get page index element |
| 1477 | f.mu.Lock() |
| 1478 | elem, ok := f.index[pgno] |
| 1479 | writeEnabled := f.writeEnabled // capture while holding lock to avoid data race |