neuron-explorer
Qwen3.5‑4B 전용 조향형 뉴런 탐색기, 모든 MLP 뉴런을 밀어 결과를 보관·탐색
Michichusa (道草): 작은 웹을 위한 검색엔진, Go로 직접 구현한 크롤러·PageRank·전체 텍스트 검색
Michichusa (道草): a search engine for the small web, built from scratch in Go with its own crawler, PageRank and full-text search.
개발 도구Go · TypeScriptGitHub @Acollie온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Michichusa (道草): a search engine built from scratch with Go, Kubernetes and gRPC
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A production-grade distributed web search engine built in Go with five microservices, PostgreSQL persistence, and PageRank-based relevance ranking.
🌐 Live → search.collie.codes · 📊 Metrics → metrics.collie.codesREADME 전문 보기 ↗A production-grade distributed web search engine built in Go with five microservices, PostgreSQL persistence, and PageRank-based relevance ranking.
🌐 Live → search.collie.codes · 📊 Metrics → metrics.collie.codes
🔒 Spider Security Hardening (Latest)
Production-ready distributed web crawler with comprehensive security features.
Responsibilities:
Security & Protection:
Key Features:
Endpoints:
:9090 - GetSeenList (bidirectional stream):8080/health (liveness probe):8080/metrics (Prometheus)Acts as a data hub, deduplicating pages and managing the crawl queue.
Responsibilities:
SeenPages tableKey Features:
QUEUE_MAX_SIZE) to prevent unbounded DB bloatEndpoints:
:8080/health (liveness probe)Computes PageRank scores using graph analysis.
Algorithm:
Key Features:
is_latest flag for serving current rankingsEndpoints:
:8080/health (liveness probe)Provides full-text search with PageRank relevance boosting.
Search Formula:
Final Score = (TextRelevance × 0.3) + (PageRank × 0.7)
Features:
Endpoints:
:9091 - SearchPages:8080/health (readiness probe with DB validation)User-facing search interface. Single-page app (React 18 + TypeScript + Vite) served by a small Go server that proxies queries to the Searcher over gRPC.
Features:
Endpoints:
:3000SeenPages
id (BIGSERIAL PRIMARY KEY)url (VARCHAR UNIQUE)title (VARCHAR)body (TEXT)links (TEXT[])prominence (FLOAT8) - PageRank scorecreated_at (TIMESTAMP)Queue
id (BIGSERIAL PRIMARY KEY)url (VARCHAR UNIQUE)PageRankResults
id (BIGSERIAL PRIMARY KEY)page_id (BIGINT FK)score (FLOAT8)sweep_id (VARCHAR)is_latest (BOOLEAN)created_at (TIMESTAMP)The project uses Protocol Buffers for service definitions and data types:
protos/
├── service/ # Service definitions
│ └── spider.proto # Spider and Searcher service APIs
└── types/ # Shared message types
└── site.proto # Common data structures
Spider Service (protos/service/spider.proto)
GetSeenList: Stream of seen pages from the spiderGetHealth: Health check endpointSearcher Service (protos/service/spider.proto)
SearchPages: Query pages with full-text searchThe Makefile provides commands to generate Go code from all .proto files:
# Generate Go code from all proto files
make proto_gen
# List all proto files that will be generated
make proto_list
# Clean generated proto files
make proto_clean
Generated files are placed in pkg/generated/ with the following structure:
pkg/generated/
├── service/
│ └── spider/
│ ├── spider.pb.go # Message types
│ └── spider_grpc.pb.go # gRPC service code
└── types/
└── site/
└── site.pb.go # Common types
To add new service definitions:
Create your .proto file in the appropriate directory:
protos/service/ for service definitionsprotos/types/ for shared message typesEnsure you specify the correct package and go_package:
syntax = "proto3";
package service;
option go_package = "service/your_service";
Run make proto_gen to generate Go code
Import the generated code in your service:
import (
pb "webcrawler/pkg/generated/service/your_service"
)
The Makefile will automatically find and generate all .proto files in the protos/ directory tree
The project uses Mockery to automatically generate mocks for all gRPC service interfaces. These mocks are essential for unit testing services without requiring actual gRPC connections.
Mocks are generated for both client and server interfaces:
# Generate mocks for all gRPC services
make mock_gen
# List which interfaces will be mocked
make mock_list
# Clean generated mocks
make mock_clean
# Generate both protos and mocks in one command
make gen
# Clean both protos and mocks
make clean
Generated mocks are placed in:
pkg/mocks/
└── service/
└── spider/
├── SpiderClient.go
├── SpiderServer.go
├── SearcherClient.go
└── SearcherServer.go
Mocks are generated with the expecter pattern for type-safe test assertions:
import (
"testing"
"github.com/stretchr/testify/mock"
mockspider "webcrawler/pkg/mocks/service/spider"
pb "webcrawler/pkg/generated/service/spider"
)
func TestSearchService(t *testing.T) {
// Create mock client
mockClient := mockspider.NewMockSearcherClient(t)
// Setup expectations using the expecter pattern
mockClient.EXPECT().
SearchPages(mock.Anything, mock.Anything).
Return(&pb.SearchResponse{
Pages: []*pb.Page{{Url: "https://example.com"}},
}, nil).
Once()
// Use the mock in your test
resp, err := mockClient.SearchPages(context.Background(), &pb.SearchRequest{
Query: "test",
Limit: 10,
})
// Assertions
assert.NoError(t, err)
assert.Len(t, resp.Pages, 1)
// Verify all expectations were met
mockClient.AssertExpectations(t)
}
Install mockery if not already installed:
go install github.com/vektra/mockery/v2@latest
The Makefile will automatically check if mockery is installed and provide installation instructions if needed.
Mock generation is configured in .mockery.yaml. To add mocks for new services:
make proto_gen.mockery.yamlmake mock_genExample configuration:
packages:
webcrawler/pkg/generated/service/your_service:
interfaces:
YourServiceClient:
config:
dir: "pkg/mocks/service/your_service"
YourServiceServer:
config:
dir: "pkg/mocks/service/your_service"
Prerequisites:
cmd/frontend/ui)Environment Variables:
export DB_USER=postgres
export DB_PASSWORD=yourpassword
export DB_HOST=localhost
export DB_NAME=databaseName
# Build all services
go build ./cmd/spider
go build ./cmd/conductor
go build ./cmd/cartographer
go build ./cmd/searcher
# Or use the Makefile
make buildSpider
make buildConductor
Spider:
export DB_HOST=localhost
go run cmd/spider/main.go
Conductor:
go run cmd/conductor/main.go
Cartographer:
go run cmd/cartographer/main.go
Searcher:
go run cmd/searcher/main.go
Frontend: (build the UI once, then run the Go server)
cd cmd/frontend/ui && npm install && npm run build && cd -
go run cmd/frontend/main.go # serves on :3000
The project has comprehensive test coverage with unit tests, integration tests, and mock-based testing.
# Run all tests
go test ./...
# Run specific package tests
go test ./cmd/spider/handler/...
# Run tests with coverage
go test -cover ./...
# Run tests with verbose output
go test -v ./...
Test Coverage:
# Run linter (uses revive)
make lint
# Or directly
go run github.com/mgechev/revive@latest -config revive.toml ./...
Live at search.collie.codes, running on a Hetzner Cloud k3s cluster provisioned with Terraform (terraform/).
# Build and push all service images (linux/amd64) to the registry
make build-and-push-all
Images are pushed to Amazon ECR; the registry URL is substituted for ECR_REGISTRY_PLACEHOLDER in the manifests at deploy time.
# Apply all manifests
kubectl apply -f deployments/
Cluster & infra:
cx33 worker)search.collie.codes, metrics.collie.codes)Per-service features:
Cartographer runs as a Kubernetes CronJob every 6 hours:
apiVersion: batch/v1
kind: CronJob
metadata:
name: cartographer
spec:
schedule: "0 */6 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: cartographer
image: cartographer:latest
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: username
restartPolicy: OnFailure
| Service | Throughput | Bottleneck |
|---|---|---|
| Spider | 10 concurrent crawls | Network latency |
| Conductor | 1000s msgs/sec | PostgreSQL write throughput |
| Cartographer | 100K pages/sweep | Random sampling |
| Searcher | <100ms p95 | PostgreSQL query |
| Service | Memory | Notes |
|---|---|---|
| Spider | 50-100 MB | SQLite cache + security components + goroutines |
| Conductor | 100-200 MB | Message buffering |
| Cartographer | 500 MB | Graph computation |
| Searcher | 100 MB | Query cache |
The Spider service exposes comprehensive Prometheus metrics:
| Metric | Type | Description |
|---|---|---|
spider_pages_fetched_total | Counter | Successfully crawled pages by domain |
spider_pages_failed_total | Counter | Failed pages by reason (invalid_url, cycle, timeout, etc.) |
spider_robots_blocked_total | Counter | Pages blocked by robots.txt by domain |
spider_links_extracted_total | Counter | Links discovered by domain |
spider_queue_depth | Gauge | Current queue size |
spider_active_crawls | Gauge | Currently active crawl goroutines |
spider_crawl_duration_seconds | Histogram | Crawl latency by domain |
Connection Pooling:
Indices:
CREATE INDEX idx_pages_url ON SeenPages(url);
CREATE INDEX idx_pagerank_is_latest ON PageRankResults(is_latest);
CREATE INDEX idx_pagerank_scores ON PageRankResults(score DESC);
CREATE INDEX idx_queue_url ON Queue(url);
-- Full-text search index
CREATE INDEX idx_pages_search ON SeenPages USING GIN(search_vector);
SSRF Protection:
Rate Limiting & Respect:
Resource Protection:
db.ExecContext(ctx, "INSERT INTO Queue (url) VALUES ($1)", url)/metricsAll long-running services run behind a HorizontalPodAutoscaler (CPU 70% / memory 80% targets):
| Service | Min | Max | Notes |
|---|---|---|---|
| Spider | 1 | 5 | Each instance keeps an independent SQLite cache; backpressure protects downstream |
| Conductor | 1 | 8 | Deduplicates and manages the crawl queue |
| Searcher | 1 | 15 | Read-only query traffic — scales the widest |
| Frontend | 1 | 10 | Stateless UI + gRPC proxy |
Cartographer is a scheduled batch job, not autoscaled — it samples pages (doesn't load the full graph) and could parallelize sweep computation.
Future scaling headroom:
Database: Consider PostgreSQL upgrades for write throughput
max_connections and shared_buffers
Source D2 files: assets/diagrams/
"database connection refused"
psql -U postgres -h localhost"gRPC: error reading from client"
"PageRank computation taking too long"
EXPLAIN ANALYZE on queriesTests failing with "testcontainers"
git checkout -b feature/your-featuremake lint && go test ./...MIT License - See LICENSE file for details.