Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@ Docsa는 문서의 변경 이력과 다양한 버전을 효율적으로 관리
이 프로젝트의 목표는 Git을 모르는 사용자도 손쉽게 버전 관리를 활용하여 문서를 편집하고 운용할 수 있도록 돕는 것입니다.
<br><br>
사용자는 그래프로 구현된 시각적인 UI를 통해 문서를 직접 편집하고, 다양한 버전 흐름을 한눈에 확인하며 관리할 수 있습니다.
Docsa는 문서의 변경 사항을 기록(commit) 단위로 추적하고, 버전(branch) 를 분기하거나, 서로 다른 버전을 병합(merge) 할 수 있는 강력한 기능을 제공합니다. <br><br>
Docsa는 문서의 변경 사항을 기록(commit) 단위로 추적하고, 브랜치(branch)를 생성하거나, 서로 다른 브랜치를 병합(merge) 할 수 있는 강력한 기능을 제공합니다. <br><br>
이러한 기능은 editor.js 기반의 블록 단위 저장 방식을 통해 구현되며, 변경된 블록만을 감지하여 데이터베이스에 저장하고 이를 조합하여 기록함으로써 저장 효율성과 자원 활용도를 극대화합니다.

## 📌 주요 기능
>*Git의 개념과 용어를 모르는 일반인 유저를 위해, Docsa에서는 일반적인 Git의 개념과 대응되는 용어를 다음과 같이 재정의합니다. <br>
>(기록: commit, 버전: branch, 병합하기: merge)
>(기록: commit, 브랜치: branch, 병합하기: merge)

### 문서 버전 관리
### 문서 브랜치 관리

- 문서는 기록,저장과 버전을 가진 최상위 단위입니다. 문서마다 모든 변경 사항을 기록 단위로 저장하여, 원하는 시점의 기록을 조회 (checkout) 할 수 있으며, 메인화면에서 생성과 삭제가 가능합니다.
- 문서는 기록, 저장과 브랜치를 가진 최상위 단위입니다. 문서마다 모든 변경 사항을 기록 단위로 저장하여, 원하는 시점의 기록을 조회(checkout) 할 수 있으며, 메인화면에서 생성과 삭제가 가능합니다.

### 버전(branch) 분기 및 병합
### 브랜치(branch) 생성 및 병합

- 하나의 문서에서 여러 버전을 생성하여 자유롭게 분기해나갈 수 있으며, 브랜치 간 병합을 통해 작업 내용을 통합할 수 있습니다. main버전 이외 버전 단위의 삭제도 가능합니다.
- 하나의 문서에서 여러 브랜치를 생성하여 자유롭게 작업을 분기할 수 있으며, 브랜치 간 병합을 통해 작업 내용을 통합할 수 있습니다. main 브랜치 이외 브랜치 단위의 삭제도 가능합니다.

### 저장(Save) 및 기록(Commit) 시스템

Expand All @@ -32,9 +32,9 @@ Docsa는 문서의 변경 사항을 기록(commit) 단위로 추적하고, 버

- ‘비교하기’를 통해 현재 보고있는 기록과 다른 기록을 문단 단위로 비교할 수 있습니다.

### 시각화된 버전 그래프 UI
### 시각화된 브랜치 그래프 UI

- 기록과 저장,브랜치 간의 관계를 트리 구조 그래프로 시각화하여, 문서의 버전 흐름을 직관적으로 확인할 수 있습니다.
- 기록과 저장, 브랜치 간의 관계를 트리 구조 그래프로 시각화하여, 문서의 브랜치 흐름을 직관적으로 확인할 수 있습니다.

### 실시간 문서 편집

Expand Down Expand Up @@ -156,4 +156,3 @@ Docsa는 문서의 변경 사항을 기록(commit) 단위로 추적하고, 버




8 changes: 3 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,22 @@ dependencies {
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

implementation 'org.springframework.retry:spring-retry'
implementation 'org.springframework:spring-aspects'

implementation 'io.micrometer:micrometer-registry-prometheus'
implementation 'org.springframework.boot:spring-boot-starter-actuator'

compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'com.h2database:h2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation 'org.awaitility:awaitility:4.2.0'

implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.12.1'

implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-mysql'

implementation platform('software.amazon.awssdk:bom:2.42.35')
implementation 'software.amazon.awssdk:s3'

Expand Down
13 changes: 13 additions & 0 deletions infra/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,16 @@ MYSQL_ROOT_PASSWORD=
# ------------ GRAFANA ------------
GRAFANA_ADMIN=
GRAFANA_PASSWORD=

# ------------ AWS ------------
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=

AWS_BUCKET=
AWS_PRESIGNED_EXPIRE_MIN=
AWS_REGION=
CDN_URL=

# ------------ FLYWAY ------------
FLYWAY_BASELINE_ON_MIGRATE=
FLYWAY_BASELINE_VERSION=
6 changes: 6 additions & 0 deletions infra/docker-compose.stg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ services:
image: prom/prometheus:latest
container_name: prometheus-stg
restart: unless-stopped
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--web.enable-remote-write-receiver"
ports:
- "${PROMETHEUS_BIND_ADDR:-127.0.0.1}:9090:9090"
depends_on:
- cadvisor
volumes:
Expand Down
8 changes: 4 additions & 4 deletions perf/delete/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

## Files

- `perf/delete/seed_dataset.js`
- 유저별 문서/커밋/브랜치 데이터 생성
- `perf/seed/seed_dataset.js`
- 유저별 문서/커밋/브랜치 데이터 생성. delete/read/thumbnail E2E에서 공용으로 사용
- `perf/delete/delete_only_benchmark.js`
- 생성된 문서를 찾아 삭제만 수행
- `perf/delete/compare_delete_summary.mjs`
Expand Down Expand Up @@ -69,7 +69,7 @@ USER_PREFIX=perfuser USER_DOMAIN=test.com USER_PASSWORD=Testtest1 \
USER_COUNT=100 DOCS_PER_USER=3 \
MAIN_COMMITS=6 FEATURE_COMMITS=4 BLOCKS_PER_COMMIT=20 \
SEED_VUS=20 \
k6 run perf/delete/seed_dataset.js
k6 run perf/seed/seed_dataset.js
```

생성 규칙:
Expand Down Expand Up @@ -103,7 +103,7 @@ k6 run perf/delete/delete_only_benchmark.js \
1. 브랜치 checkout
2. 서버 실행
3. 유저 준비(`PERF_SEED_USER_COUNT`, `PERF_SEED_DOCS_PER_USER=0` 적용된 상태)
4. 데이터 생성 (`seed_dataset.js`)
4. 데이터 생성 (`perf/seed/seed_dataset.js`)
5. 삭제 벤치 실행 (`delete_only_benchmark.js`)
6. 결과 파일 저장

Expand Down
8 changes: 4 additions & 4 deletions perf/read/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

## Files

- `perf/delete/seed_dataset.js`
- `perf/seed/seed_dataset.js`
- 목록 테스트에 사용할 문서/브랜치/커밋 데이터를 생성
- `perf/read/doc_list_benchmark.js`
- `sidebar`, `full list`, `search` 읽기 성능 측정
Expand All @@ -35,7 +35,7 @@ PERF_SEED_DOCS_PER_USER=0

## 2) Seed dataset

삭제 벤치에서 쓰던 seed 스크립트를 그대로 재사용합니다.
공용 seed 스크립트를 사용합니다.
중요한 건 목록 조회 시 문서별 branch/commit이 충분히 많아지도록 데이터를 만드는 것입니다.

```bash
Expand All @@ -45,7 +45,7 @@ USER_PREFIX=perfuser USER_DOMAIN=test.com USER_PASSWORD=Testtest1 \
USER_COUNT=50 DOCS_PER_USER=5 \
MAIN_COMMITS=8 FEATURE_COMMITS=5 BLOCKS_PER_COMMIT=100 \
SEED_VUS=20 \
k6 run perf/delete/seed_dataset.js
k6 run perf/seed/seed_dataset.js
```

애플리케이션 initializer로 DB에 직접 주입할 수도 있습니다. 이 방식은 API 호출 비용 없이
Expand Down Expand Up @@ -89,7 +89,7 @@ SEARCH_PREFIX=PERF

결과 파일:

- `perf/read/results/doc_list_benchmark_<RUN_ID>.json`
- `perf/read/results/<test-name>/.../*.json`

핵심 지표:

Expand Down
6 changes: 4 additions & 2 deletions perf/read/doc_list_benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const USER_COUNT = Number(__ENV.USER_COUNT || 50);
const DOCS_PER_USER = Number(__ENV.DOCS_PER_USER || 3);
const PAGE_SIZE = Number(__ENV.PAGE_SIZE || 10);
const RUN_ID = __ENV.RUN_ID;
const DATASET_RUN_ID = __ENV.DATASET_RUN_ID || RUN_ID;
const RESULT_DIR = __ENV.RESULT_DIR || 'perf/read/results';
const SEARCH_PREFIX = __ENV.SEARCH_PREFIX || 'PDEL';

Expand All @@ -19,6 +20,7 @@ if (!RUN_ID) {
}

export const options = {
summaryTrendStats: ['min', 'med', 'avg', 'p(90)', 'p(95)', 'p(99)', 'max'],
scenarios: {
sidebar_list: {
executor: 'constant-vus',
Expand Down Expand Up @@ -70,9 +72,9 @@ function userEmail(userNo) {

function keywordForUser(userNo) {
if (SEARCH_PREFIX === 'PERF') {
return `PERF-${RUN_ID}-u${pad3(userNo)}`;
return `PERF-${DATASET_RUN_ID}-u${pad3(userNo)}`;
}
return `${SEARCH_PREFIX}-${RUN_ID}u${pad3(userNo)}`;
return `${SEARCH_PREFIX}-${DATASET_RUN_ID}u${pad3(userNo)}`;
}

function headers(cookie) {
Expand Down
10 changes: 8 additions & 2 deletions perf/delete/seed_dataset.js → perf/seed/seed_dataset.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const MAIN_COMMITS = Number(__ENV.MAIN_COMMITS || 6);
const FEATURE_COMMITS = Number(__ENV.FEATURE_COMMITS || 4);
const BLOCKS_PER_COMMIT = Number(__ENV.BLOCKS_PER_COMMIT || 20);
const RUN_ID = __ENV.RUN_ID || Math.floor(Date.now() / 1000).toString(36);
const RESULT_DIR = __ENV.RESULT_DIR || '';

const TOTAL_DOCS = USER_COUNT * DOCS_PER_USER;

Expand Down Expand Up @@ -220,8 +221,13 @@ export default function () {
}

export function handleSummary(data) {
return {
const summary = {
stdout: `\n[seed-dataset] run_id=${RUN_ID}, users=${USER_COUNT}, docs_per_user=${DOCS_PER_USER}, total_docs=${TOTAL_DOCS}\n`,
'perf/delete/results/seed_dataset_summary.json': JSON.stringify(data, null, 2),
};

if (RESULT_DIR) {
summary[`${RESULT_DIR}/seed_dataset_summary.json`] = JSON.stringify(data, null, 2);
}

return summary;
}
Loading
Loading