{"id":"IL-0439","org":"Cybozu","date":"2018-06-28","docs":[],"title":{"en":"Database access slowed after each block device and filesystem expansion because the Linux kernel dropped the page cache","ko":"블록 디바이스와 파일 시스템을 확장할 때마다 데이터베이스 접근이 한동안 느려졌다"},"domain":["database"],"impact":{"scope":"Database access slowed down temporarily after each filesystem expansion, showing as slower application responses for customers","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"When the intent of kernel code is unclear, the comments and the description of the commit that introduced it are the standard place to learn why it was written. The fix was 4 added lines and 2 removed lines, but reaching it took much investigation, so the effort of a source fix does not follow the number of lines changed.","ko":"Cybozu 는 소스 수정의 수고가 바뀐 줄 수에 비례하지 않는다고 했다. 이번 수정은 2천만 줄에 이르는 Linux 커널 소스에서 4줄을 더하고 2줄을 지운 것에 지나지 않았지만, 거기까지 여러 시행착오가 있었다. 코드를 쓴 의도를 모를 때는 주석이나 변경 때의 커밋 설명을 보는 것이 정석이라고도 했다."},"related":[],"sources":[{"url":"https://blog.cybozu.io/entry/2018/06/28/080000","kind":"blog","archived":null}],"summary":{"en":"The page cache is the kernel feature that keeps copies of storage data in memory: the first read copies data from the device into the page cache, later reads are served from memory, and writes go to the page cache and are written back to the device later. Because memory is several orders of magnitude faster than SSDs or HDDs, once the page cache is lost, reads go to the much slower storage device again. The statistics from free and sar suggested that expanding the block device invalidated the page cache of the filesystem on it.\n\nIn the kernel source, check_disk_size_change(), called during a block device size change, logs \"%s: detected capacity change from %lld to %lld\\n\" and calls flush_disk(bdev, false) whenever the old size bdev_size differs from the new size disk_size, which invalidates the page cache (and other caches such as those used for path resolution) of the filesystem on the device, on shrinking and on expanding alike. No kernel setting avoided this. Using git blame to find the commit that added the flush_disk() call, Cybozu learned its purpose: on shrinking, data being read or written in the removed area loses its destination and causes I/O errors, and expansion could cause the same problem in the special case of expanding right after shrinking. A full reading of the related source showed that the kernel's locking makes that case impossible, so invalidation on expansion was unnecessary.\n\nCybozu's patch to fs/block_dev.c calls flush_disk() only when bdev_size > disk_size, that is when the device shrinks, and otherwise only sets bdev->bd_invalidated = 1 when disk_part_scan_enabled(bdev->bd_disk) is true. The patched kernel was tested in the development environment and applied in production, which ended the problem inside Cybozu. The patch was also submitted upstream as \"[RFC PATCH] fs: don't flush pagecahce when expanding block device\", got no reply at first, and after several simplified resubmissions over a few months was taken into the next tree and finally into Linux v4.17.","ko":"Cybozu 는 자사 시스템의 데이터 일부를 SQLite 에 저장한다. 이 데이터가 놓인 블록 디바이스는 원격 서버의 LVM 볼륨에 대응하는 iSCSI 디바이스 두 개를 RAID1 로 묶은 것이다. 데이터베이스가 커져 파일 시스템의 빈 공간이 모자라면, 이 블록 디바이스의 크기를 늘리고 그 위의 파일 시스템 크기를 늘렸다. 그때마다 데이터베이스 접근이 한동안 느려져 고객이 쓰는 애플리케이션의 응답이 나빠졌다. free 와 sar 로 보니 블록 디바이스를 늘린 뒤 페이지 캐시가 크게 줄었고, 성능 저하와 함께 스토리지 접근이 생겼다. 페이지 캐시는 스토리지 디바이스의 데이터를 메모리에 복사해 두고 읽기와 쓰기를 메모리 속도로 처리하게 하는 커널 기능이다.\n\n운영 본부의 한 엔지니어가 커널 소스를 조사했다. 블록 디바이스 크기 변경 처리에서 불리는 check_disk_size_change() 함수는 크기가 바뀌면 축소든 확장이든 flush_disk() 를 불러, 그 디바이스 위 파일 시스템의 페이지 캐시를 무효화했다. flush_disk() 호출을 넣은 커밋을 git blame 으로 찾아 설명을 읽었다. 축소 때 대상 영역을 읽고 쓰는 중이면 I/O 오류가 나고, 확장은 축소 직후 확장하는 특수한 경우에만 문제가 될 수 있다는 내용이었다. 관련 소스를 모두 조사해 보니 커널 안의 상호 배제 때문에 축소 직후 확장해도 그 문제는 일어날 수 없었고, 확장 때의 무효화는 필요 없었다.\n\n운영 본부의 두 엔지니어는 크기가 줄어들 때만 flush_disk() 를 부르는 fs/block_dev.c 패치를 만들었고, 개발 환경에서 시험한 뒤 운영 환경에 적용해 사내 문제를 풀었다. 두 엔지니어 가운데 커널 소스를 조사하지 않은 다른 한 사람이 이 패치를 upstream Linux 커널에 넣는 작업을 했다. 처음 보낸 [RFC PATCH] fs: don't flush pagecahce when expanding block device 에는 아무도 답하지 않았지만, 패치와 글을 단순하게 다듬어 여러 번 다시 보낸 끝에 몇 달 뒤 next tree 를 거쳐 v4.17 에 들어갔다."},"trigger":{"en":"Operators expanded a block device, a RAID1 multiple device made of two iSCSI devices backed by LVM volumes on remote servers, and then expanded the filesystem on it because the database had grown.","ko":"데이터베이스가 커져 빈 공간이 모자라자 파일 시스템이 놓인 블록 디바이스의 크기를 늘리고 그 위의 파일 시스템 크기를 늘렸다."},"severity":"unknown","symptoms":["high-latency"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":{"en":"The slowdown appeared as worse response times of the application customers use.","ko":"파일 시스템 확장 뒤 데이터베이스 접근이 느려진 것이 고객이 쓰는 애플리케이션의 응답 속도 저하로 드러났다."},"components":["linux-kernel","database"],"root_cause":"software-bug","remediation":[{"text":{"en":"Patched check_disk_size_change() so the kernel invalidates the page cache only when a block device shrinks","ko":"블록 디바이스 크기가 줄어들 때만 flush_disk() 를 부르고 늘어날 때는 페이지 캐시를 무효화하지 않도록 fs/block_dev.c 의 check_disk_size_change() 를 고치는 패치를 만들었다."}},{"text":{"en":"Tested the patched kernel in the development environment and applied it in production","ko":"이 패치를 넣은 커널을 개발 환경에서 시험한 뒤 운영 환경에 적용해 사내 문제를 풀었다."}}],"action_items":[{"text":{"en":"Get the patch merged into the upstream Linux kernel so it need not be backported at each future OS upgrade","ko":"자사 시스템을 받치는 OSS 의 개선에 기여하고 나중에 OS 를 올릴 때 수정 패치를 일일이 백포트하지 않으려고, 패치를 upstream Linux 커널에 넣는 작업을 했다. 패치는 v4.17 에 들어갔다."}}],"contributing":[],"recurrence_of":null,"symptoms_text":{"en":"For years, at Cybozu, database access slowed down for a while each time a filesystem was expanded. Customers saw it as slower responses from the application.\n\nPart of Cybozu's data is stored in SQLite. When a database grows and the filesystem runs short of free space, operators first expand the block device the filesystem sits on and then expand the filesystem on that device. The slowdown came after the second step.\n\nfree and sar showed that the amount of page cache in the system dropped sharply after the block device was expanded. After the filesystem was expanded, storage device access rose in step with the drop in database performance, and fell again as performance recovered.","ko":"SQLite 데이터베이스가 놓인 파일 시스템의 크기를 늘린 뒤 데이터베이스 접근이 한동안 느려졌다. 이 현상은 고객이 쓰는 애플리케이션의 응답 속도 저하로 나타났고, Cybozu 는 오랫동안 이 문제를 겪었다.\n\n확장은 두 단계였다. 먼저 파일 시스템이 놓인 블록 디바이스의 크기를 늘리고, 그다음 그 위의 파일 시스템 크기를 늘렸다. 느려지는 것은 두 번째 단계 뒤였다.\n\nfree 명령과 sar 명령으로 통계를 보니 첫 번째 단계 뒤 시스템의 페이지 캐시 양이 크게 줄어 있었다. 두 번째 단계 뒤에는 데이터베이스 접근 성능이 떨어지는 것과 함께 스토리지 디바이스 접근이 생겼고, 성능이 회복되면서 스토리지 접근도 줄었다.\n\n커널에서 이 동작을 피하는 설정 항목을 찾았지만 없었다."},"updated_at":"2026-11-14T12:34:08.864209Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0439","org_slug":"cybozu"}