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

[Paged File, I/O System] 초코칩(권기호) 미션 제출합니다. #3

Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9c43ee7
feat: 페이지 구조 구현
Chocochip101 Aug 18, 2024
476502a
feat: 페이지 직렬화
Chocochip101 Aug 25, 2024
d1fd4b4
feat: 페이지 팩터리 패턴 적용
Chocochip101 Aug 25, 2024
a4e8b41
feat: 페이지 매니저 생성
Chocochip101 Aug 25, 2024
76ac0c5
test: DisplayName 수정
Chocochip101 Aug 25, 2024
7f03c3e
feat: 불필요 PageManager 삭제
Chocochip101 Aug 25, 2024
511b3d0
feat: 버퍼풀 구현
Chocochip101 Aug 25, 2024
8c02942
refactor: innodb 패키지 생성
Chocochip101 Aug 25, 2024
e4e9aa4
feat: 더티 상태 구현
Chocochip101 Aug 25, 2024
b2f400f
feat: 페이지 내의 레코드 기능 구현
Chocochip101 Aug 26, 2024
7e67c12
feat: 페이지 디렉터리 구현
Chocochip101 Aug 26, 2024
aaa047f
feat: 레코드 추가 기능 구현
Chocochip101 Aug 26, 2024
7f32e11
feat: 레코드 변경 시, 더티 체크
Chocochip101 Aug 27, 2024
d1a989f
feat: 테이블 별로 페이지 저장
Chocochip101 Aug 27, 2024
74c8f41
feat: 페이지 번호 반환
Chocochip101 Aug 27, 2024
7142ae7
refactor: 버퍼풀 패키지 생성
Chocochip101 Aug 27, 2024
a6c932d
feat: 페이지 교체 알고리즘 전략 패턴 적용
Chocochip101 Aug 27, 2024
f57d61f
refactor: 포트에 final 적용
Chocochip101 Aug 27, 2024
63d1330
refactor: 코드 포멧팅
Chocochip101 Aug 27, 2024
4dc0e44
feat: 핸들러 삽입 구현
Chocochip101 Aug 27, 2024
ff5cfbb
refactor: 레코드 파일 분리
Chocochip101 Aug 27, 2024
52b7b34
refactor: 패키지명 변경
Chocochip101 Aug 27, 2024
bb6571a
feat: 레코드 자료형 변경
Chocochip101 Aug 27, 2024
b0a1823
refactor: pageManager 버퍼풀 내로 이동
Chocochip101 Aug 27, 2024
f79eac0
refactor: 페이지 키 객체화
Chocochip101 Aug 27, 2024
477047e
refactor: key 변경
Chocochip101 Aug 27, 2024
709092c
refactor: filemanager로 클래스명 변경
Chocochip101 Aug 27, 2024
9691d94
refactor: 개행 추가
Chocochip101 Sep 3, 2024
7b7dc57
refactor(FileHeader): 불필요 체크섬 필드 제거
Chocochip101 Nov 28, 2024
8c3fede
refactor(FileExtension): 파일 확장자 분리
Chocochip101 Nov 28, 2024
5a5359c
refactor: 예외처리 구분
Chocochip101 Nov 28, 2024
e73e8ab
refactor: 테스트 수정
Chocochip101 Nov 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: 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) {
// 디스크에서 레코드를 삭제하는 로직
}
}
89 changes: 89 additions & 0 deletions src/main/java/database/storageEngine/bufferpool/BufferPool.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package database.storageEngine.bufferpool;

import database.storageEngine.page.Page;
import database.storageEngine.page.FileManager;
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;
Chocochip101 marked this conversation as resolved.
Show resolved Hide resolved
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();
}

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

Optional<Page> optionalPage = fileManager.loadPage(key);
if (optionalPage.isPresent()) {
Page page = optionalPage.get();
putPage(key, page);
return Optional.of(page);
}

return Optional.empty();
}

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> {
Chocochip101 marked this conversation as resolved.
Show resolved Hide resolved

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