Skip to content

Decouple event system from inspector system#9

Draft
Copilot wants to merge 4 commits intomasterfrom
copilot/fix-event-query-inspect-dependency
Draft

Decouple event system from inspector system#9
Copilot wants to merge 4 commits intomasterfrom
copilot/fix-event-query-inspect-dependency

Conversation

Copy link

Copilot AI commented Dec 8, 2025

OnEvent() with EventQueryInspect only fired when an inspector was registered, coupling two independent subsystems. This forced users to set up dummy inspectors just to receive query events.

Changes

Core decoupling

  • Added eventEnabled field to Datasource - tracks event system state independently from inspectEnabled
  • OnEvent() now auto-enables events when callback is set (and disables when nil)
  • dispatch_event() checks eventEnabled flag, returns early if disabled
  • inspect() always creates QueryInspect and dispatches events regardless of inspector state
  • inspectQuery() tracks timing when either system is enabled (not just inspector)

New API methods

  • EnableEvent(), DisableEvent(), IsEventEnabled() - explicit control over event system

Usage

// Events work without inspector
client.OnEvent(pgc.DefaultEventCallback())  
// ✅ Query events dispatched

// Inspector works without events
client.OnInspector(pgc.DefaultInspectorChain())
// ✅ Inspector invoked, no events

// Independent control
client.DisableEvent()    // Events off, inspector still active
client.DisableInspect()  // Inspector off, events still active

Both systems now operate independently while remaining fully backward compatible.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • filippo.io
    • Triggering command: /update-job-proxy /update-job-proxy sorg/loggy (dns block)
    • Triggering command: /update-job-proxy /update-job-proxy -s 172.18.0.0/16 -j DROP JCA_Global_Root_CA2.pem /opt/hostedtoolcache/go/1.24.10//usr/share/ca-certificates/mozilla/Buypass_Class_2_Root_CA.crt abb0a7f8b3b8a9820012222801583815154/log.json -unreachable=falbash /tmp/go-build326/usr/bin/runc (dns block)
  • gopkg.in
    • Triggering command: /update-job-proxy /update-job-proxy sorg/loggy (dns block)
    • Triggering command: /update-job-proxy /update-job-proxy -s 172.18.0.0/16 -j DROP JCA_Global_Root_CA2.pem /opt/hostedtoolcache/go/1.24.10//usr/share/ca-certificates/mozilla/Buypass_Class_2_Root_CA.crt abb0a7f8b3b8a9820012222801583815154/log.json -unreachable=falbash /tmp/go-build326/usr/bin/runc (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

Vấn đề hiện tại

Hiện tại, OnEvent() với EventQueryInspect chỉ hoạt động khi có inspector được đăng ký qua OnInspector() hoặc SetInspector(). Điều này gây ra sự phụ thuộc không cần thiết giữa hai cơ chế.

Root cause

Trong file pgc.go, function inspect() kiểm tra IsInspectEnabled() ngay từ đầu:

func (d *Datasource) inspect(funcName, query string, args []any, duration time.Duration) {
    if !d.IsInspectEnabled() {
        return  // ⚠️ Return sớm - không dispatch event
    }
    // ... 
    d.dispatch_event(EventQueryInspect, EventLevelDebug, response)
}

Và trong inspectQuery():

func (d *Datasource) inspectQuery(funcName, query string, args ...any) func() {
    if !d.IsInspectEnabled() {
        return func() {}  // ⚠️ Return empty function
    }
    // ...
}

Điều này có nghĩa là nếu chỉ gọi OnEvent() mà không set inspector, event sẽ không được dispatch.

Yêu cầu thay đổi

1. Thêm flag riêng biệt cho event system

Trong types.go, thêm field mới trong struct Datasource:

type Datasource struct {
    // ... existing fields ...
    
    // eventEnabled indicates whether event dispatching is enabled.
    // When true, events will be dispatched to the on_event callback.
    eventEnabled bool
}

2. Thêm methods để bật/tắt event system

Trong builder.go, thêm các methods:

// EnableEvent enables event dispatching.
func (d *Datasource) EnableEvent() *Datasource {
    d.mu.Lock()
    defer d.mu.Unlock()
    d.eventEnabled = true
    return d
}

// DisableEvent disables event dispatching.
func (d *Datasource) DisableEvent() *Datasource {
    d.mu.Lock()
    defer d.mu.Unlock()
    d.eventEnabled = false
    return d
}

// IsEventEnabled returns whether event dispatching is enabled.
func (d *Datasource) IsEventEnabled() bool {
    d.mu.RLock()
    defer d.mu.RUnlock()
    return d.eventEnabled
}

3. Cập nhật OnEvent() để tự động enable event

func (d *Datasource) OnEvent(fnc func(event EventKey, level EventLevel, response wrapify.R)) *Datasource {
    d.mu.Lock()
    defer d.mu.Unlock()
    d.on_event = fnc
    d.eventEnabled = fnc != nil  // Tự động enable khi set callback
    return d
}

4. Cập nhật dispatch_event() để kiểm tra eventEnabled

Trong pgc.go:

func (d *Datasource) dispatch_event(event EventKey, level EventLevel, response wrapify.R) {
    d.mu.RLock()
    enabled := d.eventEnabled
    callback := d.on_event
    d.mu.RUnlock()
    
    if !enabled || callback == nil {
        return
    }
    go callback(event, level, response)
}

5. Refactor inspect() để tách biệt logic

func (d *Datasource) inspect(funcName, query string, args []any, duration time.Duration) {
    // Create QueryInspect regardless of inspector status (needed for event)
    q := newQueryInspectWithDuration(funcName, query, args, duration)

    // Update lastInspect if inspect is enabled
    if d.IsInspectEnabled() {
        d.mu.Lock()
        d.lastInspect = &q
        d.mu.Unlock()

        // Invoke the inspector callback if it is set
        ins := d.getInspector()
        if ins != nil {
            go ins.Inspect(q)
        }
    }

    // Dispatch event independently (dispatch_event checks eventEnabled internally)
    response := wrapify.
        WrapProcessing("Starting inspection", nil).
        WithHeader(wrapify.Processing).
        Reply()
    d.dispatch_event(EventQueryInspect, EventLevelDebug, response)
}

6. Refactor inspectQuery() để luôn track timing khi event hoặc inspect enabled

func (d *Datasource) inspectQuery(funcName, query string, args ...any) func() {
    // Check if either inspect or event is enabled
    if !d.IsInspectEnabled() && !d.IsEventEnabled() {
        return func() {}
    }

    start := time.Now()
    return func() {
        d.inspect(funcName, query, args, time.Since(start))
    }
}

7. Đảm bảo backward compatibility

  • Các methods hiện có (EnableInspect, DisableInspect, OnInspector, SetInspector) vẫn hoạt động như cũ
  • Thêm documentation rõ ràng về sự khác biệt giữa Event và Inspector

Expected behavior sau khi fix

// Scenario 1: Chỉ dùng Event (không cần Inspector)
client.OnEvent(pgc.DefaultEventCallback())
// ✅ Event sẽ được dispatch khi có query

// Scenario 2: Chỉ dùng Inspector (không cần Event)  
client.OnInspector(pgc.DefaultInspectorChain())
// ✅ Inspector callback được gọi, event không dispatch

// Scenario 3: Dùng cả hai
client.OnEvent(pgc.DefaultEventCallback())
client.OnInspector(pgc.DefaultInspectorChain())
// ✅ Cả hai đều hoạt động

// Scenario 4: Tắt riêng từng feature
client.DisableEvent()   // Tắt event, giữ inspector
client.DisableInspect() // Tắt inspector, giữ event

Files cần sửa

  1. types.go - Thêm field eventEnabled
  2. builder.go - Thêm methods EnableEvent(), DisableEvent(), IsEventEnabled() và cập nhật OnEvent()
  3. pgc.go - Cập nhật dispatch_event(), inspect(), inspectQuery()
  4. Cập n...

This pull request was created as a result of the following prompt from Copilot chat.

Vấn đề hiện tại

Hiện tại, OnEvent() với EventQueryInspect chỉ hoạt động khi có inspector được đăng ký qua OnInspector() hoặc SetInspector(). Điều này gây ra sự phụ thuộc không cần thiết giữa hai cơ chế.

Root cause

Trong file pgc.go, function inspect() kiểm tra IsInspectEnabled() ngay từ đầu:

func (d *Datasource) inspect(funcName, query string, args []any, duration time.Duration) {
    if !d.IsInspectEnabled() {
        return  // ⚠️ Return sớm - không dispatch event
    }
    // ... 
    d.dispatch_event(EventQueryInspect, EventLevelDebug, response)
}

Và trong inspectQuery():

func (d *Datasource) inspectQuery(funcName, query string, args ...any) func() {
    if !d.IsInspectEnabled() {
        return func() {}  // ⚠️ Return empty function
    }
    // ...
}

Điều này có nghĩa là nếu chỉ gọi OnEvent() mà không set inspector, event sẽ không được dispatch.

Yêu cầu thay đổi

1. Thêm flag riêng biệt cho event system

Trong types.go, thêm field mới trong struct Datasource:

type Datasource struct {
    // ... existing fields ...
    
    // eventEnabled indicates whether event dispatching is enabled.
    // When true, events will be dispatched to the on_event callback.
    eventEnabled bool
}

2. Thêm methods để bật/tắt event system

Trong builder.go, thêm các methods:

// EnableEvent enables event dispatching.
func (d *Datasource) EnableEvent() *Datasource {
    d.mu.Lock()
    defer d.mu.Unlock()
    d.eventEnabled = true
    return d
}

// DisableEvent disables event dispatching.
func (d *Datasource) DisableEvent() *Datasource {
    d.mu.Lock()
    defer d.mu.Unlock()
    d.eventEnabled = false
    return d
}

// IsEventEnabled returns whether event dispatching is enabled.
func (d *Datasource) IsEventEnabled() bool {
    d.mu.RLock()
    defer d.mu.RUnlock()
    return d.eventEnabled
}

3. Cập nhật OnEvent() để tự động enable event

func (d *Datasource) OnEvent(fnc func(event EventKey, level EventLevel, response wrapify.R)) *Datasource {
    d.mu.Lock()
    defer d.mu.Unlock()
    d.on_event = fnc
    d.eventEnabled = fnc != nil  // Tự động enable khi set callback
    return d
}

4. Cập nhật dispatch_event() để kiểm tra eventEnabled

Trong pgc.go:

func (d *Datasource) dispatch_event(event EventKey, level EventLevel, response wrapify.R) {
    d.mu.RLock()
    enabled := d.eventEnabled
    callback := d.on_event
    d.mu.RUnlock()
    
    if !enabled || callback == nil {
        return
    }
    go callback(event, level, response)
}

5. Refactor inspect() để tách biệt logic

func (d *Datasource) inspect(funcName, query string, args []any, duration time.Duration) {
    // Create QueryInspect regardless of inspector status (needed for event)
    q := newQueryInspectWithDuration(funcName, query, args, duration)

    // Update lastInspect if inspect is enabled
    if d.IsInspectEnabled() {
        d.mu.Lock()
        d.lastInspect = &q
        d.mu.Unlock()

        // Invoke the inspector callback if it is set
        ins := d.getInspector()
        if ins != nil {
            go ins.Inspect(q)
        }
    }

    // Dispatch event independently (dispatch_event checks eventEnabled internally)
    response := wrapify.
        WrapProcessing("Starting inspection", nil).
        WithHeader(wrapify.Processing).
        Reply()
    d.dispatch_event(EventQueryInspect, EventLevelDebug, response)
}

6. Refactor inspectQuery() để luôn track timing khi event hoặc inspect enabled

func (d *Datasource) inspectQuery(funcName, query string, args ...any) func() {
    // Check if either inspect or event is enabled
    if !d.IsInspectEnabled() && !d.IsEventEnabled() {
        return func() {}
    }

    start := time.Now()
    return func() {
        d.inspect(funcName, query, args, time.Since(start))
    }
}

7. Đảm bảo backward compatibility

  • Các methods hiện có (EnableInspect, DisableInspect, OnInspector, SetInspector) vẫn hoạt động như cũ
  • Thêm documentation rõ ràng về sự khác biệt giữa Event và Inspector

Expected behavior sau khi fix

// Scenario 1: Chỉ dùng Event (không cần Inspector)
client.OnEvent(pgc.DefaultEventCallback())
// ✅ Event sẽ được dispatch khi có query

// Scenario 2: Chỉ dùng Inspector (không cần Event)  
client.OnInspector(pgc.DefaultInspectorChain())
// ✅ Inspector callback được gọi, event không dispatch

// Scenario 3: Dùng cả hai
client.OnEvent(pgc.DefaultEventCallback())
client.OnInspector(pgc.DefaultInspectorChain())
// ✅ Cả hai đều hoạt động

// Scenario 4: Tắt riêng từng feature
client.DisableEvent()   // Tắt event, giữ inspector
client.DisableInspect() // Tắt inspector, giữ event

Files cần sửa

  1. types.go - Thêm field eventEnabled
  2. builder.go - Thêm methods EnableEvent(), DisableEvent(), IsEventEnabled() và cập nhật OnEvent()
  3. pgc.go - Cập nhật dispatch_event(), inspect(), inspectQuery()
  4. Cập nhật tests nếu có

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 3 commits December 8, 2025 09:37
- Add eventEnabled field to Datasource struct
- Add EnableEvent(), DisableEvent(), and IsEventEnabled() methods
- Update OnEvent() to automatically enable events when callback is set
- Update dispatch_event() to check eventEnabled independently
- Refactor inspect() to decouple inspector logic from event dispatching
- Refactor inspectQuery() to check both IsInspectEnabled() and IsEventEnabled()

Co-authored-by: pnguyen215 <39581473+pnguyen215@users.noreply.github.com>
- Test event system works independently from inspector
- Test inspector system works independently from events
- Test both systems work together
- Test Enable/Disable event methods
- Test inspectQuery with different configurations
- Test OnEvent with nil callback

Co-authored-by: pnguyen215 <39581473+pnguyen215@users.noreply.github.com>
- Document the independence between event and inspector systems
- Provide usage examples for all scenarios
- Include migration guide from old workarounds
- Explain performance considerations
- Document backward compatibility guarantees

Co-authored-by: pnguyen215 <39581473+pnguyen215@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix dependency issue in OnEvent for EventQueryInspect Decouple event system from inspector system Dec 8, 2025
Copilot AI requested a review from pnguyen215 December 8, 2025 09:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants