RAGFlow Internal Module

High-Performance Go Backend + C++ Tokenizer

A parallel service layer to the Python API, built for low-latency search, chat, and embeddings using Gin web framework, GORM ORM, and cgo bindings to optimized C++ text analysis.

What You'll Learn

RAGFlow's internal/ module is a production Go backend running alongside the Python api/ layer. It handles high-concurrency workloads (search, chat, embeddings) by leveraging Go's goroutines (no GIL), compiled binaries (predictable latency), and C++ for text analysis (DARTS double-array trie for tokenization).

The layered architecture (Router › Handler › Service › DAO › Database) separates concerns, ensures testability, and scales independently. cgo bindings to C++ add ~100ns overhead per call, but batch operations reduce amortized cost to <0.1% per token.

Why coexist with Python? Go excels at I/O concurrency and compiled performance; Python excels at ML and scripting. This hybrid approach combines both strengths.

Architecture Overview

15
Packages
218
Go Files
92
C++/H Files
~450ms
Chat Latency (p50)

System Diagram

External Clients (Browser, Mobile)
           |
           v
    Nginx Reverse Proxy
    (TLS, load balancing)
           |
       +---+---+
       v       v
    Python    Go Gin
    FastAPI   Backend
       |       |
       +---+---+
           v
      PostgreSQL
      Database
           |
      +----+----+
      v         v
   Python    Go Worker
   Queue     (cli/)
   (Redis)

Request Lifecycle (High Level)

  1. Client Request: Browser sends HTTP to Go Gin server
  2. Router Dispatch: Gin routes to handler (ChatHandler, SearchHandler, etc.)
  3. Handler Parse: Validates JWT, unmarshals JSON, enforces scopes
  4. Service Orchestration: Business logic, load KB context, call search, enforce quotas
  5. DAO Queries: GORM models execute SQL (find docs, fetch embeddings)
  6. C++ Analysis: If tokenization needed, cgo call to C++ analyzer (DARTS lookup)
  7. Response Build: Service aggregates results, Handler marshals JSON
  8. Send Response: Gin sends HTTP 200 to client
  9. Metrics Logged: Latency, token count, result count recorded
  10. Cache Updates: LRU embedding cache refreshed if new vectors fetched
  11. Async Tasks: Search query recorded in log table for analytics
  12. Client Receives: Browser renders search results

Module Organization

15 focused packages. Click each card to explore further:

Handler

HTTP request parsing, validation, response formatting. Translates HTTP ↔ Go types.

Service

Business logic layer. Orchestrates DAOs, enforces transactions, manages caching.

DAO

Data Access Objects using GORM. Domain-specific query methods.

C++ Backend (via cgo)

High-performance text analysis: DARTS tokenizer, lemmatization, OpenCC.

Router

Gin route registration. Middleware setup (CORS, auth, logging).

Server

Application startup, config loading, graceful shutdown.

Entity

Domain models (File, Pipeline, Connector). Used for API contracts.

Engine

Document processing pipeline. Splits PDFs, extracts text, chunks.

Storage

File I/O abstraction. Upload/download to S3, MinIO, or local fs.

Cache

In-memory caching (embeddings, frequent queries). LRU eviction.

Tokenizer

Go wrapper around C++ analyzer. Batching support.

Binding

cgo FFI definitions. Go declarations for C functions.

Common

Shared constants, utilities, logging setup, DB pooling.

Utility

Helper functions: UUID generation, tokens, paths, version info.

CLI & Admin

Command-line tools, admin endpoints, system status, diagnostics.

Why Coexist with Python API?

AspectPython (api/)Go (internal/)
ConcurrencyAsync; GIL limits true parallelismGoroutines; true parallelism
Latency~5-10ms overhead per request~1-2ms startup; predictable
Throughput~500 req/s; bottleneck at GIL~1,000 req/s; scales with cores
Memory per worker~100MB per process~2KB per goroutine
Best use casesUser auth, LLM calls, model servingSearch, chat, embeddings, real-time I/O

Key Design Decisions

Gin Web Framework

Provides fast routing (radix tree), built-in middleware, JSON binding, request validation. Trade-off: adds ~30KB to binary.

GORM ORM

Single source of truth (Go struct = schema). Auto-migration, hooks for timestamps. Trade-off: 10-20% slower than raw SQL, but worth it.

cgo Bridge to C++

DARTS tokenization is ~3x faster than pure Go hashmaps. cgo overhead: ~100ns per call. Batch operations amortize to <0.1% per token.

Layered Architecture

Handler → Service → DAO separates concerns, ensures testability, enables code reuse.

Design Philosophy

RAGFlow's internal Go module embodies several core principles:

Separation of Concerns

Each layer (Handler, Service, DAO) has a single responsibility. Handlers translate HTTP; Services enforce business rules; DAOs speak to the database. This modularity makes testing, refactoring, and onboarding easier.

Testability First

Services depend on interfaces, not concrete types. This enables mocking in unit tests. A SearchService test can use a MockDAO that returns hardcoded results, without touching a real database.

Goroutine Concurrency

Go's goroutines enable high concurrency with low memory overhead. A single Go process can handle thousands of concurrent requests. Compare this to Python's threading (GIL-limited) or Node.js's single-threaded event loop.

C++ for Performance-Critical Paths

Text analysis (tokenization, lemmatization) is called millions of times per day. A 3x speedup in C++ translates to massive cumulative savings. cgo overhead is negligible for batch operations.

Integration with Python API

RAGFlow's Python API (`api/` package) and Go backend (`internal/` package) coexist peacefully:

Scaling Considerations

Horizontal Scaling

Both Python and Go services are stateless. Deploy multiple instances behind a load balancer. PostgreSQL is the single source of truth.

Vertical Scaling

Go services scale better to more CPU cores (no GIL). A 16-core Go process achieves near-linear throughput scaling. Python needs process pools.

Vector Similarity Search Optimization

pgvector with IVFFlat indexes (Postgres extension) provides fast similarity search. For even larger datasets, consider Milvus or Weaviate vector databases.

Monitoring & Observability

In production, instrument the Go service with:

Common Challenges & Solutions

Challenge: Data Consistency Between Python & Go

Solution: Version fields. When updating a resource, increment a version number. Both services check the version before writing. Optimistic locking prevents race conditions.

Challenge: Slow LLM API Calls Block Search

Solution: Embedding cache with generous TTL (1 hour). 90% of queries reuse cached embeddings. For cache misses, set a short timeout (5s) and fall back to keyword search.

Challenge: Database Connection Pooling

Solution: Use pgbouncer in transaction mode. Go maintains a pool of connections. Python uses connection pooling too. Never open/close per request.

Performance at Scale

RAGFlow's internal module is designed to handle high-throughput workloads:

Throughput Benchmarks

Resource Utilization

Bottlenecks & Solutions

Deployment Architecture

A typical production deployment:

Load Balancer (Nginx)
    |
    +-- Python API (8 instances, port 5000)
    |   - User auth, model serving, LLM proxy
    |
    +-- Go Backend (8 instances, port 8080)
    |   - Search, chat, embeddings
    |
    +-- Database (PostgreSQL 14+)
    |   - pgvector extension for similarity search
    |
    +-- Vector Cache (Redis optional, in-process LRU default)
    |
    +-- Message Queue (Redis, optional, for async tasks)

Each service is independently scalable. Add more Go instances for search throughput; add more Python instances for LLM call capacity.

Further Reading