Skip to content

Commit d888b86

Browse files
authored
Changes to speed up UI (#2385)
* cache, parallelize * remove radix themes, centralize css import * add back radix to check * reduce calls for signin url * profiling * turn on caching * skeleton, html cache * optimistic ui, remove profiling code * aggressive caching, other changes * a bunch of performance stuff * logging for create timing * add handler logs * more logs * align request id * fix error * remove timing logs * fix build error * remove more timing logs * restore resolver logs * adjustments * address comments
1 parent 676b135 commit d888b86

39 files changed

+991
-203
lines changed

taco/internal/domain/identifier_resolver.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ type IdentifierResolver interface {
99
// ResolveOrganization resolves an org identifier (name or UUID) to UUID
1010
ResolveOrganization(ctx context.Context, identifier string) (string, error)
1111

12+
// GetOrganization retrieves full organization details by UUID
13+
// Used to populate context with org info to avoid repeated queries
14+
GetOrganization(ctx context.Context, orgID string) (*Organization, error)
15+
1216
// ResolveUnit resolves a unit identifier to UUID within an organization
1317
ResolveUnit(ctx context.Context, identifier, orgID string) (string, error)
1418

taco/internal/domain/organization.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,11 @@ func ValidateOrgID(orgID string) error {
106106
// ============================================
107107

108108
// OrgContext carries organization information through the request lifecycle
109+
// Includes org name to avoid repeated database queries
109110
type OrgContext struct {
110-
OrgID string // UUID of the organization
111+
OrgID string // UUID of the organization
112+
OrgName string // Short name (e.g., "acme") - populated by middleware
113+
DisplayName string // Friendly name (e.g., "Acme Corp") - populated by middleware
111114
}
112115

113116
// orgContextKey is used to store OrgContext in context.Context
@@ -119,6 +122,16 @@ func ContextWithOrg(ctx context.Context, orgID string) context.Context {
119122
return context.WithValue(ctx, orgContextKey{}, &OrgContext{OrgID: orgID})
120123
}
121124

125+
// ContextWithOrgFull adds full organization context including name and display name
126+
// This avoids repeated database queries for org info
127+
func ContextWithOrgFull(ctx context.Context, orgID, orgName, displayName string) context.Context {
128+
return context.WithValue(ctx, orgContextKey{}, &OrgContext{
129+
OrgID: orgID,
130+
OrgName: orgName,
131+
DisplayName: displayName,
132+
})
133+
}
134+
122135
// OrgFromContext retrieves organization context from context.Context
123136
// Returns the OrgContext and a boolean indicating if it was found
124137
func OrgFromContext(ctx context.Context) (*OrgContext, bool) {

taco/internal/middleware/org_context.go

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package middleware
22

33
import (
4+
"log"
5+
46
"github.com/diggerhq/digger/opentaco/internal/domain"
57
"github.com/labstack/echo/v4"
6-
"log"
78
)
89

910
const DefaultOrgID = "default"
@@ -19,17 +20,13 @@ func JWTOrgResolverMiddleware(resolver domain.IdentifierResolver) echo.Middlewar
1920
orgName = DefaultOrgID
2021
}
2122

22-
log.Printf("[JWTOrgResolver] Resolving org name '%s' to UUID", orgName)
23-
2423
// Resolve org name to UUID
2524
orgUUID, err := resolver.ResolveOrganization(c.Request().Context(), orgName)
2625
if err != nil {
2726
log.Printf("[JWTOrgResolver] Failed to resolve organization '%s': %v", orgName, err)
2827
return echo.NewHTTPError(500, "Failed to resolve organization")
2928
}
3029

31-
log.Printf("[JWTOrgResolver] Successfully resolved '%s' to UUID: %s", orgName, orgUUID)
32-
3330
// Add to domain context
3431
ctx := domain.ContextWithOrg(c.Request().Context(), orgUUID)
3532
c.SetRequest(c.Request().WithContext(ctx))
@@ -47,8 +44,6 @@ func ResolveOrgContextMiddleware(resolver domain.IdentifierResolver) echo.Middle
4744
path := c.Request().URL.Path
4845
method := c.Request().Method
4946

50-
log.Printf("[WebhookOrgResolver] MIDDLEWARE INVOKED for path: %s, method: %s", path, method)
51-
5247
// Skip org resolution for endpoints that create/list orgs
5348
// These endpoints don't require an existing org context
5449
skipOrgResolution := (method == "POST" && path == "/internal/api/orgs") ||
@@ -57,30 +52,19 @@ func ResolveOrgContextMiddleware(resolver domain.IdentifierResolver) echo.Middle
5752
(method == "GET" && path == "/internal/api/orgs/user")
5853

5954
if skipOrgResolution {
60-
log.Printf("[WebhookOrgResolver] Skipping org resolution for endpoint: %s %s", method, path)
6155
return next(c)
6256
}
6357

6458
// Get org name from echo context (set by WebhookAuth)
6559
orgName, ok := c.Get("organization_id").(string)
66-
if !ok {
67-
log.Printf("[WebhookOrgResolver] WARNING: organization_id not found in context, defaulting to 'default'")
68-
orgName = DefaultOrgID
69-
} else if orgName == "" {
70-
log.Printf("[WebhookOrgResolver] WARNING: organization_id is empty, defaulting to 'default'")
60+
if !ok || orgName == "" {
7161
orgName = DefaultOrgID
72-
} else {
73-
log.Printf("[WebhookOrgResolver] Found organization_id in context: '%s'", orgName)
7462
}
7563

76-
log.Printf("[WebhookOrgResolver] Resolving org name '%s' to UUID", orgName)
77-
78-
// Resolve org name to UUID
64+
// Resolve org name to UUID and get full org info
7965
orgUUID, err := resolver.ResolveOrganization(c.Request().Context(), orgName)
8066
if err != nil {
81-
log.Printf("[WebhookOrgResolver] ERROR: Failed to resolve organization '%s': %v", orgName, err)
82-
log.Printf("[WebhookOrgResolver] ERROR: This likely means the organization doesn't exist in the database yet or the external_org_id doesn't match")
83-
log.Printf("[WebhookOrgResolver] ERROR: Check if the organization was created successfully with external_org_id='%s'", orgName)
67+
log.Printf("[WebhookOrgResolver] Failed to resolve organization '%s': %v", orgName, err)
8468
return echo.NewHTTPError(500, map[string]interface{}{
8569
"error": "Failed to resolve organization",
8670
"detail": err.Error(),
@@ -89,13 +73,18 @@ func ResolveOrgContextMiddleware(resolver domain.IdentifierResolver) echo.Middle
8973
})
9074
}
9175

92-
log.Printf("[WebhookOrgResolver] SUCCESS: Resolved '%s' to UUID: %s", orgName, orgUUID)
93-
94-
// Add to domain context
95-
ctx := domain.ContextWithOrg(c.Request().Context(), orgUUID)
96-
c.SetRequest(c.Request().WithContext(ctx))
97-
98-
log.Printf("[WebhookOrgResolver] Domain context updated with org UUID")
76+
// Get full org info to populate context (avoids repeated queries)
77+
orgInfo, err := resolver.GetOrganization(c.Request().Context(), orgUUID)
78+
if err != nil {
79+
log.Printf("[WebhookOrgResolver] Failed to get org details for %s: %v - using basic context", orgUUID, err)
80+
// Fall back to basic context if org lookup fails
81+
ctx := domain.ContextWithOrg(c.Request().Context(), orgUUID)
82+
c.SetRequest(c.Request().WithContext(ctx))
83+
} else {
84+
// Add full org info to domain context to avoid repeated queries
85+
ctx := domain.ContextWithOrgFull(c.Request().Context(), orgInfo.ID, orgInfo.Name, orgInfo.DisplayName)
86+
c.SetRequest(c.Request().WithContext(ctx))
87+
}
9988

10089
return next(c)
10190
}

taco/internal/query/config.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,24 @@ type SQLiteConfig struct {
1818
BusyTimeout time.Duration `envconfig:"BUSY_TIMEOUT" default:"5s"`
1919
MaxOpenConns int `envconfig:"MAX_OPEN_CONNS" default:"25"`
2020
MaxIdleConns int `envconfig:"MAX_IDLE_CONNS" default:"10"`
21+
ConnMaxLifetime time.Duration `envconfig:"CONN_MAX_LIFETIME" default:"300s"`
22+
ConnMaxIdleTime time.Duration `envconfig:"CONN_MAX_IDLE_TIME" default:"600s"`
2123
PragmaJournalMode string `envconfig:"PRAGMA_JOURNAL_MODE" default:"WAL"`
2224
PragmaForeignKeys string `envconfig:"PRAGMA_FOREIGN_KEYS" default:"ON"`
2325
PragmaBusyTimeout string `envconfig:"PRAGMA_BUSY_TIMEOUT" default:"5000"`
2426
}
2527

2628
type PostgresConfig struct {
27-
Host string `envconfig:"HOST" default:"localhost"`
28-
Port int `envconfig:"PORT" default:"5432"`
29-
User string `envconfig:"USER" default:"postgres"`
30-
Password string `envconfig:"PASSWORD"`
31-
DBName string `envconfig:"DBNAME" default:"taco"`
32-
SSLMode string `envconfig:"SSLMODE" default:"disable"`
29+
Host string `envconfig:"HOST" default:"localhost"`
30+
Port int `envconfig:"PORT" default:"5432"`
31+
User string `envconfig:"USER" default:"postgres"`
32+
Password string `envconfig:"PASSWORD"`
33+
DBName string `envconfig:"DBNAME" default:"taco"`
34+
SSLMode string `envconfig:"SSLMODE" default:"disable"`
35+
MaxOpenConns int `envconfig:"MAX_OPEN_CONNS" default:"25"`
36+
MaxIdleConns int `envconfig:"MAX_IDLE_CONNS" default:"10"`
37+
ConnMaxLifetime time.Duration `envconfig:"CONN_MAX_LIFETIME" default:"300s"`
38+
ConnMaxIdleTime time.Duration `envconfig:"CONN_MAX_IDLE_TIME" default:"600s"`
3339
}
3440

3541
type MSSQLConfig struct {
@@ -41,10 +47,14 @@ type MSSQLConfig struct {
4147
}
4248

4349
type MySQLConfig struct {
44-
Host string `envconfig:"HOST" default:"localhost"`
45-
Port int `envconfig:"PORT" default:"3306"`
46-
User string `envconfig:"USER" default:"root"`
47-
Password string `envconfig:"PASSWORD"`
48-
DBName string `envconfig:"DBNAME" default:"taco"`
49-
Charset string `envconfig:"CHARSET" default:"utf8mb4"`
50+
Host string `envconfig:"HOST" default:"localhost"`
51+
Port int `envconfig:"PORT" default:"3306"`
52+
User string `envconfig:"USER" default:"root"`
53+
Password string `envconfig:"PASSWORD"`
54+
DBName string `envconfig:"DBNAME" default:"taco"`
55+
Charset string `envconfig:"CHARSET" default:"utf8mb4"`
56+
MaxOpenConns int `envconfig:"MAX_OPEN_CONNS" default:"25"`
57+
MaxIdleConns int `envconfig:"MAX_IDLE_CONNS" default:"10"`
58+
ConnMaxLifetime time.Duration `envconfig:"CONN_MAX_LIFETIME" default:"300s"`
59+
ConnMaxIdleTime time.Duration `envconfig:"CONN_MAX_IDLE_TIME" default:"600s"`
5060
}

taco/internal/query/mysql/store.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,25 @@ func Connect(cfg query.MySQLConfig) (*gorm.DB, error) {
1616
cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DBName, cfg.Charset)
1717

1818
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
19-
Logger: logger.Default.LogMode(logger.Silent),
19+
Logger: logger.Default.LogMode(logger.Silent),
20+
PrepareStmt: true, // Use prepared statements for better performance
2021
})
2122
if err != nil {
2223
return nil, fmt.Errorf("failed to connect to mysql: %w", err)
2324
}
2425

26+
// Configure connection pool for production workloads
27+
sqlDB, err := db.DB()
28+
if err != nil {
29+
return nil, fmt.Errorf("failed to get sql.DB: %w", err)
30+
}
31+
32+
// Optimize connection pool settings from config (use MYSQL_MAX_OPEN_CONNS, etc.)
33+
sqlDB.SetMaxOpenConns(cfg.MaxOpenConns)
34+
sqlDB.SetMaxIdleConns(cfg.MaxIdleConns)
35+
sqlDB.SetConnMaxLifetime(cfg.ConnMaxLifetime)
36+
sqlDB.SetConnMaxIdleTime(cfg.ConnMaxIdleTime)
37+
2538
return db, nil
2639
}
2740

taco/internal/query/postgres/store.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,24 @@ func Connect(cfg query.PostgresConfig) (*gorm.DB, error) {
1717

1818
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
1919
Logger: logger.Default.LogMode(logger.Silent),
20+
PrepareStmt: true, // Use prepared statements for better performance
2021
})
2122
if err != nil {
2223
return nil, fmt.Errorf("failed to connect to postgres: %w", err)
2324
}
2425

26+
// Configure connection pool for production workloads
27+
sqlDB, err := db.DB()
28+
if err != nil {
29+
return nil, fmt.Errorf("failed to get sql.DB: %w", err)
30+
}
31+
32+
// Optimize connection pool settings from config (use POSTGRES_MAX_OPEN_CONNS, etc.)
33+
sqlDB.SetMaxOpenConns(cfg.MaxOpenConns)
34+
sqlDB.SetMaxIdleConns(cfg.MaxIdleConns)
35+
sqlDB.SetConnMaxLifetime(cfg.ConnMaxLifetime)
36+
sqlDB.SetConnMaxIdleTime(cfg.ConnMaxIdleTime)
37+
2538
return db, nil
2639
}
2740

taco/internal/query/sqlite/store.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ func Connect(cfg query.SQLiteConfig) (*gorm.DB, error) {
3838
return nil, fmt.Errorf("apply busy_timeout: %w", err)
3939
}
4040

41-
// Configure connection pool settings
41+
// Configure connection pool settings from config (use SQLITE_MAX_OPEN_CONNS, etc.)
4242
sqlDB, err := db.DB()
4343
if err != nil {
4444
return nil, fmt.Errorf("get underlying sql.DB: %w", err)
4545
}
4646
sqlDB.SetMaxOpenConns(cfg.MaxOpenConns)
4747
sqlDB.SetMaxIdleConns(cfg.MaxIdleConns)
48+
sqlDB.SetConnMaxLifetime(cfg.ConnMaxLifetime)
49+
sqlDB.SetConnMaxIdleTime(cfg.ConnMaxIdleTime)
4850

4951
return db, nil
5052
}

taco/internal/query/types/models.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import (
88

99
type Role struct {
1010
ID string `gorm:"type:varchar(36);primaryKey"`
11-
OrgID string `gorm:"type:varchar(36);index;uniqueIndex:unique_org_role_name"` // Foreign key to organizations.id (UUID)
12-
Name string `gorm:"type:varchar(255);not null;index;uniqueIndex:unique_org_role_name"` // Unique identifier per org (e.g., "admin", "viewer")
11+
OrgID string `gorm:"type:varchar(36);index;index:idx_roles_org_id_name;uniqueIndex:unique_org_role_name"` // Foreign key to organizations.id (UUID)
12+
Name string `gorm:"type:varchar(255);not null;index;index:idx_roles_org_id_name;uniqueIndex:unique_org_role_name"` // Composite index for (org_id, name) queries
1313
Description string
1414
Permissions []Permission `gorm:"many2many:role_permissions;constraint:OnDelete:CASCADE,OnUpdate:CASCADE"`
1515
CreatedAt time.Time
@@ -27,8 +27,8 @@ func (Role) TableName() string { return "roles" }
2727

2828
type Permission struct {
2929
ID string `gorm:"type:varchar(36);primaryKey"`
30-
OrgID string `gorm:"type:varchar(36);index;uniqueIndex:unique_org_permission_name"` // Foreign key to organizations.id (UUID)
31-
Name string `gorm:"type:varchar(255);not null;index;uniqueIndex:unique_org_permission_name"` // Unique identifier per org (e.g., "unit-read", "unit-write")
30+
OrgID string `gorm:"type:varchar(36);index;index:idx_permissions_org_id_name;uniqueIndex:unique_org_permission_name"` // Foreign key to organizations.id (UUID)
31+
Name string `gorm:"type:varchar(255);not null;index;index:idx_permissions_org_id_name;uniqueIndex:unique_org_permission_name"` // Composite index for (org_id, name) queries
3232
Description string
3333
Rules []Rule `gorm:"constraint:OnDelete:CASCADE"`
3434
CreatedBy string
@@ -141,8 +141,8 @@ func (u *User) BeforeCreate(tx *gorm.DB) error {
141141

142142
type Unit struct {
143143
ID string `gorm:"type:varchar(36);primaryKey"`
144-
OrgID string `gorm:"type:varchar(36);index;uniqueIndex:idx_units_org_name"` // Foreign key to organizations.id (UUID)
145-
Name string `gorm:"type:varchar(255);not null;index;uniqueIndex:idx_units_org_name"`
144+
OrgID string `gorm:"type:varchar(36);index;index:idx_units_org_id_name;uniqueIndex:idx_units_org_name"` // Foreign key to organizations.id (UUID)
145+
Name string `gorm:"type:varchar(255);not null;index;index:idx_units_org_id_name;uniqueIndex:idx_units_org_name"` // Composite index for (org_id, name) queries
146146
Size int64 `gorm:"default:0"`
147147
UpdatedAt time.Time `gorm:"autoUpdateTime"`
148148
Locked bool `gorm:"default:false"`
@@ -163,8 +163,8 @@ func (Unit) TableName() string { return "units" }
163163

164164
type Tag struct {
165165
ID string `gorm:"type:varchar(36);primaryKey"`
166-
OrgID string `gorm:"type:varchar(36);index;uniqueIndex:unique_org_tag_name"` // Foreign key to organizations.id (UUID)
167-
Name string `gorm:"type:varchar(255);not null;index;uniqueIndex:unique_org_tag_name"` // Unique per org
166+
OrgID string `gorm:"type:varchar(36);index;index:idx_tags_org_id_name;uniqueIndex:unique_org_tag_name"` // Foreign key to organizations.id (UUID)
167+
Name string `gorm:"type:varchar(255);not null;index;index:idx_tags_org_id_name;uniqueIndex:unique_org_tag_name"` // Composite index for (org_id, name) queries
168168
}
169169

170170
func (t *Tag) BeforeCreate(tx *gorm.DB) error {

taco/internal/repositories/identifier_resolver.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,43 @@ func (r *gormIdentifierResolver) ResolveOrganization(ctx context.Context, identi
6262
return "", fmt.Errorf("organization not found: %s", lookupValue)
6363
}
6464

65+
// GetOrganization retrieves full organization details by UUID
66+
func (r *gormIdentifierResolver) GetOrganization(ctx context.Context, orgID string) (*domain.Organization, error) {
67+
if r.db == nil {
68+
return nil, fmt.Errorf("database not available")
69+
}
70+
71+
var org struct {
72+
ID string
73+
Name string
74+
DisplayName string
75+
ExternalOrgID *string
76+
CreatedBy string
77+
}
78+
79+
err := r.db.WithContext(ctx).
80+
Table("organizations").
81+
Where("id = ?", orgID).
82+
First(&org).Error
83+
84+
if err != nil {
85+
return nil, fmt.Errorf("organization not found: %s", orgID)
86+
}
87+
88+
externalID := ""
89+
if org.ExternalOrgID != nil {
90+
externalID = *org.ExternalOrgID
91+
}
92+
93+
return &domain.Organization{
94+
ID: org.ID,
95+
Name: org.Name,
96+
DisplayName: org.DisplayName,
97+
ExternalOrgID: externalID,
98+
CreatedBy: org.CreatedBy,
99+
}, nil
100+
}
101+
65102
// ResolveUnit resolves unit identifier to UUID within an organization
66103
func (r *gormIdentifierResolver) ResolveUnit(ctx context.Context, identifier, orgID string) (string, error) {
67104
return r.resolveResource(ctx, "units", identifier, orgID)

0 commit comments

Comments
 (0)