Skip to content

Educentr/go-activerecord

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

160 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

go-activerecord

Go Reference Go Report Card

go-activerecord is a high-performance ORM generator for Go implementing the Active Record pattern with support for Octopus/Tarantool 1.5 and PostgreSQL.

Active Record pattern is a database access approach where each table is wrapped in a class and each row is represented as an object with methods for data manipulation.

πŸš€ Killer Features

1. Compile-time Code Generation Instead of Runtime Reflection

// Declarative description
type FieldsUser struct {
    Id   int64  `ar:"primary_key"`
    Name string `ar:"size:256;selector:SelectByName"`
}

// Generates type-safe code
user := user.New(ctx)
user.SetName("John")
user.Insert(ctx)

// Type-safe selectors for each index
users, _ := user.SelectByName(ctx, "John")

Why it matters:

  • ⚑ Zero runtime overhead β€” no reflection, all checks at compile time
  • πŸ›‘οΈ Type safety β€” errors caught by compiler, not in production
  • πŸ” IDE autocomplete β€” full support for method autocompletion
  • πŸ“Š Performance β€” speed comparable to hand-written SQL

2. Atomic Database Operations

user, _ := user.SelectById(ctx, 123)

// Increment executed atomically in DB, not in application
user.IncLoginCount(1)    // UPDATE users SET login_count = login_count + 1

// Bitwise operations with named flags (no magic numbers!)
user.SetBitFlags(user.FlagsPremiumFlag)   // UPDATE users SET flags = flags | 4
user.ClearBitFlags(user.FlagsActiveFlag)  // UPDATE users SET flags = flags & ~1

user.Update(ctx)

Benefits:

  • πŸ”’ No race conditions β€” operations executed atomically in DB
  • πŸ’Ύ Traffic savings β€” no need for read-modify-write
  • ⚑ High performance β€” one operation instead of three

3. Multi-backend: Octopus/Tarantool + PostgreSQL

// Octopus/Tarantool 1.5
//ar:namespace:5
//ar:backend:octopus

// PostgreSQL
//ar:namespace:users
//ar:backend:postgres

Uniqueness:

  • πŸ”„ Unified codebase for different databases
  • πŸš€ Smooth migration from Tarantool to PostgreSQL
  • πŸ—οΈ Hybrid architecture β€” different data in different databases
  • πŸ“¦ Legacy support β€” works with old Octopus/Tarantool 1.5

4. Built-in Protection Against Common Mistakes

// ❌ Attempting to select millions of records without limit
users, err := user.SelectByStatus(ctx, "active", nil)  // COMPILATION ERROR!

// βœ… Mandatory limiter for non-unique indexes
limiter := activerecord.NewLimiter(1000)
users, err := user.SelectByStatus(ctx, "active", limiter)

// ❌ Attempting to change primary key
user.SetId(456)  // COMPILATION ERROR - method unavailable!

Built-in protections:

  • πŸ›‘οΈ Mandatory limiters β€” impossible to accidentally select entire database
  • πŸ” Immutable PKs β€” primary keys protected from modification
  • 🎯 SQL injection protection β€” prepared statements out of the box
  • πŸ“Š Limit warnings β€” data selection control

5. Sharding and Replication Out of the Box

# Cluster configuration
cluster:
  Timeout: 200ms
  PoolSize: 10
  shards:
    1:
      master: "10.0.1.1:3301,10.0.1.2:3301"
      replica: "10.0.2.1:3301,10.0.2.2:3301"
    2:
      master: "10.0.3.1:3301"
      replica: "10.0.4.1:3301"
// Automatic shard and replica selection for reads
users, _ := user.SelectByIds(ctx, []int64{1, 1000, 2000})

Capabilities:

  • 🌍 Horizontal scaling β€” automatic sharding
  • πŸ“– Read replicas β€” automatic read load balancing
  • πŸ”„ Failover β€” automatic switching on master failure
  • βš™οΈ Runtime configuration β€” settings changes without restart

6. Powerful Serializers for Complex Types

type FieldsConfig struct {
    // JSON out of the box
    Settings string `ar:"serializer:Json;size:2048"`

    // Printf for formatting
    Version string `ar:"serializer:Printf,%d.%d.%d;size:16"`

    // Custom logic
    Tags string `ar:"serializer:CustomTags;size:512"`
}

// Work with deserialized types in code
cfg.SetSettings(map[string]interface{}{
    "theme": "dark",
    "lang": "en",
})

Built-in serializers:

  • πŸ“¦ Json β€” encoding/json from standard library
  • 🎨 Printf β€” template formatting
  • πŸ”§ Mapstructure β€” flexible deserialization
  • ✨ Custom β€” your own serializers in 2 functions

7. Observability: Metrics and Logging

// Built-in Prometheus integration
metrics := prometheus.NewMetrics()
activerecord.RegisterMetrics(metrics)

// Detailed logging
logger := zerolog.New(os.Stdout)
activerecord.RegisterLogger(&logger)

Metrics out of the box:

  • ⏱️ Timing β€” query execution time (select_db, update_db, etc)
  • πŸ“Š Counters β€” operation counts (insert_success, update_success)
  • ❌ Errors β€” detailed error statistics
  • 🎯 Per-namespace β€” metrics for each table separately

πŸ“Š Comparison with Other Solutions

Feature go-activerecord GORM sqlx sqlc
Type safety βœ… Compile-time ⚠️ Runtime ❌ Manual βœ… Compile-time
Reflection overhead βœ… None ❌ Yes βœ… None βœ… None
Code generation βœ… Yes ❌ No ❌ No βœ… Yes
Octopus/Tarantool βœ… Yes ❌ No ❌ No ❌ No
PostgreSQL βœ… Yes βœ… Yes βœ… Yes βœ… Yes
Atomic mutators βœ… Yes ❌ No ❌ No ❌ No
Sharding βœ… Built-in ⚠️ Plugins ❌ Manual ❌ Manual
Error protection βœ… Compile-time ⚠️ Runtime ❌ None βœ… Compile-time
Metrics βœ… Built-in ⚠️ Plugins ❌ None ❌ None

⚑ Performance

Benchmark_Insert-8              50000    28543 ns/op    2048 B/op    12 allocs/op
Benchmark_Select-8             100000    12431 ns/op     896 B/op     8 allocs/op
Benchmark_Update-8              75000    19834 ns/op    1024 B/op    10 allocs/op
Benchmark_AtomicInc-8          100000    11234 ns/op     512 B/op     6 allocs/op

Why it's fast:

  • πŸš€ No reflection in runtime
  • πŸ’Ύ Connection pool reuse
  • πŸ“¦ Efficient serialization
  • ⚑ Prepared statements cached

🎯 Benefits

For Developers

  • πŸ“ Less code β€” no need to write CRUD by hand
  • πŸ› Fewer bugs β€” errors caught by compiler
  • πŸŽ“ Easy to learn β€” declarative approach is intuitive
  • πŸ” IDE support β€” method autocompletion

For Business

  • πŸš€ Fast time-to-market β€” code generation instead of writing
  • πŸ’° Resource savings β€” high performance
  • πŸ›‘οΈ Reliability β€” built-in error protection
  • πŸ“ˆ Scalability β€” sharding out of the box

For DevOps

  • πŸ“Š Observability β€” metrics out of the box
  • πŸ”§ Simple configuration β€” one config for everything
  • πŸ”„ Zero-downtime β€” runtime configuration
  • πŸ—οΈ Multi-backend β€” flexibility in database choice

πŸš€ Quick Start

Installation

# Install argen utility
git clone https://github.com/Educentr/go-activerecord
cd go-activerecord
make install

# Add dependency to project
go get github.com/Educentr/go-activerecord/v3

Usage Example

1. Create model declaration:

// model/repository/declaration/user.go
package repository

//ar:serverConf:mydb
//ar:namespace:users
//ar:backend:postgres
type FieldsUser struct {
    Id        int64  `ar:"primary_key"`
    Email     string `ar:"unique;size:256;selector:SelectByEmail"`
    Name      string `ar:"size:256"`
    LoginCount uint32 `ar:"mutators:inc,dec"`
    Flags     uint32 `ar:""`  // mutators added via FlagsUser
    CreatedAt uint32 `ar:""`
}

type IndexesUser struct {
    EmailCreated bool `ar:"fields:Email,CreatedAt;unique"`
}

// Named bit flags - generates constants FlagsActiveFlag, FlagsVerifiedFlag, etc.
type FlagsUser struct {
    Flags string `ar:"flags:Active,Verified,Premium,_,Admin"`
}

2. Generate code:

argen --path "model/repository" \
      --declaration "declaration" \
      --destination "generated"

3. Use in code:

package main

import (
    "context"
    "github.com/Educentr/go-activerecord/v3/pkg/activerecord"
    "yourapp/model/repository/generated/user"
)

func main() {
    ctx := context.Background()

    // Initialize config
    activerecord.RegisterConfig(yourConfig)

    // Create user
    u := user.New(ctx)
    u.SetEmail("[email protected]")
    u.SetName("John Doe")
    u.Insert(ctx)

    // Search by email
    found, err := user.SelectByEmail(ctx, "[email protected]")
    if err != nil {
        panic(err)
    }

    // Atomic increment
    found.IncLoginCount(1)

    // Set flags using named constants
    found.SetBitFlags(user.FlagsVerifiedFlag)
    found.Update(ctx)

    fmt.Printf("User %s logged in %d times\n",
        found.GetName(), found.GetLoginCount())
}

πŸ“š Documentation

🏒 Production Ready

go-activerecord is used in production in high-load projects:

  • βœ… Tested on billions of queries
  • βœ… Works with petabytes of data
  • βœ… Supports clusters of hundreds of servers
  • βœ… Used by Mail.ru and other major companies

🀝 Contributing

We welcome contributions! See CONTRIBUTING.md for details.

πŸ“ License

The project is distributed under the MIT license. See LICENSE file for details.

πŸ”— Useful Links


Made with ❀️ at Educentr

About

Yet another ORM for go

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages