Skip to content

Commit 1fe2f03

Browse files
ash2kMrAliaspellared
authored
Deprecate Sortable (#4734)
* Deprecate Sortable * Remove redundant checks * Move code to a non-deprecated func * Apply suggestions from code review Co-authored-by: Tyler Yahn <[email protected]> * Mention individual deprecated function * Update attribute/set.go Co-authored-by: Robert Pająk <[email protected]> * Add BenchmarkNewSet --------- Co-authored-by: Tyler Yahn <[email protected]> Co-authored-by: Robert Pająk <[email protected]>
1 parent 2d968c4 commit 1fe2f03

File tree

3 files changed

+69
-63
lines changed

3 files changed

+69
-63
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
1616

1717
- Drop support for [Go 1.20]. (#4967)
1818

19+
### Deprecated
20+
21+
- Deprecate `go.opentelemetry.io/otel/attribute.Sortable` type. (#4734)
22+
- Deprecate `go.opentelemetry.io/otel/attribute.NewSetWithSortable` function. (#4734)
23+
- Deprecate `go.opentelemetry.io/otel/attribute.NewSetWithSortableFiltered` function. (#4734)
24+
1925
## [1.24.0/0.46.0/0.0.1-alpha] 2024-02-23
2026

2127
This release is the last to support [Go 1.20].

attribute/set.go

+44-63
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
package attribute // import "go.opentelemetry.io/otel/attribute"
55

66
import (
7+
"cmp"
78
"encoding/json"
89
"reflect"
10+
"slices"
911
"sort"
10-
"sync"
1112
)
1213

1314
type (
@@ -37,10 +38,11 @@ type (
3738
iface interface{}
3839
}
3940

40-
// Sortable implements sort.Interface, used for sorting KeyValue. This is
41-
// an exported type to support a memory optimization. A pointer to one of
42-
// these is needed for the call to sort.Stable(), which the caller may
43-
// provide in order to avoid an allocation. See NewSetWithSortable().
41+
// Sortable implements sort.Interface, used for sorting KeyValue.
42+
//
43+
// Deprecated: This type is no longer used. It was added as a performance
44+
// optimization for Go < 1.21 that is no longer needed (Go < 1.21 is no
45+
// longer supported by the module).
4446
Sortable []KeyValue
4547
)
4648

@@ -54,12 +56,6 @@ var (
5456
iface: [0]KeyValue{},
5557
},
5658
}
57-
58-
// sortables is a pool of Sortables used to create Sets with a user does
59-
// not provide one.
60-
sortables = sync.Pool{
61-
New: func() interface{} { return new(Sortable) },
62-
}
6359
)
6460

6561
// EmptySet returns a reference to a Set with no elements.
@@ -185,26 +181,18 @@ func empty() Set {
185181
// Except for empty sets, this method adds an additional allocation compared
186182
// with calls that include a Sortable.
187183
func NewSet(kvs ...KeyValue) Set {
188-
// Check for empty set.
189-
if len(kvs) == 0 {
190-
return empty()
191-
}
192-
srt := sortables.Get().(*Sortable)
193-
s, _ := NewSetWithSortableFiltered(kvs, srt, nil)
194-
sortables.Put(srt)
184+
s, _ := NewSetWithFiltered(kvs, nil)
195185
return s
196186
}
197187

198188
// NewSetWithSortable returns a new Set. See the documentation for
199189
// NewSetWithSortableFiltered for more details.
200190
//
201191
// This call includes a Sortable option as a memory optimization.
202-
func NewSetWithSortable(kvs []KeyValue, tmp *Sortable) Set {
203-
// Check for empty set.
204-
if len(kvs) == 0 {
205-
return empty()
206-
}
207-
s, _ := NewSetWithSortableFiltered(kvs, tmp, nil)
192+
//
193+
// Deprecated: Use [NewSet] instead.
194+
func NewSetWithSortable(kvs []KeyValue, _ *Sortable) Set {
195+
s, _ := NewSetWithFiltered(kvs, nil)
208196
return s
209197
}
210198

@@ -218,48 +206,12 @@ func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) {
218206
if len(kvs) == 0 {
219207
return empty(), nil
220208
}
221-
srt := sortables.Get().(*Sortable)
222-
s, filtered := NewSetWithSortableFiltered(kvs, srt, filter)
223-
sortables.Put(srt)
224-
return s, filtered
225-
}
226-
227-
// NewSetWithSortableFiltered returns a new Set.
228-
//
229-
// Duplicate keys are eliminated by taking the last value. This
230-
// re-orders the input slice so that unique last-values are contiguous
231-
// at the end of the slice.
232-
//
233-
// This ensures the following:
234-
//
235-
// - Last-value-wins semantics
236-
// - Caller sees the reordering, but doesn't lose values
237-
// - Repeated call preserve last-value wins.
238-
//
239-
// Note that methods are defined on Set, although this returns Set. Callers
240-
// can avoid memory allocations by:
241-
//
242-
// - allocating a Sortable for use as a temporary in this method
243-
// - allocating a Set for storing the return value of this constructor.
244-
//
245-
// The result maintains a cache of encoded attributes, by attribute.EncoderID.
246-
// This value should not be copied after its first use.
247-
//
248-
// The second []KeyValue return value is a list of attributes that were
249-
// excluded by the Filter (if non-nil).
250-
func NewSetWithSortableFiltered(kvs []KeyValue, tmp *Sortable, filter Filter) (Set, []KeyValue) {
251-
// Check for empty set.
252-
if len(kvs) == 0 {
253-
return empty(), nil
254-
}
255-
256-
*tmp = kvs
257209

258210
// Stable sort so the following de-duplication can implement
259211
// last-value-wins semantics.
260-
sort.Stable(tmp)
261-
262-
*tmp = nil
212+
slices.SortStableFunc(kvs, func(a, b KeyValue) int {
213+
return cmp.Compare(a.Key, b.Key)
214+
})
263215

264216
position := len(kvs) - 1
265217
offset := position - 1
@@ -287,6 +239,35 @@ func NewSetWithSortableFiltered(kvs []KeyValue, tmp *Sortable, filter Filter) (S
287239
return Set{equivalent: computeDistinct(kvs)}, nil
288240
}
289241

242+
// NewSetWithSortableFiltered returns a new Set.
243+
//
244+
// Duplicate keys are eliminated by taking the last value. This
245+
// re-orders the input slice so that unique last-values are contiguous
246+
// at the end of the slice.
247+
//
248+
// This ensures the following:
249+
//
250+
// - Last-value-wins semantics
251+
// - Caller sees the reordering, but doesn't lose values
252+
// - Repeated call preserve last-value wins.
253+
//
254+
// Note that methods are defined on Set, although this returns Set. Callers
255+
// can avoid memory allocations by:
256+
//
257+
// - allocating a Sortable for use as a temporary in this method
258+
// - allocating a Set for storing the return value of this constructor.
259+
//
260+
// The result maintains a cache of encoded attributes, by attribute.EncoderID.
261+
// This value should not be copied after its first use.
262+
//
263+
// The second []KeyValue return value is a list of attributes that were
264+
// excluded by the Filter (if non-nil).
265+
//
266+
// Deprecated: Use [NewSetWithFiltered] instead.
267+
func NewSetWithSortableFiltered(kvs []KeyValue, _ *Sortable, filter Filter) (Set, []KeyValue) {
268+
return NewSetWithFiltered(kvs, filter)
269+
}
270+
290271
// filteredToFront filters slice in-place using keep function. All KeyValues that need to
291272
// be removed are moved to the front. All KeyValues that need to be kept are
292273
// moved (in-order) to the back. The index for the first KeyValue to be kept is

attribute/set_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -356,3 +356,22 @@ func BenchmarkFiltering(b *testing.B) {
356356
b.Run("Filtered", benchFn(func(kv attribute.KeyValue) bool { return kv.Key == "A" }))
357357
b.Run("AllDropped", benchFn(func(attribute.KeyValue) bool { return false }))
358358
}
359+
360+
var sinkSet attribute.Set
361+
362+
func BenchmarkNewSet(b *testing.B) {
363+
attrs := []attribute.KeyValue{
364+
attribute.String("B1", "2"),
365+
attribute.String("C2", "5"),
366+
attribute.String("B3", "2"),
367+
attribute.String("C4", "1"),
368+
attribute.String("A5", "4"),
369+
attribute.String("C6", "3"),
370+
attribute.String("A7", "1"),
371+
}
372+
b.ReportAllocs()
373+
b.ResetTimer()
374+
for i := 0; i < b.N; i++ {
375+
sinkSet = attribute.NewSet(attrs...)
376+
}
377+
}

0 commit comments

Comments
 (0)