plantpulse-storage (Storage Layer)
Role
The layer that persistently stores all PlantPulse data. Four specialized engines are bundled into a single module, organized by data type. It is first in the startup sequence defined in pd, and acts as a gatekeeper — if it does not start, no subsequent services are attempted.
| Item | Value |
|---|---|
| Module name | plantpulse-storage |
| Installation path | /opt/kopens/plantpulse-platform/plantpulse-storage/ |
| Data location | /data1/pp-data/ (PP_DATA_DIR, volume pp-data) |
| Backup location | /data1/pp-backup/ (PP_BACKUP_DIR, volume pp-backup) |
pd service | storage — both MASTER and WORKER (MinIO on MASTER only) |
| Startup order | valkey → postgres → cassandra → minio |
Architecture
Directory Structure
plantpulse-storage/
├── cache/valkey/ # conf/valkey.conf (생성물) · 로그는 /data1/pp-data/valkey/logs
├── db/
│ ├── cassandra/ # bin/ (cqlsh · nodetool) · conf/cassandra.yaml · jvm-server.options (생성물) · logs/
│ └── postgres/ # bin/ (psql · pg_ctl) · conf/postgresql.conf · pg_hba.conf (생성물) · logs/
├── object/
│ ├── minio/ # bin/ · logs/
│ ├── rustfs/ weedfs/ # 대안 오브젝트 스토어 — 이미지에 있지만 pd 가 기동하지 않습니다
├── graph/janusgraph/ # 그래프 DB — 이미지에 있지만 pd 가 기동하지 않습니다
└── tools/reaper/ # Cassandra Reaper — 이미지에 있지만 pd 가 기동하지 않습니다
All files under conf/ are generated artifacts. The place to modify is the host's /etc/kopens/conf/*.template → How to Change Configuration
Each Engine
Cassandra — Time Series and Events
| Item | Value |
|---|---|
| Port | 9042 (CQL) · 7000/7001 (inter-node) · 7099 (JMX) |
| Keyspace | pp (PP_KEYSPACE) |
| Account | cassandra (PP_CASSANDRA_USER) / PP_CASSANDRA_PASSWORD — PasswordAuthenticator |
| Configuration | cassandra.yaml.template · jvm-server.options.template (WORKER uses cluster/ variant) |
| Advertised address | broadcast_rpc_address — rendering fails if 127.0.0.1 |
Key tables: pp.ts_data_points (time series engine data points, per-row TTL), pp.tm_tag_point · tm_tag_point_archive · tm_tag_point_snapshot (tags), pp.tm_asset_data (assets), pp.tw_* (record of cold tier migrations). pd retention shows TTL, compaction strategy, and size per table.
docker exec plantpulse-datalake pd node status # 링 상태 — UN 이 정상
docker exec plantpulse-datalake pd node cql # cqlsh
docker exec plantpulse-datalake pd node errors # 최근 WARN/ERROR
docker exec plantpulse-datalake pd node compactionstats
docker exec plantpulse-datalake pd node repair # 무겁습니다
docker exec plantpulse-datalake pd node cleanup # 파괴적 — 스냅샷 삭제
| Tuning Point | Template | Recommended |
|---|---|---|
| Heap | jvm-server.options.template | 1/4 of host memory, max 32 GB |
compaction_throughput | cassandra.yaml.template | 128 MB/s (NVMe) / 16 MB/s (HDD) |
concurrent_compactors | cassandra.yaml.template | min(코어 수, 8) |
Cassandra Reaper is included in image tools/reaper/ but is a manual tool that pd does not start. Schedule repairs via pd node repair.
PostgreSQL — Metadata
| Item | Value |
|---|---|
| Port | 5432 |
| Database | pp (PP_DB_NAME) — others include temporal · temporal_visibility · kestra · hive-metastore-230 |
| Account | plantpulse / PP_PG_PASSWORD. Roles temporal · hive · gravitino · kestra · replica also here |
| Authentication | scram-sha-256 (pg_hba.conf.template) |
| Configuration | postgresql.conf.template · pg_hba.conf.template |
On first boot, schema and roles are created without passwords, and pd syncs role passwords via sidecar values (on every boot, ALTER ROLE — idempotent). Thus PostgreSQL accounts not handled by rotation tools like PP_GRAVITINO_PASSWORD · PP_KESTRA_DB_PASSWORD can be managed by editing the sidecar and restarting.
docker exec -it plantpulse-datalake pd node psql # postgres OS 사용자로 psql
docker exec plantpulse-datalake pd storage # WAL · 복제 슬롯 · max_slot_wal_keep_size
docker exec plantpulse-datalake pd backup # 논리 덤프
| Tuning Point | Recommended |
|---|---|
shared_buffers | 25% of host memory |
effective_cache_size | 50–75% |
max_connections | 200 (500 for large deployments) |
max_slot_wal_keep_size | Set an upper limit — prevents replication slots from removed workers from holding WAL indefinitely (image default 8 GB) |
Valkey — In-Memory Cache
| Item | Value |
|---|---|
| Port | 6379 (plain) · 6380 (TLS) |
| Account | redis (PP_REDIS_USER) / PP_REDIS_PASSWORD — requirepass |
| Configuration | valkey.conf.template (WORKER uses cluster/ variant — replicaof master) |
| Log | /data1/pp-data/valkey/logs/system.log — located under data volume |
docker exec -it plantpulse-datalake bash -c '"$PP_HOME/tools/valkey/bin/valkey-cli" -h 127.0.0.1 -a "$PP_REDIS_PASSWORD"'
> INFO memory
> CONFIG GET maxmemory
MinIO — Object Storage
| Item | Value |
|---|---|
| Port | 9000 (S3 API — monitored by pd) · 9001 (console — user-facing) |
| Account | minio (PP_MINIO_USER) / PP_MINIO_PASSWORD — injected via startup env MINIO_ROOT_* |
| Default bucket | plantpulse (PP_MINIO_BUCKET). Iceberg data, backups, attachments |
| Console | http://<server-ip>:9001/ — login uses the same credentials as above |
MinIO runs on MASTER nodes only. The mc client is in the image, and the startup script registers the pp-minio alias.
Unified Operations
docker exec plantpulse-datalake pd status storage # 넷의 UP/DOWN
docker exec plantpulse-datalake pd restart storage # 넷 전부 — 의존하는 모든 서비스가 연결을 잃습니다
docker exec plantpulse-datalake pd storage # 볼륨 · WAL · 슬롯 · 스냅샷 · 크기
storage breaks everything aboveWhen possible, restart the entire datalake via host restart-datalake.sh. It also determines dependent apps → Start · Stop · Restart
Backup / Recovery
| Engine | Tool | Method |
|---|---|---|
| PostgreSQL | pgBackRest (physical) · pg_dump (logical) | full / diff · point-in-time recovery |
| Cassandra | Medusa | full / diff snapshots |
| MinIO | mc mirror | external S3 replication |
Procedures are in Backup and Restore.
Common Issues
| Symptom | Cause | Action |
|---|---|---|
Cassandra WriteTimeout | disk I/O saturation | pd node compactionstats, adjust throughput |
| Cassandra node down | network · disk · OOM | GC pause in pd logs storage, check host dmesg |
PostgreSQL Too many connections | connection pool leak | raise max_connections + inspect application pool |
| WAL grows continuously | replication slot from removed worker | pd storage → slot.orphan → restart host ops-check.sh --reclaim |
| Valkey out of memory | maxmemory-policy not set | allkeys-lru or volatile-lru |
| MinIO disk full | Iceberg · backup accumulation | set lifecycle policy in console 9001 |
| delayed response after restart | Cassandra warmup | normal (3–5 min) |
Recommended Disk Layout
/data1/
├── pp-data/ # 데이터 볼륨 (NVMe SSD)
│ ├── cassandra/ # SSTable · commitlog · saved_caches
│ ├── postgres/ # data · dump
│ ├── minio/
│ ├── valkey/
│ └── kafka/
├── pp-temp/
└── pp-backup/ # pgbackrest · medusa
Separating Cassandra commitlog and data onto separate disks (or separate LVs) significantly improves write throughput.