Skip to content

Commit cb51dc9

Browse files
committed
Enable accessing underlying Message in TableView
1 parent f476814 commit cb51dc9

File tree

3 files changed

+122
-11
lines changed

3 files changed

+122
-11
lines changed

pulsar/table_view.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,18 @@ type TableView interface {
5656
// ContainsKey returns true if this TableView contains a mapping for the specified key.
5757
ContainsKey(key string) bool
5858

59-
// Get returns the value to which the specified key is mapped, or nil if this map contains no mapping for the key.
59+
// Get returns the value to which the specified key is mapped, or nil if this map contains no mapping for the key or the Message cannot be encoded to the SchemaValueType
6060
Get(key string) interface{}
6161

62-
// Entries returns a map view of the mappings contained in this TableView.
62+
// Message returns the Message to which the specified key is mapped, or nil if this map contains no mapping for the key.
63+
Message(key string) Message
64+
65+
// Entries returns a map view of the mappings contained in this TableView, with values encoded into SchemaValueType.
6366
Entries() map[string]interface{}
6467

68+
// Messages returns a map view of the Message mappings contained in this TableView.
69+
Messages() map[string]Message
70+
6571
// Keys returns a slice of the keys contained in this TableView.
6672
Keys() []string
6773

pulsar/table_view_impl.go

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ type TableViewImpl struct {
3939
options TableViewOptions
4040

4141
dataMu sync.Mutex
42-
data map[string]interface{}
42+
data map[string]Message
4343

4444
readersMu sync.Mutex
4545
cancelRaders map[string]cancelReader
@@ -75,7 +75,7 @@ func newTableView(client *client, options TableViewOptions) (TableView, error) {
7575
tv := TableViewImpl{
7676
client: client,
7777
options: options,
78-
data: make(map[string]interface{}),
78+
data: make(map[string]Message),
7979
cancelRaders: make(map[string]cancelReader),
8080
logger: logger,
8181
closedCh: make(chan struct{}),
@@ -178,6 +178,23 @@ func (tv *TableViewImpl) ContainsKey(key string) bool {
178178
}
179179

180180
func (tv *TableViewImpl) Get(key string) interface{} {
181+
tv.dataMu.Lock()
182+
defer tv.dataMu.Unlock()
183+
msg, ok := tv.data[key]
184+
if !ok {
185+
return nil
186+
}
187+
188+
v, err := tv.schemaValueFromMessage(msg)
189+
if err != nil {
190+
tv.logger.Errorf("getting value for message, %w; msg is %v", err, msg)
191+
return nil
192+
}
193+
194+
return v
195+
}
196+
197+
func (tv *TableViewImpl) Message(key string) Message {
181198
tv.dataMu.Lock()
182199
defer tv.dataMu.Unlock()
183200
return tv.data[key]
@@ -186,10 +203,23 @@ func (tv *TableViewImpl) Get(key string) interface{} {
186203
func (tv *TableViewImpl) Entries() map[string]interface{} {
187204
tv.dataMu.Lock()
188205
defer tv.dataMu.Unlock()
206+
189207
data := make(map[string]interface{}, len(tv.data))
190-
for k, v := range tv.data {
208+
for k, msg := range tv.data {
209+
v, err := tv.schemaValueFromMessage(msg)
210+
if err != nil {
211+
tv.logger.Errorf("getting value for message, %w; msg is %v", len(tv.listeners), err, msg)
212+
continue
213+
}
191214
data[k] = v
192215
}
216+
217+
return data
218+
}
219+
220+
func (tv *TableViewImpl) Messages() map[string]Message {
221+
tv.dataMu.Lock()
222+
defer tv.dataMu.Unlock()
193223
return tv.data
194224
}
195225

@@ -245,23 +275,32 @@ func (tv *TableViewImpl) handleMessage(msg Message) {
245275
tv.dataMu.Lock()
246276
defer tv.dataMu.Unlock()
247277

248-
payload := reflect.New(tv.options.SchemaValueType)
249278
if len(msg.Payload()) == 0 {
250279
delete(tv.data, msg.Key())
251280
} else {
252-
if err := msg.GetSchemaValue(payload.Interface()); err != nil {
253-
tv.logger.Errorf("msg.GetSchemaValue() failed with %v; msg is %v", err, msg)
254-
}
255-
tv.data[msg.Key()] = reflect.Indirect(payload).Interface()
281+
tv.data[msg.Key()] = msg
256282
}
257283

284+
v, err := tv.schemaValueFromMessage(msg)
285+
if err != nil {
286+
tv.logger.Errorf("will not action %d listeners, getting value for message, %w; msg is %v", len(tv.listeners), err, msg)
287+
return
288+
}
258289
for _, listener := range tv.listeners {
259-
if err := listener(msg.Key(), reflect.Indirect(payload).Interface()); err != nil {
290+
if err := listener(msg.Key(), v); err != nil {
260291
tv.logger.Errorf("table view listener failed for %v: %w", msg, err)
261292
}
262293
}
263294
}
264295

296+
func (tv *TableViewImpl) schemaValueFromMessage(msg Message) (interface{}, error) {
297+
payload := reflect.New(tv.options.SchemaValueType)
298+
if err := msg.GetSchemaValue(payload.Interface()); err != nil {
299+
return nil, fmt.Errorf("msg.GetSchemaValue() failed: %w", err)
300+
}
301+
return reflect.Indirect(payload).Interface(), nil
302+
}
303+
265304
func (tv *TableViewImpl) watchReaderForNewMessages(ctx context.Context, reader Reader) {
266305
for {
267306
msg, err := reader.Next(ctx)

pulsar/table_view_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,72 @@ func TestTableView(t *testing.T) {
8181
}
8282
}
8383

84+
func TestTableView_Message(t *testing.T) {
85+
client, err := NewClient(ClientOptions{
86+
URL: lookupURL,
87+
})
88+
89+
assert.NoError(t, err)
90+
defer client.Close()
91+
92+
topic := newTopicName()
93+
schema := NewStringSchema(nil)
94+
95+
// Create producer
96+
producer, err := client.CreateProducer(ProducerOptions{
97+
Topic: topic,
98+
Schema: schema,
99+
})
100+
assert.NoError(t, err)
101+
defer producer.Close()
102+
103+
numMsg := 10
104+
valuePrefix := "hello table view: "
105+
publicationTimeForKey := map[string]time.Time{}
106+
keys := make([]string, 0, numMsg)
107+
108+
for i := 0; i < numMsg; i++ {
109+
key := fmt.Sprintf("%d", i)
110+
keys = append(keys, key)
111+
112+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
113+
defer cancel()
114+
_, err = producer.Send(ctx, &ProducerMessage{
115+
Key: key,
116+
Value: fmt.Sprintf(valuePrefix + key),
117+
})
118+
assert.NoError(t, err)
119+
120+
publicationTimeForKey[key] = time.Now()
121+
}
122+
123+
// Create table view
124+
v := ""
125+
tv, err := client.CreateTableView(TableViewOptions{
126+
Topic: topic,
127+
Schema: schema,
128+
SchemaValueType: reflect.TypeOf(&v),
129+
})
130+
assert.NoError(t, err)
131+
defer tv.Close()
132+
133+
// Wait until table view receives all messages
134+
for tv.Size() < numMsg {
135+
time.Sleep(time.Second * 500)
136+
t.Logf("TableView number of elements: %d", tv.Size())
137+
}
138+
139+
for _, k := range keys {
140+
msg := tv.Message(k)
141+
142+
// Check that the payload can be accessed as bytes
143+
assert.Equal(t, []byte(fmt.Sprintf("%s%s", valuePrefix, k)), msg.Payload())
144+
145+
// Check publication times can be accessed and are close to the recorded times above
146+
assert.WithinDuration(t, publicationTimeForKey[k], msg.PublishTime(), time.Millisecond*10)
147+
}
148+
}
149+
84150
func TestTableViewSchemas(t *testing.T) {
85151
var tests = []struct {
86152
name string

0 commit comments

Comments
 (0)