A social RSS reader built on the AT Protocol. glean.at
glean atproto atmosphere rss feed social app
14
fork

Configure Feed

Select the types of activity you want to include in your feed.

Renaming and cleanup

+132 -132
+78 -78
internal/cluster/jaccard_test.go
··· 15 15 "gotest.tools/v3/assert" 16 16 ) 17 17 18 - func setupClusterTestDB(t *testing.T) *db.Databases { 18 + func setupClusterTestDB(t *testing.T) *db.Store { 19 19 t.Helper() 20 20 vec.Auto() 21 21 f, err := os.CreateTemp("", "glean-cluster-test-*.db") ··· 35 35 _ = os.Remove(path + "_recs-wal") 36 36 }) 37 37 38 - dbs, err := db.OpenAll(path) 38 + dbs, err := db.Open(path) 39 39 assert.NilError(t, err) 40 40 t.Cleanup(func() { _ = dbs.Close() }) 41 41 assert.NilError(t, dbs.InitVecTables(8)) 42 42 return dbs 43 43 } 44 44 45 - func seedClusterData(t *testing.T, ctx context.Context, dbs *db.Databases) { 45 + func seedClusterData(t *testing.T, ctx context.Context, dbs *db.Store) { 46 46 t.Helper() 47 47 48 48 users := []string{"did:test:alice", "did:test:bob", "did:test:carol", "did:test:dave"} 49 49 for _, did := range users { 50 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, did) 50 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, did) 51 51 assert.NilError(t, err) 52 52 } 53 53 ··· 59 59 {"https://e.com/feed", "Feed E"}, 60 60 } 61 61 for _, f := range feeds { 62 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type, subscriber_count) VALUES (?, ?, ?, '', 'rss', 2)`, f.url, f.title, f.url) 62 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type, subscriber_count) VALUES (?, ?, ?, '', 'rss', 2)`, f.url, f.title, f.url) 63 63 assert.NilError(t, err) 64 64 } 65 65 ··· 75 75 {"did:test:carol", "https://c.com/feed"}, 76 76 } 77 77 for _, s := range subs { 78 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, s.user, s.feed) 78 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, s.user, s.feed) 79 79 assert.NilError(t, err) 80 80 } 81 81 } 82 82 83 - func seedFollowData(t *testing.T, ctx context.Context, dbs *db.Databases) { 83 + func seedFollowData(t *testing.T, ctx context.Context, dbs *db.Store) { 84 84 t.Helper() 85 85 follows := []struct{ user, target string }{ 86 86 {"did:test:alice", "did:test:bob"}, ··· 88 88 {"did:test:carol", "did:test:dave"}, 89 89 } 90 90 for _, f := range follows { 91 - _, err := dbs.DB().ExecContext(ctx, `INSERT OR IGNORE INTO follows (user_did, target_did) VALUES (?, ?)`, f.user, f.target) 91 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT OR IGNORE INTO follows (user_did, target_did) VALUES (?, ?)`, f.user, f.target) 92 92 assert.NilError(t, err) 93 93 } 94 94 } 95 95 96 - func newTestEngine(dbs *db.Databases) *Engine { 97 - return NewEngine(dbs.DB(), NewMockEmbedder(8), slog.Default()) 96 + func newTestEngine(dbs *db.Store) *Engine { 97 + return NewEngine(dbs.SQLDB(), NewMockEmbedder(8), slog.Default()) 98 98 } 99 99 100 100 func TestComputeFeedSimilarity(t *testing.T) { ··· 107 107 assert.NilError(t, err) 108 108 109 109 var count int 110 - err = dbs.DB().QueryRowContext(ctx, `SELECT COUNT(*) FROM recs.feed_similarity`).Scan(&count) 110 + err = dbs.SQLDB().QueryRowContext(ctx, `SELECT COUNT(*) FROM recs.feed_similarity`).Scan(&count) 111 111 assert.NilError(t, err) 112 112 assert.Assert(t, count > 0, "expected feed similarity pairs") 113 113 } ··· 122 122 assert.NilError(t, err) 123 123 124 124 var count int 125 - err = dbs.DB().QueryRowContext(ctx, `SELECT COUNT(*) FROM recs.user_similarity`).Scan(&count) 125 + err = dbs.SQLDB().QueryRowContext(ctx, `SELECT COUNT(*) FROM recs.user_similarity`).Scan(&count) 126 126 assert.NilError(t, err) 127 127 assert.Assert(t, count > 0, "expected user similarity pairs") 128 128 } ··· 224 224 assert.NilError(t, engine.RecordImpressions(ctx, "did:test:alice", impressions)) 225 225 226 226 var count int 227 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, 227 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, 228 228 `SELECT COUNT(*) FROM main.recommendation_impressions WHERE user_did = 'did:test:alice'`).Scan(&count)) 229 229 assert.Equal(t, count, 2) 230 230 231 231 assert.NilError(t, engine.RecordImpressions(ctx, "did:test:alice", impressions)) 232 232 233 233 var shownCount int 234 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, 234 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, 235 235 `SELECT shown_count FROM main.recommendation_impressions WHERE user_did = 'did:test:alice' AND target_id = 'https://a.com/feed'`).Scan(&shownCount)) 236 236 assert.Equal(t, shownCount, 2, "shown_count should increment on repeated impression") 237 237 } ··· 249 249 assert.NilError(t, engine.MarkImpressionActed(ctx, "did:test:alice", "feed", "https://a.com/feed")) 250 250 251 251 var acted bool 252 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, 252 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, 253 253 `SELECT acted FROM main.recommendation_impressions WHERE user_did = 'did:test:alice' AND target_id = 'https://a.com/feed'`).Scan(&acted)) 254 254 assert.Assert(t, acted, "impression should be marked as acted") 255 255 } ··· 264 264 assert.NilError(t, engine.ComputeFollowDistances(ctx)) 265 265 266 266 var d1, d2, d3 int 267 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, 267 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, 268 268 `SELECT COUNT(*) FROM recs.follow_distances WHERE distance = 1`).Scan(&d1)) 269 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, 269 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, 270 270 `SELECT COUNT(*) FROM recs.follow_distances WHERE distance = 2`).Scan(&d2)) 271 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, 271 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, 272 272 `SELECT COUNT(*) FROM recs.follow_distances WHERE distance = 3`).Scan(&d3)) 273 273 assert.Assert(t, d1 >= 3, "expected at least 3 direct follow distances") 274 274 assert.Assert(t, d2 >= 1, "expected at least 1 two-hop distance (alice -> bob -> carol)") 275 275 assert.Assert(t, d3 >= 1, "expected at least 1 three-hop distance (alice -> bob -> carol -> dave)") 276 276 277 277 var exists int 278 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, 278 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, 279 279 `SELECT COUNT(*) FROM recs.follow_distances WHERE user_a = 'did:test:alice' AND user_b = 'did:test:carol'`).Scan(&exists)) 280 280 assert.Assert(t, exists == 1, "alice should reach carol") 281 281 282 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, 282 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, 283 283 `SELECT COUNT(*) FROM recs.follow_distances WHERE user_a = 'did:test:alice' AND user_b = 'did:test:dave'`).Scan(&exists)) 284 284 assert.Assert(t, exists == 1, "alice should reach dave via 3 hops") 285 285 } ··· 300 300 assert.NilError(t, engine.WriteFollowDistances(ctx, distances)) 301 301 302 302 var count int 303 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, `SELECT COUNT(*) FROM recs.follow_distances`).Scan(&count)) 303 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, `SELECT COUNT(*) FROM recs.follow_distances`).Scan(&count)) 304 304 assert.Equal(t, count, len(distances)) 305 305 } 306 306 ··· 311 311 312 312 engine := newTestEngine(dbs) 313 313 314 - _, err := dbs.DB().ExecContext(ctx, ` 314 + _, err := dbs.SQLDB().ExecContext(ctx, ` 315 315 INSERT INTO main.recommendation_impressions (user_did, target_type, target_id, first_shown_at, last_shown_at, shown_count, acted) 316 316 VALUES ('did:test:alice', 'feed', 'https://stale.com/feed', datetime('now', '-6 days'), datetime('now'), 6, 0) 317 317 `) ··· 331 331 332 332 engine := newTestEngine(dbs) 333 333 334 - _, err := dbs.DB().ExecContext(ctx, ` 334 + _, err := dbs.SQLDB().ExecContext(ctx, ` 335 335 INSERT INTO main.recommendation_impressions (user_did, target_type, target_id, first_shown_at, last_shown_at, shown_count, acted) 336 336 VALUES ('did:test:alice', 'feed', 'https://recent.com/feed', datetime('now'), datetime('now'), 5, 0) 337 337 `) ··· 351 351 352 352 engine := newTestEngine(dbs) 353 353 354 - _, err := dbs.DB().ExecContext(ctx, ` 354 + _, err := dbs.SQLDB().ExecContext(ctx, ` 355 355 INSERT INTO main.recommendation_impressions (user_did, target_type, target_id, first_shown_at, last_shown_at, shown_count, acted) 356 356 VALUES ('did:test:alice', 'feed', 'https://acted.com/feed', datetime('now', '-6 days'), datetime('now'), 6, 1) 357 357 `) ··· 427 427 428 428 engine := newTestEngine(dbs) 429 429 430 - _, err := dbs.DB().ExecContext(ctx, ` 430 + _, err := dbs.SQLDB().ExecContext(ctx, ` 431 431 INSERT INTO main.recommendation_impressions (user_did, target_type, target_id, first_shown_at, last_shown_at, shown_count, acted) 432 432 VALUES ('did:test:alice', 'feed', 'https://a.com/feed', datetime('now'), datetime('now'), 1, 1) 433 433 `) 434 434 assert.NilError(t, err) 435 435 for i := range minActionsTune { 436 - _, err = dbs.DB().ExecContext(ctx, ` 436 + _, err = dbs.SQLDB().ExecContext(ctx, ` 437 437 INSERT INTO main.recommendation_impressions (user_did, target_type, target_id, first_shown_at, last_shown_at, shown_count, acted) 438 438 VALUES ('did:test:alice', 'feed', ?, datetime('now'), datetime('now'), 1, 1) 439 439 `, fmt.Sprintf("https://%d.com/feed", i)) ··· 455 455 engine := newTestEngine(dbs) 456 456 assert.NilError(t, engine.ComputeFollowDistances(ctx)) 457 457 458 - _, err := dbs.DB().ExecContext(ctx, `UPDATE articles.feeds SET subscriber_count = 2 WHERE feed_url = 'https://a.com/feed'`) 458 + _, err := dbs.SQLDB().ExecContext(ctx, `UPDATE articles.feeds SET subscriber_count = 2 WHERE feed_url = 'https://a.com/feed'`) 459 459 assert.NilError(t, err) 460 - _, err = dbs.DB().ExecContext(ctx, `UPDATE articles.feeds SET subscriber_count = 2 WHERE feed_url = 'https://b.com/feed'`) 460 + _, err = dbs.SQLDB().ExecContext(ctx, `UPDATE articles.feeds SET subscriber_count = 2 WHERE feed_url = 'https://b.com/feed'`) 461 461 assert.NilError(t, err) 462 462 463 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:newuser") 463 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:newuser") 464 464 assert.NilError(t, err) 465 465 466 466 recs, err := engine.ColdStartRecommendations(ctx, "did:test:newuser", 10) ··· 505 505 assert.NilError(t, engine.DismissArticle(ctx, "did:test:alice", "https://a.com/article1", "not_interested")) 506 506 507 507 var count int 508 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, 508 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, 509 509 `SELECT COUNT(*) FROM main.dismissed_recommendations WHERE user_did = 'did:test:alice' AND target_type = 'article'`).Scan(&count)) 510 510 assert.Equal(t, count, 1) 511 511 } ··· 519 519 assert.NilError(t, engine.ComputeSignalProfiles(ctx)) 520 520 521 521 var count int 522 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, `SELECT COUNT(*) FROM recs.user_signal_profiles`).Scan(&count)) 522 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, `SELECT COUNT(*) FROM recs.user_signal_profiles`).Scan(&count)) 523 523 assert.Assert(t, count >= 3, "expected signal profiles for all users") 524 524 } 525 525 ··· 534 534 assert.NilError(t, engine.DismissFeed(ctx, "did:test:alice", "https://a.com/feed", "reason2")) 535 535 536 536 var count int 537 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, 537 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, 538 538 `SELECT COUNT(*) FROM main.dismissed_recommendations WHERE user_did = 'did:test:alice' AND target_type = 'feed'`).Scan(&count)) 539 539 assert.Equal(t, count, 1, "duplicate dismiss should not create extra rows") 540 540 } ··· 543 543 ctx := context.Background() 544 544 dbs := setupClusterTestDB(t) 545 545 546 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:alice") 546 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:alice") 547 547 assert.NilError(t, err) 548 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:bob") 548 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:bob") 549 549 assert.NilError(t, err) 550 550 551 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type, subscriber_count) VALUES (?, ?, ?, ?, 'rss', 2)`, 551 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type, subscriber_count) VALUES (?, ?, ?, ?, 'rss', 2)`, 552 552 "https://go.com/feed", "Go Blog", "https://go.com", "programming language golang software development") 553 553 assert.NilError(t, err) 554 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type, subscriber_count) VALUES (?, ?, ?, ?, 'rss', 2)`, 554 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type, subscriber_count) VALUES (?, ?, ?, ?, 'rss', 2)`, 555 555 "https://rust.com/feed", "Rust Blog", "https://rust.com", "programming language rust software development") 556 556 assert.NilError(t, err) 557 557 558 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, CURRENT_TIMESTAMP)`, "did:test:alice", "https://go.com/feed") 558 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, CURRENT_TIMESTAMP)`, "did:test:alice", "https://go.com/feed") 559 559 assert.NilError(t, err) 560 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, CURRENT_TIMESTAMP)`, "did:test:alice", "https://rust.com/feed") 560 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, CURRENT_TIMESTAMP)`, "did:test:alice", "https://rust.com/feed") 561 561 assert.NilError(t, err) 562 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, CURRENT_TIMESTAMP)`, "did:test:bob", "https://go.com/feed") 562 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, CURRENT_TIMESTAMP)`, "did:test:bob", "https://go.com/feed") 563 563 assert.NilError(t, err) 564 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, CURRENT_TIMESTAMP)`, "did:test:bob", "https://rust.com/feed") 564 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, CURRENT_TIMESTAMP)`, "did:test:bob", "https://rust.com/feed") 565 565 assert.NilError(t, err) 566 566 567 567 engine := newTestEngine(dbs) ··· 570 570 assert.NilError(t, engine.ComputeFeedSimilarity(ctx)) 571 571 572 572 var jaccard float64 573 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, 573 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, 574 574 `SELECT jaccard FROM recs.feed_similarity WHERE feed_a = ? AND feed_b = ?`, 575 575 "https://go.com/feed", "https://rust.com/feed").Scan(&jaccard)) 576 576 assert.Assert(t, jaccard > 1.0, "embedding cosine similarity should boost feed similarity above pure Jaccard") ··· 610 610 ctx := context.Background() 611 611 dbs := setupClusterTestDB(t) 612 612 613 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, feed_type, subscriber_count) VALUES (?, ?, ?, 'rss', 1)`, 613 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, feed_type, subscriber_count) VALUES (?, ?, ?, 'rss', 1)`, 614 614 "https://tech.com/feed", "Tech Feed", "https://tech.com") 615 615 assert.NilError(t, err) 616 616 617 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, summary, url) VALUES (?, ?, ?, ?, ?)`, 617 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, summary, url) VALUES (?, ?, ?, ?, ?)`, 618 618 "https://tech.com/feed", "1", "golang programming language tutorial", "learn the go programming language for backend development", "https://tech.com/go") 619 619 assert.NilError(t, err) 620 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, summary, url) VALUES (?, ?, ?, ?, ?)`, 620 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, summary, url) VALUES (?, ?, ?, ?, ?)`, 621 621 "https://tech.com/feed", "2", "rust programming language guide", "learn the rust programming language for systems development", "https://tech.com/rust") 622 622 assert.NilError(t, err) 623 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, summary, url) VALUES (?, ?, ?, ?, ?)`, 623 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, summary, url) VALUES (?, ?, ?, ?, ?)`, 624 624 "https://tech.com/feed", "3", "cooking recipes for dinner", "easy dinner recipes for the whole family", "https://tech.com/cook") 625 625 assert.NilError(t, err) 626 626 ··· 629 629 assert.NilError(t, engine.ComputeArticleEmbeddings(ctx)) 630 630 631 631 var count int 632 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, `SELECT COUNT(*) FROM recs.article_embeddings`).Scan(&count)) 632 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, `SELECT COUNT(*) FROM recs.article_embeddings`).Scan(&count)) 633 633 assert.Equal(t, count, 3, "expected 3 article embeddings") 634 634 } 635 635 ··· 637 637 ctx := context.Background() 638 638 dbs := setupClusterTestDB(t) 639 639 640 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:alice") 640 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:alice") 641 641 assert.NilError(t, err) 642 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:bob") 642 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:bob") 643 643 assert.NilError(t, err) 644 644 645 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, feed_type, subscriber_count) VALUES (?, ?, ?, 'rss', 2)`, 645 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, feed_type, subscriber_count) VALUES (?, ?, ?, 'rss', 2)`, 646 646 "https://tech.com/feed", "Tech Feed", "https://tech.com") 647 647 assert.NilError(t, err) 648 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, feed_type, subscriber_count) VALUES (?, ?, ?, 'rss', 2)`, 648 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, feed_type, subscriber_count) VALUES (?, ?, ?, 'rss', 2)`, 649 649 "https://dev.com/feed", "Dev Feed", "https://dev.com") 650 650 assert.NilError(t, err) 651 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, feed_type, subscriber_count) VALUES (?, ?, ?, 'rss', 2)`, 651 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, feed_type, subscriber_count) VALUES (?, ?, ?, 'rss', 2)`, 652 652 "https://shared.com/feed", "Shared Feed", "https://shared.com") 653 653 assert.NilError(t, err) 654 654 655 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, "did:test:alice", "https://tech.com/feed") 655 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, "did:test:alice", "https://tech.com/feed") 656 656 assert.NilError(t, err) 657 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, "did:test:alice", "https://shared.com/feed") 657 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, "did:test:alice", "https://shared.com/feed") 658 658 assert.NilError(t, err) 659 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, "did:test:bob", "https://dev.com/feed") 659 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, "did:test:bob", "https://dev.com/feed") 660 660 assert.NilError(t, err) 661 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, "did:test:bob", "https://shared.com/feed") 661 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, "did:test:bob", "https://shared.com/feed") 662 662 assert.NilError(t, err) 663 663 664 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, summary, url, published) VALUES (?, ?, ?, ?, ?, datetime('now'))`, 664 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, summary, url, published) VALUES (?, ?, ?, ?, ?, datetime('now'))`, 665 665 "https://tech.com/feed", "1", "golang programming tutorial", "learn go programming", "https://tech.com/go") 666 666 assert.NilError(t, err) 667 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, summary, url, published) VALUES (?, ?, ?, ?, ?, datetime('now'))`, 667 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, summary, url, published) VALUES (?, ?, ?, ?, ?, datetime('now'))`, 668 668 "https://dev.com/feed", "2", "rust programming tutorial", "learn rust programming", "https://dev.com/rust") 669 669 assert.NilError(t, err) 670 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, summary, url, published) VALUES (?, ?, ?, ?, ?, datetime('now'))`, 670 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, summary, url, published) VALUES (?, ?, ?, ?, ?, datetime('now'))`, 671 671 "https://dev.com/feed", "3", "cooking dinner recipes", "easy dinner recipes", "https://dev.com/cook") 672 672 assert.NilError(t, err) 673 673 674 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.likes (uri, author_did, feed_url, article_url, created_at) VALUES (?, ?, ?, ?, datetime('now'))`, 674 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.likes (uri, author_did, feed_url, article_url, created_at) VALUES (?, ?, ?, ?, datetime('now'))`, 675 675 "at://alice/like/1", "did:test:alice", "https://tech.com/feed", "https://tech.com/go") 676 676 assert.NilError(t, err) 677 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.likes (uri, author_did, feed_url, article_url, created_at) VALUES (?, ?, ?, ?, datetime('now'))`, 677 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.likes (uri, author_did, feed_url, article_url, created_at) VALUES (?, ?, ?, ?, datetime('now'))`, 678 678 "at://bob/like/1", "did:test:bob", "https://dev.com/feed", "https://dev.com/rust") 679 679 assert.NilError(t, err) 680 680 ··· 693 693 ctx := context.Background() 694 694 dbs := setupClusterTestDB(t) 695 695 696 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type) VALUES (?, ?, ?, ?, 'rss')`, 696 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type) VALUES (?, ?, ?, ?, 'rss')`, 697 697 "https://go.com/feed", "Go Blog", "https://go.com", "old description") 698 698 assert.NilError(t, err) 699 699 ··· 702 702 assert.NilError(t, engine.ComputeFeedEmbeddings(ctx)) 703 703 704 704 var count int 705 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, `SELECT COUNT(*) FROM recs.feed_embeddings`).Scan(&count)) 705 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, `SELECT COUNT(*) FROM recs.feed_embeddings`).Scan(&count)) 706 706 assert.Equal(t, count, 1) 707 707 708 - _, err = dbs.DB().ExecContext(ctx, `UPDATE articles.feeds SET description = 'new description' WHERE feed_url = 'https://go.com/feed'`) 708 + _, err = dbs.SQLDB().ExecContext(ctx, `UPDATE articles.feeds SET description = 'new description' WHERE feed_url = 'https://go.com/feed'`) 709 709 assert.NilError(t, err) 710 710 711 711 assert.NilError(t, engine.ComputeFeedEmbeddings(ctx)) 712 712 713 713 var sourceText string 714 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, `SELECT source_text FROM recs.feed_embedding_meta WHERE feed_url = 'https://go.com/feed'`).Scan(&sourceText)) 714 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, `SELECT source_text FROM recs.feed_embedding_meta WHERE feed_url = 'https://go.com/feed'`).Scan(&sourceText)) 715 715 assert.Assert(t, sourceText == "Go Blog new description", "embedding should be recomputed when description changes, got: %s", sourceText) 716 716 } 717 717 ··· 719 719 ctx := context.Background() 720 720 dbs := setupClusterTestDB(t) 721 721 722 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:alice") 722 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:alice") 723 723 assert.NilError(t, err) 724 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:bob") 724 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:bob") 725 725 assert.NilError(t, err) 726 726 727 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, feed_type, subscriber_count) VALUES (?, ?, ?, 'rss', 2)`, 727 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, feed_type, subscriber_count) VALUES (?, ?, ?, 'rss', 2)`, 728 728 "https://a.com/feed", "Feed A", "https://a.com") 729 729 assert.NilError(t, err) 730 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, feed_type, subscriber_count) VALUES (?, ?, ?, 'rss', 2)`, 730 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, feed_type, subscriber_count) VALUES (?, ?, ?, 'rss', 2)`, 731 731 "https://b.com/feed", "Feed B", "https://b.com") 732 732 assert.NilError(t, err) 733 733 734 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, datetime('now', '-60 days'))`, 734 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, datetime('now', '-60 days'))`, 735 735 "did:test:alice", "https://a.com/feed") 736 736 assert.NilError(t, err) 737 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, datetime('now'))`, 737 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, datetime('now'))`, 738 738 "did:test:bob", "https://a.com/feed") 739 739 assert.NilError(t, err) 740 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, datetime('now'))`, 740 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, datetime('now'))`, 741 741 "did:test:alice", "https://b.com/feed") 742 742 assert.NilError(t, err) 743 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, datetime('now'))`, 743 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url, added_at) VALUES (?, ?, datetime('now'))`, 744 744 "did:test:bob", "https://b.com/feed") 745 745 assert.NilError(t, err) 746 746 ··· 748 748 assert.NilError(t, engine.ComputeFeedSimilarity(ctx)) 749 749 750 750 var jaccard float64 751 - assert.NilError(t, dbs.DB().QueryRowContext(ctx, 751 + assert.NilError(t, dbs.SQLDB().QueryRowContext(ctx, 752 752 `SELECT jaccard FROM recs.feed_similarity WHERE feed_a = 'https://a.com/feed' AND feed_b = 'https://b.com/feed'`).Scan(&jaccard)) 753 753 assert.Assert(t, jaccard > 0, "time-decayed feed similarity should be positive") 754 754 assert.Assert(t, jaccard < 1.0, "time decay should reduce similarity below raw Jaccard") ··· 758 758 ctx := context.Background() 759 759 dbs := setupClusterTestDB(t) 760 760 761 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:newuser") 761 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, "did:test:newuser") 762 762 assert.NilError(t, err) 763 763 764 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type, subscriber_count) VALUES (?, ?, ?, ?, 'rss', 2)`, 764 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type, subscriber_count) VALUES (?, ?, ?, ?, 'rss', 2)`, 765 765 "https://go.com/feed", "Go Blog", "https://go.com", "golang programming language") 766 766 assert.NilError(t, err) 767 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type, subscriber_count) VALUES (?, ?, ?, ?, 'rss', 2)`, 767 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type, subscriber_count) VALUES (?, ?, ?, ?, 'rss', 2)`, 768 768 "https://godev.com/feed", "Go Dev", "https://godev.com", "golang development tutorials") 769 769 assert.NilError(t, err) 770 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type, subscriber_count) VALUES (?, ?, ?, ?, 'rss', 2)`, 770 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title, site_url, description, feed_type, subscriber_count) VALUES (?, ?, ?, ?, 'rss', 2)`, 771 771 "https://cooking.com/feed", "Cooking", "https://cooking.com", "recipes for dinner") 772 772 assert.NilError(t, err) 773 773 774 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, 774 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, 775 775 "did:test:newuser", "https://go.com/feed") 776 776 assert.NilError(t, err) 777 777
+1 -1
internal/db/article.go
··· 51 51 ReadAt sql.NullTime 52 52 } 53 53 54 - func (s *ArticleStore) UpsertArticlesBatch(ctx context.Context, articles []feed.Article) error { 54 + func (s *ArticleStore) BatchUpsertArticles(ctx context.Context, articles []feed.Article) error { 55 55 if len(articles) == 0 { 56 56 return nil 57 57 }
+15 -15
internal/db/article_test.go
··· 9 9 "gotest.tools/v3/assert" 10 10 ) 11 11 12 - func setupTestDB(t *testing.T) *Databases { 12 + func setupTestDB(t *testing.T) *Store { 13 13 t.Helper() 14 14 f, err := os.CreateTemp("", "glean-test-*.db") 15 15 assert.NilError(t, err) ··· 21 21 } 22 22 }) 23 23 24 - dbs, err := OpenAll(path) 24 + dbs, err := Open(path) 25 25 assert.NilError(t, err) 26 26 t.Cleanup(func() { _ = dbs.Close() }) 27 27 return dbs 28 28 } 29 29 30 - func seedArticleReadState(t *testing.T, ctx context.Context, dbs *Databases) (userDID string, feedURL string, readArticleID, unreadArticleID int64) { 30 + func seedArticleReadState(t *testing.T, ctx context.Context, dbs *Store) (userDID string, feedURL string, readArticleID, unreadArticleID int64) { 31 31 t.Helper() 32 32 33 33 userDID = "did:test:user1" 34 34 feedURL = "https://example.com/feed.xml" 35 35 36 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, userDID) 36 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, userDID) 37 37 assert.NilError(t, err) 38 38 39 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title) VALUES (?, ?)`, feedURL, "Test Feed") 39 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title) VALUES (?, ?)`, feedURL, "Test Feed") 40 40 assert.NilError(t, err) 41 41 42 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, userDID, feedURL) 42 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, userDID, feedURL) 43 43 assert.NilError(t, err) 44 44 45 - res, err := dbs.DB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, url) VALUES (?, ?, ?, ?)`, 45 + res, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, url) VALUES (?, ?, ?, ?)`, 46 46 feedURL, "guid-read", "Read Article", "https://example.com/read") 47 47 assert.NilError(t, err) 48 48 readArticleID, _ = res.LastInsertId() 49 49 50 - res, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, url) VALUES (?, ?, ?, ?)`, 50 + res, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.articles (feed_url, guid, title, url) VALUES (?, ?, ?, ?)`, 51 51 feedURL, "guid-unread", "Unread Article", "https://example.com/unread") 52 52 assert.NilError(t, err) 53 53 unreadArticleID, _ = res.LastInsertId() ··· 190 190 assert.Assert(t, !article.FullContent.Valid) 191 191 } 192 192 193 - func seedSearchData(t *testing.T, ctx context.Context, dbs *Databases) (userDID, feedURL string) { 193 + func seedSearchData(t *testing.T, ctx context.Context, dbs *Store) (userDID, feedURL string) { 194 194 t.Helper() 195 195 196 196 userDID = "did:test:searcher" 197 197 feedURL = "https://search.example.com/feed.xml" 198 198 199 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, userDID) 199 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, userDID) 200 200 assert.NilError(t, err) 201 201 202 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title) VALUES (?, ?)`, feedURL, "Tech Blog") 202 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title) VALUES (?, ?)`, feedURL, "Tech Blog") 203 203 assert.NilError(t, err) 204 204 205 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, userDID, feedURL) 205 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.subscriptions (user_did, feed_url) VALUES (?, ?)`, userDID, feedURL) 206 206 assert.NilError(t, err) 207 207 208 208 articles := []struct { ··· 213 213 {"g3", "Python Data Science", "NumPy and Pandas tutorial", "Python is popular for data analysis"}, 214 214 } 215 215 for _, a := range articles { 216 - _, err := dbs.DB().ExecContext(ctx, ` 216 + _, err := dbs.SQLDB().ExecContext(ctx, ` 217 217 INSERT INTO articles.articles (feed_url, guid, title, summary, content) VALUES (?, ?, ?, ?, ?) 218 218 `, feedURL, a.guid, a.title, a.summary, a.content) 219 219 assert.NilError(t, err) ··· 289 289 userDID, feedURL := seedSearchData(t, ctx, dbs) 290 290 291 291 otherFeed := "https://other.example.com/feed.xml" 292 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title) VALUES (?, ?)`, otherFeed, "Other Feed") 292 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO articles.feeds (feed_url, title) VALUES (?, ?)`, otherFeed, "Other Feed") 293 293 assert.NilError(t, err) 294 294 295 - _, err = dbs.DB().ExecContext(ctx, ` 295 + _, err = dbs.SQLDB().ExecContext(ctx, ` 296 296 INSERT INTO articles.articles (feed_url, guid, title) VALUES (?, ?, ?) 297 297 `, otherFeed, "other-1", "Go Concurrency Tips") 298 298 assert.NilError(t, err)
+2 -2
internal/db/batch_test.go
··· 47 47 assert.NilError(t, err) 48 48 } 49 49 50 - func seedSubscriptionData(t *testing.T, ctx context.Context, dbs *Databases) (userDID string) { 50 + func seedSubscriptionData(t *testing.T, ctx context.Context, dbs *Store) (userDID string) { 51 51 t.Helper() 52 52 userDID = "did:test:subuser" 53 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, userDID) 53 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, userDID) 54 54 assert.NilError(t, err) 55 55 return userDID 56 56 }
+10 -10
internal/db/db.go
··· 16 16 *sql.DB 17 17 } 18 18 19 - type Databases struct { 19 + type Store struct { 20 20 Users *UserStore 21 21 Articles *ArticleStore 22 22 ··· 25 25 26 26 var multiDriverSeq int64 27 27 28 - func OpenAll(basePath string) (*Databases, error) { 28 + func Open(basePath string) (*Store, error) { 29 29 articlesPath := basePath + "_articles" 30 30 recsPath := basePath + "_recs" 31 31 ··· 113 113 return nil, err 114 114 } 115 115 116 - return &Databases{ 116 + return &Store{ 117 117 Users: NewUserStore(d), 118 118 Articles: NewArticleStore(d), 119 119 db: d, 120 120 }, nil 121 121 } 122 122 123 - func (d *Databases) Close() error { 124 - if d.db != nil { 125 - _ = d.db.Close() 123 + func (s *Store) Close() error { 124 + if s.db != nil { 125 + _ = s.db.Close() 126 126 } 127 127 return nil 128 128 } 129 129 130 - func (d *Databases) InitVecTables(dimension int) error { 130 + func (s *Store) InitVecTables(dimension int) error { 131 131 if dimension <= 0 { 132 132 return nil 133 133 } ··· 135 135 fmt.Sprintf(`CREATE VIRTUAL TABLE IF NOT EXISTS recs.feed_embeddings USING vec0(feed_url TEXT PRIMARY KEY, embedding float[%d])`, dimension), 136 136 fmt.Sprintf(`CREATE VIRTUAL TABLE IF NOT EXISTS recs.article_embeddings USING vec0(article_id INTEGER PRIMARY KEY, embedding float[%d])`, dimension), 137 137 } { 138 - if _, err := d.db.ExecContext(context.Background(), stmt); err != nil { 138 + if _, err := s.db.ExecContext(context.Background(), stmt); err != nil { 139 139 return fmt.Errorf("create vec0 table: %w", err) 140 140 } 141 141 } 142 142 return nil 143 143 } 144 144 145 - func (d *Databases) DB() *sql.DB { 146 - return d.db.DB 145 + func (s *Store) SQLDB() *sql.DB { 146 + return s.db.DB 147 147 } 148 148 149 149 func initUsersSchema(db *DB) error {
+1 -1
internal/db/follow.go
··· 106 106 return true, nil 107 107 } 108 108 109 - func (s *UserStore) GetFollowDIDs(ctx context.Context, userDID string) ([]string, error) { 109 + func (s *UserStore) FollowDIDs(ctx context.Context, userDID string) ([]string, error) { 110 110 rows, err := s.db.QueryContext(ctx, ` 111 111 SELECT target_did FROM follows WHERE user_did = ? 112 112 `, userDID)
+9 -9
internal/db/follow_test.go
··· 7 7 "gotest.tools/v3/assert" 8 8 ) 9 9 10 - func seedFollowData(t *testing.T, ctx context.Context, dbs *Databases) (userDID, targetDID string) { 10 + func seedFollowData(t *testing.T, ctx context.Context, dbs *Store) (userDID, targetDID string) { 11 11 t.Helper() 12 12 13 13 userDID = "did:test:follower" 14 14 targetDID = "did:test:followed" 15 15 16 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, userDID) 16 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, userDID) 17 17 assert.NilError(t, err) 18 - _, err = dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, targetDID) 18 + _, err = dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, targetDID) 19 19 assert.NilError(t, err) 20 20 21 21 return userDID, targetDID ··· 83 83 userDID, _ := seedFollowData(t, ctx, dbs) 84 84 85 85 target2 := "did:test:followed2" 86 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, target2) 86 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, target2) 87 87 assert.NilError(t, err) 88 88 89 89 err = dbs.Users.UpsertFollow(ctx, userDID, "did:test:followed", "uri1", "cid1") ··· 102 102 _, targetDID := seedFollowData(t, ctx, dbs) 103 103 104 104 follower2 := "did:test:follower2" 105 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, follower2) 105 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, follower2) 106 106 assert.NilError(t, err) 107 107 108 108 err = dbs.Users.UpsertFollow(ctx, "did:test:follower", targetDID, "uri1", "cid1") ··· 115 115 assert.Equal(t, len(followers), 2) 116 116 } 117 117 118 - func TestGetFollowDIDs(t *testing.T) { 118 + func TestFollowDIDs(t *testing.T) { 119 119 ctx := context.Background() 120 120 dbs := setupTestDB(t) 121 121 userDID, _ := seedFollowData(t, ctx, dbs) 122 122 123 123 target2 := "did:test:followed2" 124 - _, err := dbs.DB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, target2) 124 + _, err := dbs.SQLDB().ExecContext(ctx, `INSERT INTO users (did) VALUES (?)`, target2) 125 125 assert.NilError(t, err) 126 126 127 127 err = dbs.Users.UpsertFollow(ctx, userDID, "did:test:followed", "uri1", "cid1") ··· 129 129 err = dbs.Users.UpsertFollow(ctx, userDID, target2, "uri2", "cid2") 130 130 assert.NilError(t, err) 131 131 132 - dids, err := dbs.Users.GetFollowDIDs(ctx, userDID) 132 + dids, err := dbs.Users.FollowDIDs(ctx, userDID) 133 133 assert.NilError(t, err) 134 134 assert.Equal(t, len(dids), 2) 135 135 } ··· 162 162 assert.NilError(t, err) 163 163 assert.Equal(t, following2, true) 164 164 165 - dids, err := dbs.Users.GetFollowDIDs(ctx, userDID) 165 + dids, err := dbs.Users.FollowDIDs(ctx, userDID) 166 166 assert.NilError(t, err) 167 167 assert.Equal(t, len(dids), 2) 168 168 }
+1 -1
internal/db/oauth_store.go
··· 14 14 db *DB 15 15 } 16 16 17 - func NewOAuthStore(dbs *Databases) *OAuthStore { 17 + func NewOAuthStore(dbs *Store) *OAuthStore { 18 18 return &OAuthStore{db: dbs.db} 19 19 } 20 20
+7 -7
internal/db/store.go
··· 8 8 "pkg.rbrt.fr/glean/internal/feed" 9 9 ) 10 10 11 - type FeedStoreAdapter struct { 11 + type FeedAdapter struct { 12 12 store *ArticleStore 13 13 } 14 14 15 - func NewFeedStoreAdapter(store *ArticleStore) *FeedStoreAdapter { 16 - return &FeedStoreAdapter{store: store} 15 + func NewFeedAdapter(store *ArticleStore) *FeedAdapter { 16 + return &FeedAdapter{store: store} 17 17 } 18 18 19 - func (a *FeedStoreAdapter) GetFeedsToFetch(ctx context.Context, olderThan time.Duration, limit int) ([]*feed.Feed, error) { 19 + func (a *FeedAdapter) GetFeedsToFetch(ctx context.Context, olderThan time.Duration, limit int) ([]*feed.Feed, error) { 20 20 dbFeeds, err := a.store.GetFeedsToFetch(ctx, olderThan, limit) 21 21 if err != nil { 22 22 return nil, err ··· 28 28 return feeds, nil 29 29 } 30 30 31 - func (a *FeedStoreAdapter) RecordFetchError(ctx context.Context, feedURL, lastError string) error { 31 + func (a *FeedAdapter) RecordFetchError(ctx context.Context, feedURL, lastError string) error { 32 32 return a.store.MarkFeedFetchError(ctx, feedURL, lastError) 33 33 } 34 34 35 - func (a *FeedStoreAdapter) StoreFetchResult(ctx context.Context, feedURL string, articles []feed.Article, faviconURL string) error { 35 + func (a *FeedAdapter) StoreFetchResult(ctx context.Context, feedURL string, articles []feed.Article, faviconURL string) error { 36 36 if len(articles) > 0 { 37 - if err := a.store.UpsertArticlesBatch(ctx, articles); err != nil { 37 + if err := a.store.BatchUpsertArticles(ctx, articles); err != nil { 38 38 return fmt.Errorf("failed to save articles: %w", err) 39 39 } 40 40 }
+1 -1
internal/db/user.go
··· 69 69 return u, nil 70 70 } 71 71 72 - func (s *UserStore) ListUserDIDs(ctx context.Context) (map[string]bool, error) { 72 + func (s *UserStore) UserDIDs(ctx context.Context) (map[string]bool, error) { 73 73 rows, err := s.db.QueryContext(ctx, `SELECT did FROM users`) 74 74 if err != nil { 75 75 return nil, err
+4 -4
internal/server/server.go
··· 58 58 } 59 59 60 60 type Server struct { 61 - dbs *db.Databases 61 + dbs *db.Store 62 62 router *chi.Mux 63 63 templates *template.Template 64 64 logger *slog.Logger ··· 73 73 sessionKey []byte 74 74 } 75 75 76 - func New(dbs *db.Databases, clientID, callbackURL, addr string, scheduler *feed.Scheduler, engine *cluster.Engine, logger *slog.Logger, sessionKey []byte) *Server { 76 + func New(dbs *db.Store, clientID, callbackURL, addr string, scheduler *feed.Scheduler, engine *cluster.Engine, logger *slog.Logger, sessionKey []byte) *Server { 77 77 oauthStore := db.NewOAuthStore(dbs) 78 78 79 79 var config oauth.ClientConfig ··· 204 204 s.router.Post("/auth/logout", s.handleAuthLogout) 205 205 s.router.Get("/oauth/client-metadata", s.handleOAuthClientMetadata) 206 206 207 - xrpc := atproto.NewXRPCHandler(s.dbs.DB(), s.engine) 207 + xrpc := atproto.NewXRPCHandler(s.dbs.SQLDB(), s.engine) 208 208 s.router.Get("/xrpc/at.glean.listSubscriptions", xrpc.ListSubscriptions) 209 209 s.router.Get("/xrpc/at.glean.listAnnotations", xrpc.ListAnnotations) 210 210 s.router.Get("/xrpc/at.glean.listLikes", xrpc.ListLikes) ··· 416 416 return 417 417 } 418 418 419 - existing, err := s.dbs.Users.ListUserDIDs(ctx) 419 + existing, err := s.dbs.Users.UserDIDs(ctx) 420 420 if err != nil { 421 421 s.logger.Error("failed to list existing users", "error", err) 422 422 return
+3 -3
main.go
··· 43 43 logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) 44 44 45 45 vec.Auto() 46 - dbs, err := db.OpenAll(*dbPath) 46 + dbs, err := db.Open(*dbPath) 47 47 if err != nil { 48 48 logger.Error("failed to open databases", "error", err) 49 49 os.Exit(1) ··· 53 53 clientID := envOr("GLEAN_OAUTH_CLIENT_ID", "") 54 54 callbackURL := envOr("GLEAN_OAUTH_REDIRECT_URL", "") 55 55 56 - storeAdapter := db.NewFeedStoreAdapter(dbs.Articles) 56 + storeAdapter := db.NewFeedAdapter(dbs.Articles) 57 57 scheduler := feed.NewScheduler(storeAdapter, logger, *fetchInterval, 30*time.Minute) 58 58 59 59 var embedder cluster.Embedder ··· 73 73 } 74 74 } 75 75 76 - engine := cluster.NewEngine(dbs.DB(), embedder, logger) 76 + engine := cluster.NewEngine(dbs.SQLDB(), embedder, logger) 77 77 78 78 srv := server.New(dbs, clientID, callbackURL, *addr, scheduler, engine, logger, []byte(sessionKey)) 79 79