Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Page File Management] 재즈(함석명) 미션 제출합니다. #5

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
4f31b12
feat: 페이지 메타데이터 PageHeader 객체와 필드 정의
seokmyungham Aug 27, 2024
dbefaf9
feat: 저장할 데이터를 의미하는 레코드 객체 정의
seokmyungham Aug 27, 2024
fa5bfb4
feat: 페이지 객체와 기본 필드 정의
seokmyungham Aug 27, 2024
ad34432
feat: 페이지에 레코드를 추가하는 Page addRecord() 메서드 정의
seokmyungham Aug 27, 2024
05172c3
feat: 페이지에 레코드를 제거하는 Page deleteRecord() 메서드 정의
seokmyungham Aug 27, 2024
7bbf7f4
feat: BufferPool 객체와 LRU Cache 필드 정의
seokmyungham Aug 27, 2024
c590b69
feat: 파일 I/O 책임을 갖는 FileManager 객체 정의 및 Serializable 상속
seokmyungham Aug 28, 2024
f07396a
refactor: 버퍼 풀로부터 LRU 페이지 교체 전략을 분리
seokmyungham Aug 28, 2024
83a2755
feat: 버퍼 풀로부터 페이지를 반환하는 getPage() 메서드 정의
seokmyungham Aug 28, 2024
9af29d5
feat: 버퍼 풀 플러시 flush() 메서드 정의
seokmyungham Aug 28, 2024
426db15
feat: 페이지를 수정하는 modifyPage() 메서드 정의
seokmyungham Aug 28, 2024
8eedadb
refactor: 클래스 패키지 이동
seokmyungham Aug 28, 2024
51ad914
fix: createDirectories 메서드로 상위 디렉토리가 만들어지지 않는 문제를 해결
seokmyungham Aug 28, 2024
04a3344
test: LRUCache 테스트 추가
seokmyungham Aug 28, 2024
28f1132
test: 버퍼 풀 테스트 추가
seokmyungham Aug 28, 2024
d3f1c15
style: 사용하지 않는 Import 제거
seokmyungham Aug 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ java {

test {
useJUnitPlatform()
}
}
53 changes: 53 additions & 0 deletions src/main/java/database/BufferPool.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package database;

import java.util.Map;

public class BufferPool {

private static final int BUFFER_SIZE = 40;

private final Map<PageId, Page> bufferPool;
private final PageManager pageManager;

public BufferPool(PageManager pageManager) {
this.bufferPool = new LRUCache<>(BUFFER_SIZE, 0.75f, true, pageManager);
this.pageManager = pageManager;
}

public Page getPage(PageId pageId) {
Page page = bufferPool.computeIfAbsent(pageId, id -> pageManager.loadPage(pageId));
page.pin();
return page;
}

public void modifyPage(PageId pageId) {
Page page = getPage(pageId);
/*
..modify..
*/
page.setDirty(true);
page.unPin();
}

/**
* 플러시 리스트 플러시
*/
public void flush() {
for (Map.Entry<PageId, Page> entry : bufferPool.entrySet()) {
Page page = entry.getValue();

if (!page.isPinned() && page.isDirty()) {
pageManager.savePage(entry.getKey(), page);
page.setDirty(false);
}
}
}
Comment on lines +35 to +44
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 플러시 리스트 플러시까지 구현하셨군요 👍🏻


public Map<PageId, Page> getBufferPool() {
return bufferPool;
}

public PageManager getPageManager() {
return pageManager;
}
}
62 changes: 62 additions & 0 deletions src/main/java/database/FileManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package database;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileManager {

private static final String DIRECTORY_PATH = "data/files/";
private static final String FILE_EXTENSION = ".ibd";

public FileManager() {
createDirectory();
}

private void createDirectory() {
Path directory = Paths.get(DIRECTORY_PATH);
if (Files.notExists(directory)) {
try {
Files.createDirectories(directory);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

public Page readPage(PageId pageId) {
Path filePath = Paths.get(DIRECTORY_PATH, pageId.getFileName() + FILE_EXTENSION);

try (RandomAccessFile file = new RandomAccessFile(filePath.toString(), "r")) {
long offset = (long) pageId.getPageNum() * Page.PAGE_SIZE;
file.seek(offset);

try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file.getFD()))) {
return (Page) ois.readObject();
}
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}

public void writePage(PageId pageId, Page page) {
Path filePath = Paths.get(DIRECTORY_PATH, pageId.getFileName() + FILE_EXTENSION);

try (RandomAccessFile file = new RandomAccessFile(filePath.toString(), "rw")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏻

long offset = (long) pageId.getPageNum() * Page.PAGE_SIZE;
file.seek(offset);

try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file.getFD()))) {
oos.writeObject(page);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
34 changes: 34 additions & 0 deletions src/main/java/database/LRUCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package database;

import java.util.LinkedHashMap;
import java.util.Map;

public class LRUCache<K, V> extends LinkedHashMap<K, V> {

private final int bufferSize;
private final PageManager pageManager;

public LRUCache(int initialCapacity, float loadFactor, boolean accessOrder, PageManager pageManager) {
super(initialCapacity, loadFactor, accessOrder);
this.pageManager = pageManager;
this.bufferSize = initialCapacity;
}

@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
boolean isBufferPoolOverCapacity = size() > bufferSize;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메서드가 아닌 변수로 분리한 이유가 있나요?

boolean isUnpinned = !((Page) eldest.getValue()).isPinned();

if (isBufferPoolOverCapacity && isUnpinned) {
flushIfDirty((PageId) eldest.getKey(), (Page) eldest.getValue());
return true;
}
return false;
}

private void flushIfDirty(PageId pageId, Page page) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private void flushIfDirty(PageId pageId, Page page) {
private void flush(PageId pageId, Page page) {

해당 메서드를 사용하는 측에서는 더티 페이지의 여부가 중요하나요?

if (page.isDirty()) {
pageManager.savePage(pageId, page);
}
}
}
92 changes: 92 additions & 0 deletions src/main/java/database/Page.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package database;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class Page implements Serializable {

public static final int PAGE_SIZE = 16 * 1024;

private final PageHeader header;
private final List<Record> records;
private int freeSpace;
private int pinCount;

public Page(int pageNum, PageType pageType) {
this.header = new PageHeader(pageNum, pageType);
this.records = new ArrayList<>();
this.freeSpace = PAGE_SIZE - header.getHeaderSize();
}

public boolean addRecord(Record record) {
if (freeSpace >= record.getSize()) {
records.add(record);
freeSpace -= record.getSize();

header.incrementRecordCount();
header.setDirty(true);

return true;
} else {
return false;
}
}

public boolean deleteRecord(Record record) {
if (records.remove(record)) {
freeSpace += record.getSize();

header.setDirty(true);
header.decrementRecordCount();

return true;
} else {
return false;
}
}

public void pin() {
this.pinCount++;
}

public void unPin() {
this.pinCount--;
}

public boolean isPinned() {
return this.pinCount > 0;
}

public PageHeader getHeader() {
return header;
}

public List<Record> getRecords() {
return records;
}

public int getFreeSpace() {
return freeSpace;
}

public int getPinCount() {
return pinCount;
}

public void setDirty(boolean isDirty) {
header.setDirty(isDirty);
}

public boolean isDirty() {
return header.isDirty();
}

public PageType getPageType() {
return header.getPageType();
}

public int getPageNum() {
return header.getPageNum();
}
}
52 changes: 52 additions & 0 deletions src/main/java/database/PageHeader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package database;

import java.io.Serializable;

public class PageHeader implements Serializable {

public static final int HEADER_SIZE = 13;

private final int pageNum;
private final PageType pageType;
private int recordCount;
private boolean isDirty;

public PageHeader(int pageNum, PageType pageType) {
this.pageNum = pageNum;
this.pageType = pageType;
this.recordCount = 0;
this.isDirty = false;
}

public void incrementRecordCount() {
this.recordCount++;
}

public void decrementRecordCount() {
this.recordCount--;
}

public void setDirty(boolean isDirty) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

더 의미있는 메서드들로 나눌수 있을 것 같아요.

this.isDirty = isDirty;
}

public int getPageNum() {
return pageNum;
}

public PageType getPageType() {
return pageType;
}

public int getRecordCount() {
return recordCount;
}

public boolean isDirty() {
return isDirty;
}

public int getHeaderSize() {
return HEADER_SIZE;
}
}
39 changes: 39 additions & 0 deletions src/main/java/database/PageId.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package database;

import java.util.Objects;

public class PageId {

private final String fileName;
private final int PageNum;

public PageId(String fileName, int pageNum) {
this.fileName = fileName;
this.PageNum = pageNum;
}

public String getFileName() {
return fileName;
}

public int getPageNum() {
return PageNum;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PageId pageId = (PageId) o;
return PageNum == pageId.PageNum && Objects.equals(fileName, pageId.fileName);
}

@Override
public int hashCode() {
return Objects.hash(fileName, PageNum);
}
}
18 changes: 18 additions & 0 deletions src/main/java/database/PageManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package database;

public class PageManager {

private final FileManager fileManager;

public PageManager(FileManager fileManager) {
this.fileManager = fileManager;
}

public Page loadPage(PageId pageId) {
return fileManager.readPage(pageId);
}

public void savePage(PageId pageId, Page page) {
fileManager.writePage(pageId, page);
}
}
8 changes: 8 additions & 0 deletions src/main/java/database/PageType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package database;

public enum PageType {

CLUSTERED_INDEX,
SECONDARY_INDEX,
UNDO_LOG;
}
Comment on lines +3 to +8
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

페이지 타입은 어떤 기준으로 나누셨나요?

26 changes: 26 additions & 0 deletions src/main/java/database/Record.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package database;

import java.io.Serializable;

public class Record implements Serializable {

private final int recordId;
private final byte[] data;

public Record(int recordId, byte[] data) {
this.recordId = recordId;
this.data = data;
}

public int getRecordId() {
return recordId;
}

public byte[] getData() {
return data;
}

public int getSize() {
return data.length;
}
}
Loading