Files
aa6abc5ed6 feat(event): add personal event subscriptions (#589)
* dws event

* fix consume fail

* test: add stream ticket injection probe

* feat: add portal ticket stream mode

* user event

* fix: allow portal ticket normal without app secret

* event

* user event

* eventType filter

* refactor(event): 优化IPC端点路径处理和改进相关测试

- 用dwsevent.IPCEndpoint替代原先根据GOOS判断的路径逻辑
- 新增event包实现Unix socket路径长度限制及长路径fallback机制
- 添加endpoint_test.go覆盖路径短长及唯一性的单元测试
- 修改busctl模块使用统一的IPC端点获取方法,避免重复实现
- transport_unix.go新增checkSocketPath函数检查路径长度,防止EINVAL错误
- 在监听和连接Unix socket时加入路径限制检查,提升错误明晰度
- 去除多个文件中无用的runtime导入,简化代码依赖

* opt

* event skill

* default value

* install script event

* fix: remove subscribe id event fanout filter

* fix(personal): 修正指定发送人消息描述错误

* more im event

* filter subId

* fix: align personal event schema with stream payload

* fix: avoid duplicate app helper name

* feat: simplify personal event schemas

* feat: simplify event schema output

* docs: refine dingtalk event skill references

* feat: align personal event consume flags

* fix event stop and status visibility

* hide app event public entrypoints

* hide incomplete personal sender event

* remove external event reference comments

* chore(event): prepare official release

* fix(event): harden personal stream lifecycle

---------

Co-authored-by: 玉澜 <yulan.wqy@alibaba-inc.com>
Co-authored-by: zhengyubai <zhengyubai618@gmail.com>
2026-07-10 17:54:43 +08:00

95 lines
2.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2026 Alibaba Group
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package dedup implements a fixed-capacity LRU set used by the bus to
// suppress duplicate events that the DingTalk Stream SDK redelivers on
// reconnect (see plan invariant #2). The set stores only keys, never values,
// and is safe for concurrent use by multiple goroutines.
package dedup
import (
"container/list"
"sync"
)
// DefaultCapacity is the default LRU size when LRU is constructed without an
// explicit capacity. 8192 is sized to absorb a typical reconnect-storm window
// (~5 min × 30 events/s) while staying memory-cheap (~256 KB at 32 bytes/key).
const DefaultCapacity = 8192
// LRU is a fixed-capacity LRU set of string keys. Zero value is not usable;
// call New or NewWithCapacity.
type LRU struct {
mu sync.Mutex
cap int
keys map[string]*list.Element
eviction *list.List // back = newest, front = oldest
}
// New returns an LRU with DefaultCapacity.
func New() *LRU { return NewWithCapacity(DefaultCapacity) }
// NewWithCapacity returns an LRU sized to hold up to cap keys. cap must be > 0.
func NewWithCapacity(cap int) *LRU {
if cap <= 0 {
cap = DefaultCapacity
}
return &LRU{
cap: cap,
keys: make(map[string]*list.Element, cap),
eviction: list.New(),
}
}
// Seen reports whether key was already present and inserts it if not. The
// return value is true when the caller should treat the event as a duplicate
// (drop it) and false when this is the first occurrence.
//
// Empty keys are never considered duplicates and are not stored — callers
// without a stable identifier should use RawEvent.DedupKey() which falls back
// to a content hash.
func (l *LRU) Seen(key string) bool {
if key == "" {
return false
}
l.mu.Lock()
defer l.mu.Unlock()
if el, ok := l.keys[key]; ok {
l.eviction.MoveToBack(el)
return true
}
if len(l.keys) >= l.cap {
oldest := l.eviction.Front()
if oldest != nil {
delete(l.keys, oldest.Value.(string))
l.eviction.Remove(oldest)
}
}
el := l.eviction.PushBack(key)
l.keys[key] = el
return false
}
// Len returns the current number of stored keys.
func (l *LRU) Len() int {
l.mu.Lock()
defer l.mu.Unlock()
return len(l.keys)
}
// Cap returns the configured capacity.
func (l *LRU) Cap() int { return l.cap }