Skip to content

Commit

Permalink
[Paged File, I/O System] 초코칩(권기호) 미션 제출합니다. (#3)
Browse files Browse the repository at this point in the history
* feat: 페이지 구조 구현

* feat: 페이지 직렬화

* feat: 페이지 팩터리 패턴 적용

* feat: 페이지 매니저 생성

* test: DisplayName 수정

* feat: 불필요 PageManager 삭제

* feat: 버퍼풀 구현

* refactor: innodb 패키지 생성

* feat: 더티 상태 구현

* feat: 페이지 내의 레코드 기능 구현

* feat: 페이지 디렉터리 구현

* feat: 레코드 추가 기능 구현

* feat: 레코드 변경 시, 더티 체크

* feat: 테이블 별로 페이지 저장

* feat: 페이지 번호 반환

* refactor: 버퍼풀 패키지 생성

* feat: 페이지 교체 알고리즘 전략 패턴 적용

* refactor: 포트에 final 적용

* refactor: 코드 포멧팅

* feat: 핸들러 삽입 구현

* refactor: 레코드 파일 분리

* refactor: 패키지명 변경

* feat: 레코드 자료형 변경

* refactor: pageManager 버퍼풀 내로 이동

* refactor: 페이지 키 객체화

* refactor: key 변경

* refactor: filemanager로 클래스명 변경

* refactor: 개행 추가

* refactor(FileHeader): 불필요 체크섬 필드 제거

* refactor(FileExtension): 파일 확장자 분리

* refactor: 예외처리 구분

* refactor: 테스트 수정
  • Loading branch information
Chocochip101 authored Dec 2, 2024
1 parent 2083b70 commit dc15cdf
Show file tree
Hide file tree
Showing 32 changed files with 1,098 additions and 22 deletions.
6 changes: 6 additions & 0 deletions .idea/git_toolbox_blame.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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

import database.engine.DatabaseServer;

public class Application {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package database;
package database.engine;

import java.io.BufferedReader;
import java.io.InputStreamReader;
Expand All @@ -7,6 +7,7 @@
import java.net.Socket;

public class ClientHandler implements Runnable {

private final Socket clientSocket;

public ClientHandler(Socket clientSocket) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package database;
package database.engine;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class DatabaseServer {
private int port;

private final int port;
private boolean isRunning;

public DatabaseServer(int port) {
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/database/engine/Handler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package database.engine;

import java.util.List;

public interface Handler {

void insert(String tableName, Record record);

List<Record> search(String tableName, Object key);

void update(String tableName, Object key, byte[] newRecord);

void delete(String tableName, Object key);
}

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

import java.util.List;

public class Record {

private final List<Object> values;

public Record(List<Object> values) {
this.values = values;
}

public List<Object> getValues() {
return values;
}

@Override
public String toString() {
return "Record{" +
"values=" + values +
'}';
}
}
19 changes: 0 additions & 19 deletions src/main/java/database/page/Page.java

This file was deleted.

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

import database.engine.Handler;
import database.engine.Record;
import database.storageEngine.bufferpool.BufferPool;
import database.storageEngine.bufferpool.PageReplacementStrategy;
import database.storageEngine.bufferpool.TablePageKey;
import database.storageEngine.bufferpool.pageReplacementStrategies.LRUStrategy;
import database.storageEngine.page.Page;
import database.storageEngine.page.StorageRecord;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class StorageEngineHandler implements Handler {

private static final int BUFFER_SIZE = 40;

private final BufferPool bufferPool;

public StorageEngineHandler() {
PageReplacementStrategy<TablePageKey> lruStrategy = new LRUStrategy<>(BUFFER_SIZE);
this.bufferPool = new BufferPool(BUFFER_SIZE, lruStrategy);
}

@Override
public void insert(String tableName, Record record) {
StorageRecord storageRecord = new StorageRecord(record.getValues());
Page page = bufferPool.findPageWithSpace(tableName, storageRecord);
page.addRecord(storageRecord);
}

@Override
public List<Record> search(String tableName, Object key) {
List<Record> results = new ArrayList<>();

for (long pageNumber = 0; ; pageNumber++) {
TablePageKey tablePageKey = new TablePageKey(tableName, pageNumber);
Optional<Page> pageOpt = bufferPool.getPage(tablePageKey);

if (pageOpt.isPresent()) {
Page page = pageOpt.get();
List<Record> records = page.searchRecords(key).stream()
.map(storageRecord -> new Record(storageRecord.getValues()))
.toList();
if (!records.isEmpty()) {
results.addAll(records);
}
} else {
break;
}
}
return results;
}

@Override
public void update(String tableName, Object key, byte[] newRecord) {
// 디스크에서 레코드를 업데이트하는 로직
}

@Override
public void delete(String tableName, Object key) {
// 디스크에서 레코드를 삭제하는 로직
}
}
87 changes: 87 additions & 0 deletions src/main/java/database/storageEngine/bufferpool/BufferPool.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package database.storageEngine.bufferpool;

import database.storageEngine.page.FileExtension;
import database.storageEngine.page.FileManager;
import database.storageEngine.page.Page;
import database.storageEngine.page.StorageRecord;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.IntStream;

public class BufferPool {

private final int capacity;
private final PageReplacementStrategy<TablePageKey> strategy;
private final Map<TablePageKey, Page> pages;
private final FileManager fileManager;

public BufferPool(int capacity, PageReplacementStrategy<TablePageKey> strategy) {
this.capacity = capacity;
this.strategy = strategy;
this.pages = new HashMap<>();
this.fileManager = new FileManager(FileExtension.IDB);
}

public Optional<Page> getPage(TablePageKey key) {
// Cache hit
if (pages.containsKey(key)) {
strategy.get(key);
return Optional.of(pages.get(key));
}

// Cache miss
Page page = fileManager.loadPage(key);
putPage(key, page);
return Optional.of(page);
}

public void putPage(TablePageKey key, Page page) {
if (!pages.containsKey(key)) {
if (pages.size() >= capacity) {
TablePageKey evictedKey = strategy.evict();
if (evictedKey != null) {
flushPage(evictedKey);
pages.remove(evictedKey);
}
}
pages.put(key, page);
strategy.put(key);
}
}

public void flushPage(TablePageKey key) {
if (pages.containsKey(key)) {
Page page = pages.get(key);
if (page.isDirty()) {
fileManager.savePage(key.tableName(), page);
page.clean();
}
}
}

public void flushAllPages() {
for (Map.Entry<TablePageKey, Page> entry : pages.entrySet()) {
flushPage(entry.getKey());
}
}

public void removePage(TablePageKey key) {
if (pages.containsKey(key)) {
flushPage(key);
pages.remove(key);
strategy.evict();
}
}

public Page findPageWithSpace(String tableName, StorageRecord storageRecord) {
return IntStream.range(0, capacity)
.mapToObj(pageNumber -> {
TablePageKey key = new TablePageKey(tableName, pageNumber);
return pages.get(key);
})
.filter(page -> page.getFreeSpace() >= storageRecord.getSize())
.findFirst()
.orElseGet(fileManager::createNewDataPage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package database.storageEngine.bufferpool;

import java.util.Optional;

public interface PageReplacementStrategy<K> {

Optional<K> get(K key);

void put(K key);

K evict();

boolean contains(K key);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package database.storageEngine.bufferpool;

public record TablePageKey(String tableName, long pageNumber) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package database.storageEngine.bufferpool.pageReplacementStrategies;

import database.storageEngine.bufferpool.PageReplacementStrategy;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;

public class LFUStrategy<K> implements PageReplacementStrategy<K> {

private final int capacity;
private final Map<K, Integer> usageCount;
private final Map<K, Long> cache;

public LFUStrategy(int capacity) {
this.capacity = capacity;
this.usageCount = new HashMap<>();
this.cache = new HashMap<>();
}

@Override
public void put(K key) {
if (cache.size() >= capacity) {
evict();
}
cache.put(key, System.nanoTime()); // 현재 시간으로 타임스탬프 저장
usageCount.put(key, usageCount.getOrDefault(key, 0) + 1);
}

@Override
public K evict() {
K leastUsedKey = findLeastUsedKey();
if (leastUsedKey != null) {
cache.remove(leastUsedKey);
usageCount.remove(leastUsedKey);
}
return leastUsedKey;
}

@Override
public boolean contains(K key) {
return cache.containsKey(key);
}

private K findLeastUsedKey() {
Optional<Entry<K, Integer>> minEntry = usageCount.entrySet().stream()
.min((entry1, entry2) -> {
int freqCompare = entry1.getValue().compareTo(entry2.getValue());
if (freqCompare == 0) {
// 동일한 사용 빈도일 경우, 먼저 들어온 키를 우선 제거
return cache.get(entry1.getKey()).compareTo(cache.get(entry2.getKey()));
}
return freqCompare;
});
return minEntry.map(Map.Entry::getKey).orElse(null);
}

@Override
public Optional<K> get(K key) {
if (cache.containsKey(key)) {
usageCount.put(key, usageCount.get(key) + 1);
return Optional.of(key);
}
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package database.storageEngine.bufferpool.pageReplacementStrategies;

import database.storageEngine.bufferpool.PageReplacementStrategy;
import java.util.LinkedHashSet;
import java.util.Optional;

public class LRUStrategy<K> implements PageReplacementStrategy<K> {

private final int capacity;
private final LinkedHashSet<K> cache;

public LRUStrategy(int capacity) {
this.capacity = capacity;
this.cache = new LinkedHashSet<>(capacity);
}

@Override
public void put(K key) {
if (cache.contains(key)) {
cache.remove(key);
} else if (cache.size() >= capacity) {
evict();
}
cache.add(key);
}

@Override
public K evict() {
K firstKey = cache.iterator().next();
cache.remove(firstKey);
return firstKey;
}

@Override
public boolean contains(K key) {
return cache.contains(key);
}

@Override
public Optional<K> get(K key) {
if (cache.contains(key)) {
cache.remove(key);
cache.add(key);
return Optional.of(key);
}
return Optional.empty();
}
}

Loading

0 comments on commit dc15cdf

Please sign in to comment.