* 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>
95 lines
2.8 KiB
Go
95 lines
2.8 KiB
Go
// 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 }
|