Skip to content
Draft
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
20 changes: 20 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ DOCREADER_TRANSPORT=grpc

# ========== B1. 数据库 ⚠️ 必填 ==========
# 主数据库类型:postgres / mysql / sqlite。
# mysql 模式要求 MySQL 8.0.16+(CHECK 约束 / SKIP LOCKED / JSON_LENGTH / utf8mb4_0900_ai_ci),
# 且 RETRIEVE_DRIVER 不得为 postgres 或 sqlite(向量检索需委托给外部引擎)。
DB_DRIVER=postgres
# 数据库主机地址。
DB_HOST=postgres
Expand All @@ -113,6 +115,23 @@ DB_PASSWORD=postgres123!@#
DB_NAME=WeKnora
# SQLite 驱动时使用(DB_DRIVER=sqlite),postgres/mysql 忽略。
# DB_PATH=./data/weknora.db
#
# MySQL 连接池与超时(仅 DB_DRIVER=mysql 时生效):
# DB_CONNECT_TIMEOUT=10s # 连接超时
# DB_READ_TIMEOUT=30s # 读超时
# DB_WRITE_TIMEOUT=30s # 写超时
# DB_MAX_OPEN_CONNS=50 # 最大打开连接数
# DB_MAX_IDLE_CONNS=10 # 最大空闲连接数
# DB_CONN_MAX_LIFETIME=10m # 连接最大存活时间
# DB_CONN_MAX_IDLE_TIME=5m # 空闲连接最大存活时间
#
# MySQL TLS(外部或要求加密传输的 MySQL;仅 DB_DRIVER=mysql 时生效):
# DB_USE_TLS=false
# DB_TLS_SERVER_NAME= # SNI / 证书校验名称
# DB_TLS_CA= # CA PEM 文件路径
# DB_TLS_CERT= # mTLS 客户端证书 PEM(必须与 DB_TLS_KEY 同时设置)
# DB_TLS_KEY= # mTLS 客户端私钥 PEM
# DB_TLS_INSECURE_SKIP_VERIFY=false # 不安全,仅用于本地自签证书

# ========== B2. Redis / 流处理 / Asynq 队列 ==========
# --- 流处理后端与 Redis 连接 ---
Expand Down Expand Up @@ -236,6 +255,7 @@ LOCAL_STORAGE_BASE_DIR=/data/files
# ========== C1. 向量库 / 检索引擎 ==========
# 向量存储类型(逗号分隔可多驱动):postgres / elasticsearch_v7 / elasticsearch_v8 /
# opensearch / qdrant / milvus / weaviate / doris / tencent_vectordb。
# 注意:DB_DRIVER=mysql 时,此处不能填 postgres(启动会校验失败)。
RETRIEVE_DRIVER=postgres
# 多向量库并行检索超时(秒,RETRIEVE_DRIVER 含多个驱动时生效)。
# MULTI_STORE_RETRIEVE_TIMEOUT_SEC=
Expand Down
38 changes: 38 additions & 0 deletions .github/workflows/app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,41 @@ jobs:

- name: Build server
run: go build ./cmd/server

# The repository-layer tests in the job above run on SQLite, which silently
# accepts SQL that MySQL rejects — the PostgreSQL bare-key `col->>'key'` form
# is the clearest example. Without a real server in CI, the MySQL integration
# tests skip themselves and stop being a signal.
mysql-integration:
name: MySQL integration tests
runs-on: ubuntu-latest
timeout-minutes: 20
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: weknora-test
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -pweknora-test"
--health-interval=5s
--health-timeout=5s
--health-retries=20
steps:
- uses: actions/checkout@v6

- uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
cache: true

- name: Download modules
run: go mod download

# Each test creates and drops its own database, so it only needs a server
# to connect to; `mysql` is the always-present bootstrap schema.
- name: Run MySQL integration tests
env:
WEKNORA_MYSQL_TEST_DSN: "root:weknora-test@tcp(127.0.0.1:3306)/mysql?charset=utf8mb4&collation=utf8mb4_0900_ai_ci&parseTime=true&loc=UTC"
run: go test ./internal/application/repository/ -run TestMySQL -v
88 changes: 88 additions & 0 deletions docker-compose.mysql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# MySQL override for docker-compose.yml
#
# Swap the metadata database from PostgreSQL to MySQL 8.0+. Usage:
#
# docker compose --profile qdrant -f docker-compose.yml -f docker-compose.mysql.yml up -d
#
# Required environment (in .env or shell):
# DB_DRIVER=mysql
# DB_HOST=postgres # the service name is unchanged so app's depends_on resolves
# DB_PORT=3306
# DB_USER=weknora
# DB_PASSWORD=<your-password>
# DB_NAME=weknora
# MYSQL_ROOT_PASSWORD=<root-password> # required; no fixed fallback
# RETRIEVE_DRIVER=qdrant # MUST NOT be postgres or sqlite — MySQL mode
# # never creates the embeddings table. Use an
# # external engine: qdrant, milvus, elasticsearch_v8,
# # opensearch, doris, weaviate, tencent_vectordb.
#
# The app validates this combination at startup and fails fast with an
# actionable message if RETRIEVE_DRIVER is misconfigured.
#
# Limitations under MySQL:
# - Wiki keyword search uses multi-column LIKE instead of PostgreSQL's
# to_tsvector / pg_trgm. Matching and ranking semantics differ.
# - The full / langfuse profile assumes the `postgres` service is really
# PostgreSQL; this override replaces it with MySQL, so Langfuse init
# will fail. Deploy a separate PostgreSQL instance for Langfuse.
#
# NOTE: this override only changes the metadata database. To actually run
# retrieval you also need the chosen external engine (qdrant/milvus/...)
# declared as a service and reachable. See docs/使用其他向量数据库.md.

services:
postgres: # keep the name so the app's depends_on: postgres resolves
image: mysql:8.0.37 # pinned supported patch version
container_name: WeKnora-mysql
environment:
# Clear the inherited PostgreSQL env vars — without this the MySQL
# image would still receive POSTGRES_USER / POSTGRES_PASSWORD /
# POSTGRES_DB (harmless but confusing) from the base compose file.
- POSTGRES_USER=
- POSTGRES_PASSWORD=
- POSTGRES_DB=
- MYSQL_USER=${DB_USER:?DB_USER is required for MySQL deployments}
- MYSQL_PASSWORD=${DB_PASSWORD:?DB_PASSWORD is required for MySQL deployments}
- MYSQL_DATABASE=${DB_NAME:?DB_NAME is required for MySQL deployments}
# Root password is deliberately separate from DB_PASSWORD so the app
# credential and the admin credential can be rotated independently.
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:?MYSQL_ROOT_PASSWORD is required for MySQL deployments}
# The base docker-compose.yml defines this service's `volumes` as a LIST
# (`- postgres-data:/var/lib/postgresql/data`). Docker Compose normally
# *appends* list entries across files, so a plain `- mysql-data:...`
# override would leave both volumes mounted (postgres-data dangling and
# unused). Using the `!override` tag tells Compose to REPLACE the list
# entirely, so only mysql-data is mounted.
#
# Requires Docker Compose v2.24+ (Jan 2024) for the `!override` tag.
# On older Compose, drop `!override` and accept the harmless extra
# empty postgres-data mount at /var/lib/postgresql/data.
volumes: !override
- mysql-data:/var/lib/mysql
networks:
- WeKnora-network
healthcheck:
# mysqladmin ping returns success once the server answers, even before
# accounts are fully provisioned. No password is needed for a basic
# connectivity check, so we intentionally omit -u/-p (which would also
# leak credentials into the container's inspect output).
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 10s
retries: 5
start_period: 30s
restart: unless-stopped
stop_grace_period: 1m
# utf8mb4 is the default in MySQL 8.0, but set it explicitly so the
# character set is deterministic regardless of the server's config file.
# --default-authentication-plugin=caching_sha2_password is intentionally
# NOT set: it was deprecated in 8.0.34 and removed in 8.4. caching_sha2
# is already the default in 8.0.
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_0900_ai_ci
- --default-time-zone=+00:00

volumes:
mysql-data:
5 changes: 3 additions & 2 deletions docker/Dockerfile.app
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ RUN if [ -n "$APK_MIRROR_ARG" ]; then \
apt-get update && \
apt-get install -y git build-essential libsqlite3-dev

# Install migrate tool
RUN go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
# Install the migration CLI with both metadata database drivers. Keep the
# version aligned with go.mod so local and container behavior stay identical.
RUN go install -tags 'postgres mysql' github.com/golang-migrate/migrate/v4/cmd/migrate@v4.19.1

# Copy go mod and sum files
COPY go.mod go.sum ./
Expand Down
Loading
Loading