Replace e50f4d164 with query IDs so that we properly handle deletions. See e50f4d164 for full details on the bug that this fixes.

This commit is contained in:
David Dworken 2023-10-25 20:07:09 -07:00
parent e50f4d164b
commit 93cffd98b4
No known key found for this signature in database

View File

@ -31,6 +31,13 @@ const PADDED_NUM_ENTRIES = TABLE_HEIGHT * 5
var CURRENT_QUERY_FOR_HIGHLIGHTING string = "" var CURRENT_QUERY_FOR_HIGHLIGHTING string = ""
var SELECTED_COMMAND string = "" var SELECTED_COMMAND string = ""
// Globally shared monotonically increasing IDs used to prevent race conditions in handling async queries.
// If the user types 'l' and then 's', two queries will be dispatched: One for 'l' and one for 'ls'. These
// counters are used to ensure that we don't process the query results for 'ls' and then promptly overwrite
// them with the results for 'l'.
var LAST_DISPATCHED_QUERY_ID = 0
var LAST_PROCESSED_QUERY_ID = -1
var baseStyle = lipgloss.NewStyle(). var baseStyle = lipgloss.NewStyle().
BorderStyle(lipgloss.NormalBorder()). BorderStyle(lipgloss.NormalBorder()).
BorderForeground(lipgloss.Color("240")) BorderForeground(lipgloss.Color("240"))
@ -183,8 +190,8 @@ type bannerMsg struct {
banner string banner string
} }
type asyncQueryFinishedMsg struct { type asyncQueryFinishedMsg struct {
// The query that finished running. Used to ensure that we only process this message if it matches the current query. // The query ID finished running. Used to ensure that we only process this message if it is the latest query to finish.
searchedQuery string queryId int
// The table rows and entries // The table rows and entries
rows []table.Row rows []table.Row
entries []*data.HistoryEntry entries []*data.HistoryEntry
@ -275,9 +282,11 @@ func runQueryAndUpdateTable(m model, forceUpdateTable, maintainCursor bool) tea.
if m.runQuery != nil { if m.runQuery != nil {
query = *m.runQuery query = *m.runQuery
} }
queryId := LAST_DISPATCHED_QUERY_ID
LAST_DISPATCHED_QUERY_ID += 1
return func() tea.Msg { return func() tea.Msg {
rows, entries, searchErr := getRows(m.ctx, hctx.GetConf(m.ctx).DisplayedColumns, query, PADDED_NUM_ENTRIES) rows, entries, searchErr := getRows(m.ctx, hctx.GetConf(m.ctx).DisplayedColumns, query, PADDED_NUM_ENTRIES)
return asyncQueryFinishedMsg{query, rows, entries, searchErr, forceUpdateTable, maintainCursor, nil} return asyncQueryFinishedMsg{queryId, rows, entries, searchErr, forceUpdateTable, maintainCursor, nil}
} }
} }
return nil return nil
@ -349,7 +358,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.isLoading = false m.isLoading = false
return m, nil return m, nil
case asyncQueryFinishedMsg: case asyncQueryFinishedMsg:
if m.runQuery != nil && *m.runQuery == msg.searchedQuery { if msg.queryId > LAST_PROCESSED_QUERY_ID {
LAST_PROCESSED_QUERY_ID = msg.queryId
m = updateTable(m, msg.rows, msg.entries, msg.searchErr, msg.forceUpdateTable, msg.maintainCursor) m = updateTable(m, msg.rows, msg.entries, msg.searchErr, msg.forceUpdateTable, msg.maintainCursor)
if msg.overriddenSearchQuery != nil { if msg.overriddenSearchQuery != nil {
m.queryInput.SetValue(*msg.overriddenSearchQuery) m.queryInput.SetValue(*msg.overriddenSearchQuery)
@ -391,6 +401,7 @@ func (m model) View() string {
if m.isLoading { if m.isLoading {
loadingMessage = fmt.Sprintf("%s Loading hishtory entries from other devices...", m.spinner.View()) loadingMessage = fmt.Sprintf("%s Loading hishtory entries from other devices...", m.spinner.View())
} }
// TODO: There is a bug where it is needed to add a new line between warnings
warning := "" warning := ""
if m.isOffline { if m.isOffline {
warning += "Warning: failed to contact the hishtory backend (are you offline?), so some results may be stale" warning += "Warning: failed to contact the hishtory backend (are you offline?), so some results may be stale"
@ -693,14 +704,16 @@ func TuiQuery(ctx context.Context, initialQuery string) error {
p := tea.NewProgram(initialModel(ctx, initialQuery), tea.WithOutput(os.Stderr)) p := tea.NewProgram(initialModel(ctx, initialQuery), tea.WithOutput(os.Stderr))
// Async: Get the initial set of rows // Async: Get the initial set of rows
go func() { go func() {
queryId := LAST_DISPATCHED_QUERY_ID
LAST_DISPATCHED_QUERY_ID++
rows, entries, err := getRows(ctx, hctx.GetConf(ctx).DisplayedColumns, initialQuery, PADDED_NUM_ENTRIES) rows, entries, err := getRows(ctx, hctx.GetConf(ctx).DisplayedColumns, initialQuery, PADDED_NUM_ENTRIES)
if err == nil || initialQuery == "" { if err == nil || initialQuery == "" {
p.Send(asyncQueryFinishedMsg{searchedQuery: initialQuery, rows: rows, entries: entries, searchErr: err, forceUpdateTable: true, maintainCursor: false, overriddenSearchQuery: nil}) p.Send(asyncQueryFinishedMsg{queryId: queryId, rows: rows, entries: entries, searchErr: err, forceUpdateTable: true, maintainCursor: false, overriddenSearchQuery: nil})
} else { } else {
// initialQuery is likely invalid in some way, let's just drop it // initialQuery is likely invalid in some way, let's just drop it
emptyQuery := "" emptyQuery := ""
rows, entries, err := getRows(ctx, hctx.GetConf(ctx).DisplayedColumns, emptyQuery, PADDED_NUM_ENTRIES) rows, entries, err := getRows(ctx, hctx.GetConf(ctx).DisplayedColumns, emptyQuery, PADDED_NUM_ENTRIES)
p.Send(asyncQueryFinishedMsg{searchedQuery: emptyQuery, rows: rows, entries: entries, searchErr: err, forceUpdateTable: true, maintainCursor: false, overriddenSearchQuery: &emptyQuery}) p.Send(asyncQueryFinishedMsg{queryId: queryId, rows: rows, entries: entries, searchErr: err, forceUpdateTable: true, maintainCursor: false, overriddenSearchQuery: &emptyQuery})
} }
}() }()
// Async: Retrieve additional entries from the backend // Async: Retrieve additional entries from the backend