Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add a message journal #116

Merged
merged 1 commit into from
Jan 2, 2024

Conversation

DuckBoss
Copy link
Contributor

@DuckBoss DuckBoss commented May 3, 2023

This feature adds a message journal that tracks dispatched messages and worker events.
The yggctl command added to enable the retrieval of the message journal can also be filtered
by the message id or the worker name, and optional data can be truncated by a user specified length.

Note: Do not merge this before the following blocking PRs:

Features:

  • New yggctl comand: message-journal
    • Usage: $ yggctl message-journal <opts>
    • User specified options to filter journal entries:
      • --message-id <my_message_id>: Only displays journal entries that have the specified message id.
      • --worker <my_worker>: Only displays journal entries that have the specified worker.
      • --since <utc timestamp>: Only displays journal entries of events created after the specified timestamp.
      • --until <utc timestamp>: Only displays journal entries of events created up until the specified timestamp.
      • --persistent: Displays journal entries from the current and all previous yggdrasil sessions. By default, the message journal only shows entries from the active session.

Example Usage:
Input: yggctl message-journal
Output:

MESSAGE # MESSAGE ID WORKER NAME RESPONSE TO WORKER EVENT WORKER DATA
0 message-id-0 echo ... BEGIN { }
1 message-id-1 echo ... WORKING { "message": "hello, world!" }
2 message-id-2 echo ... END { }

@DuckBoss DuckBoss force-pushed the jajerome/message-journal branch 2 times, most recently from d4398a1 to cb26a4d Compare May 3, 2023 22:29
@DuckBoss DuckBoss marked this pull request as ready for review May 3, 2023 22:31
Copy link
Contributor

@jirihnidek jirihnidek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have only one request ATM. I will probably have more next week.

cmd/yggctl/main.go Outdated Show resolved Hide resolved
@DuckBoss DuckBoss force-pushed the jajerome/message-journal branch 2 times, most recently from 31eb3da to 82a9b6d Compare May 5, 2023 19:39
Copy link
Collaborator

@subpop subpop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, this is a great start. I'm focusing a lot of my review on the D-Bus method definition for now, since that's where the real API boundary exists. I do have some reservations about using an in-memory slice to store all the messages; that could grow significantly over the course of a long-running process like yggd, so we might need to reconsider the implementation of the journal.

&cli.StringFlag{
Name: "truncate-message",
Aliases: []string{"t"},
Usage: "Truncates worker event messages if they exceed the specified character count (10 by default).",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A default value can automatically be included in the help output by using the Value field of a Flag. That sets the default value returned by c.String("truncate-message") as well, so you don't need to check the flag value later.

dbus/com.redhat.Yggdrasil1.xml Outdated Show resolved Hide resolved
dbus/com.redhat.Yggdrasil1.xml Outdated Show resolved Hide resolved
internal/work/dispatcher.go Outdated Show resolved Hide resolved
dbus/com.redhat.Yggdrasil1.xml Outdated Show resolved Hide resolved
@DuckBoss DuckBoss force-pushed the jajerome/message-journal branch from 82a9b6d to 97fd6e2 Compare May 9, 2023 19:05
Copy link
Contributor

@jirihnidek jirihnidek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have only few small requests ATM. I will test this more tomorrow.

cmd/yggctl/actions.go Show resolved Hide resolved
cmd/yggctl/actions.go Show resolved Hide resolved
cmd/yggctl/actions.go Show resolved Hide resolved
@DuckBoss DuckBoss force-pushed the jajerome/message-journal branch 2 times, most recently from da86f9d to 240abe7 Compare May 25, 2023 16:54
@DuckBoss
Copy link
Contributor Author

DuckBoss commented May 25, 2023

Summary of changes made since last review:

  • updated in-memory message journal to use sqlite file to handle runtime and persistent journal entries.
    • The runtime table is cleared every time yggd service is started.
    • The persistent table keeps journal entries across sessions.
  • new filtering flags: --persistent, --from, --to
    • --persistent flag retrieves journal entries from the persistent table that stores journal entries across multiple yggd sessions. By default, without the flag, journal entries are retrieved from the current session.
    • --from: filters journal entries starting from the datetime specified.
    • --to: filters journal entries up to the datetime specified.
  • adds yggd service flag --enable-message-journal
  • adds yggd config option enable-message-journal
  • moved message journal code out of yggd service client into a separate package.
  • Added some basic test cases for the updated message journal.

Changes based on feedback:

[subpop feedback]

  • fixed dbus function name mismatch 77a934d
  • fixed dbus function mismatch with return type 77a934d
  • dbus spec update to handle journal filtering on the service instead of the yggctl client. 77a934d
  • added default value 15 to yggctl flag truncate-message 77a934d

[jirihnidek feedback]

  • improved dbus session error handling. 72053a4

Other:

  • Rebased on 5/25/2023

@DuckBoss DuckBoss force-pushed the jajerome/message-journal branch 2 times, most recently from 6269089 to 8c916ea Compare May 25, 2023 19:45
Copy link
Contributor

@jirihnidek jirihnidek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. It looks promising. 👍 I tried to test it and I crashed the yggd. :-) More details in comments. I have several comments and requests.

cmd/yggctl/main.go Outdated Show resolved Hide resolved
continue
}
event.Message = eventMessage
workerMessageID = eventId
case ipc.WorkerEventNameBegin:
eventID, ok := s.Body[1].(string)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct. You are trying to access s.Body[1] without checking the length of s.Body first. You can shoot down yggd, when you sent following command over MQTT:

go run ./cmd/yggctl generate data-message --directive echo "hello" |     pub -broker tcp://localhost:1883 -topic yggdrasil/$(hostname)/data/in

When I run testing echo worker, then I'm getting following "traceback" on terminal with yggd:

[jhnidek@thinkpad-p1 yggdrasil]$ go run ./cmd/yggd --server tcp://localhost:1883 --log-level trace --client-id $(hostname)
[yggd] 2023/05/29 09:47:00 /home/jhnidek/github/redhat-insights/yggdrasil/cmd/yggd/main.go:192: starting yggd version 
[yggd] 2023/05/29 09:47:00 /home/jhnidek/github/redhat-insights/yggdrasil/internal/work/dispatcher.go:68: connecting to session bus for worker IPC: unix:path=/run/user/1000/bus
[yggd] 2023/05/29 09:47:00 /home/jhnidek/github/redhat-insights/yggdrasil/internal/work/dispatcher.go:95: exported /com/redhat/Yggdrasil1/Dispatcher1 on bus
[yggd] 2023/05/29 09:47:00 /home/jhnidek/github/redhat-insights/yggdrasil/cmd/yggd/client.go:150: connecting to session bus: unix:path=/run/user/1000/bus
[yggd] 2023/05/29 09:47:00 /home/jhnidek/github/redhat-insights/yggdrasil/cmd/yggd/client.go:175: exported /com/redhat/Yggdrasil1 on bus
[yggd] 2023/05/29 09:47:00 /home/jhnidek/github/redhat-insights/yggdrasil/internal/transport/mqtt.go:51: connected to broker: tcp://localhost:1883
[yggd] 2023/05/29 09:47:00 /home/jhnidek/github/redhat-insights/yggdrasil/cmd/yggd/main.go:267: no TLSconfig, disabling TLS watcher update.
[yggd] 2023/05/29 09:47:00 /home/jhnidek/github/redhat-insights/yggdrasil/internal/transport/mqtt.go:73: subscribed to topic: yggdrasil/thinkpad-p1/data/in
[yggd] 2023/05/29 09:47:00 /home/jhnidek/github/redhat-insights/yggdrasil/internal/transport/mqtt.go:86: subscribed to topic: yggdrasil/thinkpad-p1/control/in
[yggd] 2023/05/29 09:47:00 /home/jhnidek/github/redhat-insights/yggdrasil/internal/transport/mqtt.go:174: published message to topic yggdrasil/thinkpad-p1/control/out
[yggd] 2023/05/29 09:47:00 /home/jhnidek/github/redhat-insights/yggdrasil/internal/transport/mqtt.go:175: message: {"type":"connection-status","message_id":"6a5cc967-6b61-497a-8a66-9e95eaf32147","response_to":"","version":1,"sent":"2023-05-29T09:47:00.442261841+02:00","content":{"canonical_facts":null,"dispatchers":{},"state":"online"}}
[yggd] 2023/05/29 09:47:05 /home/jhnidek/github/redhat-insights/yggdrasil/internal/work/dispatcher.go:244: send message ddd96e7e-daaa-4cf6-8e36-852bacd0fc84 to worker echo
[yggd] 2023/05/29 09:47:05 /home/jhnidek/github/redhat-insights/yggdrasil/internal/work/dispatcher.go:113: received signal: &dbus.Signal{Sender:":1.433", Path:"/com/redhat/Yggdrasil1/Worker1/echo", Name:"com.redhat.Yggdrasil1.Worker1.Event", Body:[]interface {}{0x1}, Sequence:0x8}
[yggd] 2023/05/29 09:47:05 /home/jhnidek/github/redhat-insights/yggdrasil/internal/work/dispatcher.go:271: cannot add journal entry: message journal not enabled
[yggd] 2023/05/29 09:47:05 /home/jhnidek/github/redhat-insights/yggdrasil/internal/transport/mqtt.go:174: published message to topic yggdrasil/thinkpad-p1/control/out
[yggd] 2023/05/29 09:47:05 /home/jhnidek/github/redhat-insights/yggdrasil/internal/transport/mqtt.go:175: message: {"type":"connection-status","message_id":"0f0cdcbf-2fa1-4e6d-9116-411dac290912","response_to":"","version":1,"sent":"2023-05-29T09:47:05.940491216+02:00","content":{"canonical_facts":null,"dispatchers":{"echo":{"DispatchedAt":"","Version":"1"}},"state":"online"}}
panic: runtime error: index out of range [1] with length 1

goroutine 53 [running]:
github.com/redhatinsights/yggdrasil/internal/work.(*Dispatcher).Connect.func1()
	/home/jhnidek/github/redhat-insights/yggdrasil/internal/work/dispatcher.go:157 +0xaa5
created by github.com/redhatinsights/yggdrasil/internal/work.(*Dispatcher).Connect
	/home/jhnidek/github/redhat-insights/yggdrasil/internal/work/dispatcher.go:111 +0x64a
exit status 2

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is correct; the "message" field of an Event is nullable. It's not clearly
annotated as such in the Interface specification, but perhaps it should be.

Event:
@name: Name of the event.
@message: Optional message included with the event.

internal/work/dispatcher.go Outdated Show resolved Hide resolved
cmd/yggctl/actions.go Outdated Show resolved Hide resolved
internal/messagejournal/messagejournal.go Outdated Show resolved Hide resolved
internal/messagejournal/messagejournal.go Outdated Show resolved Hide resolved
internal/messagejournal/messagejournal.go Outdated Show resolved Hide resolved
internal/messagejournal/messagejournal.go Outdated Show resolved Hide resolved
internal/messagejournal/messagejournal.go Outdated Show resolved Hide resolved
internal/messagejournal/messagejournal.go Outdated Show resolved Hide resolved
@DuckBoss DuckBoss force-pushed the jajerome/message-journal branch from f850a14 to 5208d8b Compare June 1, 2023 15:01
@DuckBoss DuckBoss requested a review from jirihnidek June 1, 2023 17:30
Copy link
Collaborator

@subpop subpop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! I looked over all the changes and have some feedback. Excellent progress though; I like this way this is coming together. What is your reasoning behind two tables in the database? Why track persistent and ephemeral messages separately?

internal/messagejournal/messagejournal.go Outdated Show resolved Hide resolved
internal/messagejournal/messagejournal.go Outdated Show resolved Hide resolved
internal/messagejournal/messagejournal.go Outdated Show resolved Hide resolved
internal/messagejournal/messagejournal.go Outdated Show resolved Hide resolved
internal/messagejournal/messagejournal.go Outdated Show resolved Hide resolved
internal/work/dispatcher.go Outdated Show resolved Hide resolved
cmd/yggd/main.go Outdated Show resolved Hide resolved
cmd/yggctl/actions.go Outdated Show resolved Hide resolved
cmd/yggctl/actions.go Outdated Show resolved Hide resolved
cmd/yggctl/actions.go Outdated Show resolved Hide resolved
@DuckBoss
Copy link
Contributor Author

DuckBoss commented Jun 7, 2023

This is great! I looked over all the changes and have some feedback. Excellent progress though; I like this way this is coming together. What is your reasoning behind two tables in the database? Why track persistent and ephemeral messages separately?

I think my reasoning was to maintain simplicity down the line if persistent message entries should become an optional feature that can be turned on/off while still keeping the temporary entries enabled. Apart from that I think it was to just maintain a logical separation, although this wouldn't take much to make into a single table if required.

EDIT: After further experimentation, I decided to just merge the runtime/persistent tables into a single table. It didn't make much sense to turn off persistent journal entries specifically when the message journal is enabled especially since yggdrasil doesn't have any logic to clear tables on exit -- only on start up.

@DuckBoss DuckBoss force-pushed the jajerome/message-journal branch 5 times, most recently from 020e642 to 78c3b88 Compare June 15, 2023 16:36
Copy link
Contributor

@jirihnidek jirihnidek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for update 👍 I have few comments, questions and requests.

cmd/yggd/main.go Outdated Show resolved Hide resolved
cmd/yggd/main.go Outdated Show resolved Hide resolved
cmd/yggd/main.go Outdated Show resolved Hide resolved
cmd/yggd/main.go Outdated Show resolved Hide resolved
cmd/yggd/main.go Outdated Show resolved Hide resolved
cmd/yggctl/main.go Show resolved Hide resolved
<arg type="s" name="message_id" direction="in" />
<arg type="s" name="worker" direction="in" />
<arg type="s" name="from" direction="in" />
<arg type="s" name="to" direction="in" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"from-time" "to-time" ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, 'from' and 'to' should be clarified. I like from-time and to-time. Another option is the English words "since" and "until". Those have a similar "from time" and "to time" association, respectively.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer since/until too.. it's less jarring to read in the --help output.

"worker_event": ipc.WorkerEventName(workerEvent).String(),
"worker_message": workerEventMessage,
}
entries = append(entries, newMessage)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be very slow, when there will be thousands of entries. Have you tested this case?

Copy link
Contributor Author

@DuckBoss DuckBoss Jun 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it could slow down with thousands of entries, but I was hoping the addition of filters would prevent a case where thousands of events would have to be retrieved at a single time (specifically the message-id or to/sent filters).

I'm not sure, is there an alternative way to handle this that you think would work well? 🤔

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about this. I wonder if there's a way to defer some of the
aggregation of this function further up the call stack. Maybe instead of
appending each entry to a slice within GetEntries and returning it at the end,
GetEntries could accept a callback function that gets called on each entry as
its read from the database. That passes the responsibility of aggregating the
results (or not) to the caller. I don't know what advantage that really brings
though, since eventually, this map gets serialized into a D-Bus message and
passed back across the D-Bus broker.

@@ -0,0 +1 @@
DROP TABLE IF EXISTS journal;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no empty line at the end of the file. Is it expected?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen any functional difference and my editor hasn't complained about it. I can't find any styling guidelines around this case either...maybe just leave it as it is and not add anything extra to migration scripts 🤷

Copy link
Collaborator

@subpop subpop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent progress! I left another round of comments here and there. Thank you for bearing with me on these rounds of reviews.

@@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS journal (
id INTEGER NOT NULL PRIMARY KEY,
message_id VARCHAR(36) NOT NULL,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQLite has a more simple than SQL standard type system:
https://sqlite.org/datatype3.html. It all basically resolves to five types:
TEXT, NUMERIC, INTEGER, REAL, BLOB. You can probably simplify this CREATE
statement to using the SQLite type affinities instead.

CREATE TABLE IF NOT EXISTS journal (
    id INTEGER NOT NULL PRIMARY KEY,
    message_id TEXT NOT NULL,
    sent INTEGER NOT NULL,
    worker_name TEXT NOT NULL,
    response_to TEXT,
    worker_event INTEGER,
    worker_message TEXT,
);


const messageJournalTableName string = "journal"

//go:embed migrations/*.sql
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines 45 to 47
// New initializes a message journal sqlite database consisting
// of a runtime table that gets cleared on every session start
// and a persistent table that maintains journal entries across sessions.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is no longer accurate, given the change in design.

return nil
}

// AddEntry adds a new message journal entry to both the temporary runtime table
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is no longer accurate, given the design changes.

internal/messagejournal/messagejournal.go Show resolved Hide resolved
&cli.StringFlag{
Name: "from",
Aliases: []string{},
Usage: "Only display worker messages and emitted events sent starting from the timestamp provided.",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cli package does have a TimestampFlag. That might help enforce the
proper data type for this flag.

&cli.BoolFlag{
Name: "persistent",
Aliases: []string{"p"},
Usage: "Display worker messages and emitted events gathered across multiple sessions from persistent storage.",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit-pick: Usage strings don't end with periods. They don't need to be complete
sentences. They can be short, descriptive fragments.

Name: "message-journal",
Usage: "Display a list of all worker messages and emitted events",
UsageText: "yggctl message-journal",
Description: "The message-journal command sends a dbus call to yggdrasil to retrieve a message journal containing all worker messages and emitted events.",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

D-Bus is an irrelevant implementation detail from the user's perspective. Since
these strings are meant to be user-facing, this description should tell the user
what the operation will/should do, not necessarily how it accomplishes that
operation.

Suggested change
Description: "The message-journal command sends a dbus call to yggdrasil to retrieve a message journal containing all worker messages and emitted events.",
Description: "The message-journal command retrieves a message list containing all worker messages and emitted events.",

&cli.UintFlag{
Name: "truncate-message",
Aliases: []string{"t"},
Usage: "Truncates worker event messages if they exceed the specified character count.",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to use the BACKTICK notation in Usage text if possible. It helps to
clarify the data types expected in the flag.

if len(journalEntries) == 0 {
fmt.Println("No journal entries found.")
return nil
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. I think there is value to printing a message if no records are retrieved. If
we don't, we'd either print a single line with the table headers or we'd print
nothing at all. In either case, the user would be left wondering if the command
worked correctly. Reporting an error to the user in the case of zero records
avoids that case.

@DuckBoss
Copy link
Contributor Author

DuckBoss commented Sep 22, 2023

Summary of Changes... there's a bunch..

Changes since optional data handling rework:

  • test: updated test cases to use new optional event data changes [d55a3f1]
  • feat: added 'lastUpdated' time to journal [845b100]
  • fix: updated 'since/until' short flags to 's/u' instead of 'ft/tt' for consistency [8b21784]
  • revert: removed --truncate-message filter [9972ee9]
  • style: formatting updates [7aea134]
  • [Link] refactor: updated migrations to use sqlite syntax [2fb6938]
  • fix: added missing Required flag option [d5c393b]
  • feat: added DefaultText flag param [1791ed7]

Changes since last review (before optional data handling rework):

The changes made here have all been squashed into [0efddca] and were made before the implementation
of the new method of handling optional event data.
This section might not be useful for reviewers, but provides a more accurate history of changes.
Additionally, a branch with the previous history (pre-squash) has been cloned and preserved HERE

Based on older feedback [commits squashed to 0efddca]:

  • [Link] refactor: removed unnecessary method return [2b82cd6]
  • [Link/Jiri] refactor: renamed to/from-time CLI flags [c626b3f and c0fa01d]
  • [Link/Jiri] fix: return error if no entries are found [9134ce5]
  • [Link] fix: updated wording in message-journal flag to be more user-friendly [037d3c4]
  • [Link] refactor: updated journal CLI flag to accept a file path [17fd854]

Other changes [commits squashed to 0efddca]:

  • refactor: simplified error variable names [1920021]
  • fix: fixed error message inconsistencies [6f5803b]
  • feat: updated optional worker event data to use a map [0c67496]
  • feat: added response_to field in emitted events [0c1218c and 09492ad]

@DuckBoss DuckBoss requested a review from subpop September 25, 2023 14:44
@DuckBoss DuckBoss force-pushed the jajerome/message-journal branch from 1791ed7 to 90f2fb3 Compare October 3, 2023 19:24
@subpop subpop force-pushed the jajerome/message-journal branch from 90f2fb3 to f617b60 Compare October 11, 2023 13:15
Copy link
Collaborator

@subpop subpop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few small changes here and there; mostly around string content. But great work! I tested the functionality and it works out. I'm going to try and stress test it with some looping message transmission, but I figured I'd get this review back to you to clear up the little bits here and there.

@@ -88,4 +88,4 @@
<arg name="out" direction="out" type="s" />
</method>
</interface>
</node>
</node>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an editor conflict going on. This file's trailing newline got removed.
This change is superfluous and shouldn't be committed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change hasn't been dropped from the PR yet.

<arg type="s" name="worker" direction="in" />
<arg type="s" name="since" direction="in" />
<arg type="s" name="until" direction="in" />
<arg type="s" name="persistent" direction="in" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

persistent is a bool type, not a string type.

Suggested change
<arg type="s" name="persistent" direction="in" />
<arg type="b" name="persistent" direction="in" />

}

type errorJournal struct {
err interface{}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting design choice. Why is err an interface{} type instead of an
error type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Being unfamiliar with this, I think I looked to how errors were tested in other parts of the code and used this as a basis:

type errorTag struct {
v interface{}
}
func (e *errorTag) Error() string {
return fmt.Sprintf("cannot parse '%T' as string", e.v)
}
func (e *errorTag) Is(o error) bool {
return reflect.TypeOf(e) == reflect.TypeOf(o)
}

But you're right, using an error type and then checking for errors in tests like -
wantError: &errorJournal{fmt.Errorf("my error message")} would also work.

values (?,?,?,?,?,?)`

insertAction, err := j.database.Prepare(
fmt.Sprintf(insertEntryTemplate, messageJournalTableName),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the table name a static string and printed into the SQL template? This
could be simplified, couldn't it? Is this left over from the previous design
where there were two tables?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good point, this was left over from the previous design.

// buildDynamicGetEntriesQuery is a utility method that builds the dynamic sql query
// required to filter journal entry messages from the message journal database
// when they are retrieved in the 'GetEntries' method.
func (j *MessageJournal) buildDynamicGetEntriesQuery(filter Filter) (string, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the kind of function that I would like to see coupled with some unit
tests. It's currently declared as a method on MessageJournal, but only because
it uses the initializedAt member field. Adding initializedAt as a function
parameter means you can drop the method to a standalone function. This would
make it more straight forward to write a series of tests, as it becomes a basic
input/output transformation function.

Suggested change
func (j *MessageJournal) buildDynamicGetEntriesQuery(filter Filter) (string, error) {
func buildDynamicGetEntriesQuery(filter Filter, initializedAt time.Time) (string, error) {

&cli.StringFlag{
Name: "worker",
Aliases: []string{"w"},
Usage: "Only display worker messages and emitted events for the specified worker `NAME`",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Usage: "Only display worker messages and emitted events for the specified worker `NAME`",
Usage: "Include only events emitted by `WORKER`",

&cli.StringFlag{
Name: "message-id",
Aliases: []string{"m"},
Usage: "Only display worker messages and emitted events for the entries with the specified message id `STRING`",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Usage: "Only display worker messages and emitted events for the entries with the specified message id `STRING`",
Usage: "Include only events emitted for message ID `STRING`",

&cli.StringFlag{
Name: "since",
Aliases: []string{"s"},
Usage: "Only display worker messages and emitted events sent starting from the provided `TIMESTAMP` (YYYY-MM-DD HH:MM:SS)",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Usage: "Only display worker messages and emitted events sent starting from the provided `TIMESTAMP` (YYYY-MM-DD HH:MM:SS)",
Usage: "Include only events emitted after `TIMESTAMP` (YYYY-MM-DD HH:MM:SS)",

&cli.StringFlag{
Name: "until",
Aliases: []string{"u"},
Usage: "Only display worker messages and emitted events sent up to the provided `TIMESTAMP` (YYYY-MM-DD HH:MM:SS)",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Usage: "Only display worker messages and emitted events sent up to the provided `TIMESTAMP` (YYYY-MM-DD HH:MM:SS)",
Usage: "Include only events emitted before `TIMESTAMP` (YYYY-MM-DD HH:MM:SS)",

Aliases: []string{"f"},
Usage: "Print output in `FORMAT` (json, table or text)",
Value: "table",
DefaultText: "table",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is DefaultText required with Value too? What does DefaultText do?

Copy link
Contributor Author

@DuckBoss DuckBoss Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DefaultText is not strictly required, but it specifies what is selected by default in the --help output:

--format FORMAT, -f FORMAT       Print output in Format (json, table or text) (default: table)

Although it's generally used for when a default value for a flag is computed, I think this is still a good idea to add to any flags that have non-computed default values in yggctl.
Doc reference: https://cli.urfave.org/v2/examples/flags/#default-values-for-help-output

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, interesting. What happens if DefaultText is omitted, but Value is set to a string like "table". Doesn't it use that value as the default in the help text? I interpreted DefaultText as being an option to override the default value's string formatted value in cases like the example when the default is randomized.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see.. I checked this again and it looks like that's the case. It looks like it's fine to remove DefaultText then.

Copy link
Collaborator

@subpop subpop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few small changes here and there; mostly around string content. But great work! I tested the functionality and it works out. I'm going to try and stress test it with some looping message transmission, but I figured I'd get this review back to you to clear up the little bits here and there.

@DuckBoss DuckBoss force-pushed the jajerome/message-journal branch from 84d59dc to e47d50e Compare October 13, 2023 16:06
@DuckBoss
Copy link
Contributor Author

DuckBoss commented Oct 13, 2023

Changes since last review [10/11/2023]

yggctl:

  • Fix: Removed unnecessary flag option DefaultText [d61b1a3]
  • Refactor: Updated message-journal flag descriptions [661327a]

yggd journal:

  • Fix: Fixed an incorrect dbus type string -> bool [ed13ffd]
  • Refactor: Simplified table name usage, removed table name constants [f30768a]
  • Refactor: Updated error struct to use error type [36787f6]
  • Refactor: Renamed journal 'New' method to 'Open' [4492f4f]
  • Refactor: Query builder function moved to be standalone, query string separated into individual lines [3b6dfea]
    • There were extra newline/tab characters in the query so the query template string has been separated into individual lines. This should resolve issues during testing.
  • Tests: Added tests for query builder function [e47d50e]

@DuckBoss DuckBoss requested a review from subpop October 16, 2023 14:41
@DuckBoss DuckBoss force-pushed the jajerome/message-journal branch from e47d50e to 58cc14a Compare November 13, 2023 22:44
Copy link
Collaborator

@subpop subpop left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I re-reviewed this, am all caught up, found a couple of lines I had some questions about, and am about to try stress testing it.

cmd/yggd/main.go Outdated
func setupMessageJournal(client *Client) error {
messageJournalPath := config.DefaultConfig.MessageJournal
if messageJournalPath != "" {
journalFilePath := filepath.Join(messageJournalPath)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the intention of this statement? Calling filepath.Join with one
element seems superfluous. If you want to clean the file path, I concur, but
filepath.Clean might be more appropriate in that case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, not sure why the filepath.Join is still there. It must be another remnant from the previous two-database design. Thanks for catching that.

@@ -88,4 +88,4 @@
<arg name="out" direction="out" type="s" />
</method>
</interface>
</node>
</node>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change hasn't been dropped from the PR yet.

@subpop
Copy link
Collaborator

subpop commented Dec 7, 2023

I hate to ask, but when was the last time you rebased this onto main? It appears to be missing quite a few commits from main. I'd be surprised if go.sum is the only conflict.

@subpop
Copy link
Collaborator

subpop commented Dec 7, 2023

OK. I looped messages at it and it did great. I'm all good with this after it gets a rebase onto main.

@DuckBoss
Copy link
Contributor Author

DuckBoss commented Dec 7, 2023

I hate to ask, but when was the last time you rebased this onto main? It appears to be missing quite a few commits from main. I'd be surprised if go.sum is the only conflict.

I rebased it somewhat recently (I think it's been 3 weeks?) so it should be mostly caught up. I'll rebase it again after I make the final adjustments.

@DuckBoss DuckBoss force-pushed the jajerome/message-journal branch 3 times, most recently from edbe6f0 to 05e0093 Compare December 14, 2023 15:56
@subpop
Copy link
Collaborator

subpop commented Dec 18, 2023

@jirihnidek are your changes addressed? The PR is waiting on your approval, since you made a change request in a previous review.

Copy link
Contributor

@jirihnidek jirihnidek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is only one simple to solve conflict with main branch. When conflict is resolved, then it will get my approval. 👍

@DuckBoss DuckBoss force-pushed the jajerome/message-journal branch from 05e0093 to 17f20cb Compare January 2, 2024 15:59
This feature adds a message journal that tracks dispatched messages
and worker events. This will be useful for diagnostic purposes.
The message journal can be filtered by the message id or the worker name
and worker messages can be truncated by a user specified length.

Signed-off-by: Jason Jerome <jajerome@redhat.com>
@DuckBoss DuckBoss force-pushed the jajerome/message-journal branch from 17f20cb to 5b88bbf Compare January 2, 2024 16:22
Copy link
Contributor

@jirihnidek jirihnidek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, Thanks for all your updates. 👍

@jirihnidek jirihnidek merged commit 9ee405f into RedHatInsights:main Jan 2, 2024
14 checks passed
@DuckBoss DuckBoss deleted the jajerome/message-journal branch January 12, 2024 16:30
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.

3 participants