瀏覽代碼

Merge pull request #1625 from slingamn/cleanup_star.2

fix #1615
tags/v2.7.0-rc1
Shivaram Lingamneni 3 年之前
父節點
當前提交
0b414cb158
沒有連結到貢獻者的電子郵件帳戶。
共有 6 個檔案被更改,包括 87 行新增64 行删除
  1. 28
    12
      irc/client.go
  2. 1
    1
      irc/handlers.go
  3. 2
    7
      irc/histserv.go
  4. 45
    37
      irc/mysql/history.go
  5. 5
    1
      irc/server.go
  6. 6
    6
      irc/znc.go

+ 28
- 12
irc/client.go 查看文件

@@ -987,16 +987,6 @@ func (session *Session) playResume() {
987 987
 			oldestLostMessage = lastDiscarded
988 988
 		}
989 989
 	}
990
-	_, privmsgSeq, _ := server.GetHistorySequence(nil, client, "*")
991
-	if privmsgSeq != nil {
992
-		privmsgs, _ := privmsgSeq.Between(history.Selector{}, history.Selector{}, config.History.ClientLength)
993
-		for _, item := range privmsgs {
994
-			sender := server.clients.Get(NUHToNick(item.Nick))
995
-			if sender != nil {
996
-				friends.Add(sender)
997
-			}
998
-		}
999
-	}
1000 990
 
1001 991
 	timestamp := session.resumeDetails.Timestamp
1002 992
 	gap := oldestLostMessage.Sub(timestamp)
@@ -1052,7 +1042,8 @@ func (session *Session) playResume() {
1052 1042
 	}
1053 1043
 
1054 1044
 	// replay direct PRIVSMG history
1055
-	if !timestamp.IsZero() && privmsgSeq != nil {
1045
+	_, privmsgSeq, err := server.GetHistorySequence(nil, client, "")
1046
+	if !timestamp.IsZero() && err == nil && privmsgSeq != nil {
1056 1047
 		after := history.Selector{Time: timestamp}
1057 1048
 		items, _ := privmsgSeq.Between(after, history.Selector{}, config.History.ZNCMax)
1058 1049
 		if len(items) != 0 {
@@ -1955,7 +1946,7 @@ func (client *Client) listTargets(start, end history.Selector, limit int) (resul
1955 1946
 		extras = append(extras, persistentExtras...)
1956 1947
 	}
1957 1948
 
1958
-	_, cSeq, err := client.server.GetHistorySequence(nil, client, "*")
1949
+	_, cSeq, err := client.server.GetHistorySequence(nil, client, "")
1959 1950
 	if err == nil && cSeq != nil {
1960 1951
 		correspondents, err := cSeq.ListCorrespondents(start, end, limit)
1961 1952
 		if err == nil {
@@ -1967,6 +1958,31 @@ func (client *Client) listTargets(start, end history.Selector, limit int) (resul
1967 1958
 	return results, nil
1968 1959
 }
1969 1960
 
1961
+// latest PRIVMSG from all DM targets
1962
+func (client *Client) privmsgsBetween(startTime, endTime time.Time, targetLimit, messageLimit int) (results []history.Item, err error) {
1963
+	start := history.Selector{Time: startTime}
1964
+	end := history.Selector{Time: endTime}
1965
+	targets, err := client.listTargets(start, end, targetLimit)
1966
+	if err != nil {
1967
+		return
1968
+	}
1969
+	for _, target := range targets {
1970
+		if strings.HasPrefix(target.CfName, "#") {
1971
+			continue
1972
+		}
1973
+		_, seq, err := client.server.GetHistorySequence(nil, client, target.CfName)
1974
+		if err == nil && seq != nil {
1975
+			items, err := seq.Between(start, end, messageLimit)
1976
+			if err == nil {
1977
+				results = append(results, items...)
1978
+			} else {
1979
+				client.server.logger.Error("internal", "error querying privmsg history", client.Nick(), target.CfName, err.Error())
1980
+			}
1981
+		}
1982
+	}
1983
+	return
1984
+}
1985
+
1970 1986
 func (client *Client) handleRegisterTimeout() {
1971 1987
 	client.Quit(fmt.Sprintf("Registration timeout: %v", RegisterTimeout), nil)
1972 1988
 	client.destroy(nil)

+ 1
- 1
irc/handlers.go 查看文件

@@ -1080,7 +1080,7 @@ Get an explanation of <argument>, or "index" for a list of help topics.`), rb)
1080 1080
 
1081 1081
 // HISTORY <target> [<limit>]
1082 1082
 // e.g., HISTORY #ubuntu 10
1083
-// HISTORY me 15
1083
+// HISTORY alice 15
1084 1084
 // HISTORY #darwin 1h
1085 1085
 func historyHandler(server *Server, client *Client, msg ircmsg.Message, rb *ResponseBuffer) bool {
1086 1086
 	config := server.Config()

+ 2
- 7
irc/histserv.go 查看文件

@@ -9,7 +9,6 @@ import (
9 9
 	"os"
10 10
 	"runtime/debug"
11 11
 	"strconv"
12
-	"strings"
13 12
 	"time"
14 13
 
15 14
 	"github.com/oragono/oragono/irc/history"
@@ -71,7 +70,7 @@ the request of the account holder.`,
71 70
 			help: `Syntax: $bPLAY <target> [limit]$b
72 71
 
73 72
 PLAY plays back history messages, rendering them into direct messages from
74
-HistServ. 'target' is a channel name (or 'me' for direct messages), and 'limit'
73
+HistServ. 'target' is a channel name or nickname to query, and 'limit'
75 74
 is a message count or a time duration. Note that message playback may be
76 75
 incomplete or degraded, relative to direct playback from /HISTORY or
77 76
 CHATHISTORY.`,
@@ -206,11 +205,7 @@ func histservPlayHandler(service *ircService, server *Server, client *Client, co
206 205
 
207 206
 // handles parameter parsing and history queries for /HISTORY and /HISTSERV PLAY
208 207
 func easySelectHistory(server *Server, client *Client, params []string) (items []history.Item, channel *Channel, err error) {
209
-	target := params[0]
210
-	if strings.ToLower(target) == "me" {
211
-		target = "*"
212
-	}
213
-	channel, sequence, err := server.GetHistorySequence(nil, client, target)
208
+	channel, sequence, err := server.GetHistorySequence(nil, client, params[0])
214 209
 
215 210
 	if sequence == nil || err != nil {
216 211
 		return nil, nil, errNoSuchChannel

+ 45
- 37
irc/mysql/history.go 查看文件

@@ -193,16 +193,25 @@ func (mysql *MySQL) createTables() (err error) {
193 193
 	}
194 194
 
195 195
 	_, err = mysql.db.Exec(fmt.Sprintf(`CREATE TABLE sequence (
196
-		id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
196
+		history_id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
197 197
 		target VARBINARY(%[1]d) NOT NULL,
198 198
 		nanotime BIGINT UNSIGNED NOT NULL,
199
-		history_id BIGINT NOT NULL,
200
-		KEY (target, nanotime),
201
-		KEY (history_id)
199
+		KEY (target, nanotime)
202 200
 	) CHARSET=ascii COLLATE=ascii_bin;`, MaxTargetLength))
203 201
 	if err != nil {
204 202
 		return err
205 203
 	}
204
+	/* XXX: this table used to be:
205
+	CREATE TABLE sequence (
206
+		id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
207
+		target VARBINARY(%[1]d) NOT NULL,
208
+		nanotime BIGINT UNSIGNED NOT NULL,
209
+		history_id BIGINT NOT NULL,
210
+		KEY (target, nanotime),
211
+		KEY (history_id)
212
+	) CHARSET=ascii COLLATE=ascii_bin;
213
+	Some users may still be using the old schema.
214
+	*/
206 215
 
207 216
 	_, err = mysql.db.Exec(fmt.Sprintf(`CREATE TABLE conversations (
208 217
 		id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
@@ -352,31 +361,32 @@ func (mysql *MySQL) deleteHistoryIDs(ctx context.Context, ids []uint64) (err err
352 361
 
353 362
 func (mysql *MySQL) selectCleanupIDs(ctx context.Context, age time.Duration) (ids []uint64, maxNanotime int64, err error) {
354 363
 	rows, err := mysql.db.QueryContext(ctx, `
355
-		SELECT history.id, sequence.nanotime
364
+		SELECT history.id, sequence.nanotime, conversations.nanotime
356 365
 		FROM history
357 366
 		LEFT JOIN sequence ON history.id = sequence.history_id
367
+		LEFT JOIN conversations on history.id = conversations.history_id
358 368
 		ORDER BY history.id LIMIT ?;`, cleanupRowLimit)
359 369
 	if err != nil {
360 370
 		return
361 371
 	}
362 372
 	defer rows.Close()
363 373
 
364
-	// a history ID may have 0-2 rows in sequence: 1 for a channel entry,
365
-	// 2 for a DM, 0 if the data is inconsistent. therefore, deduplicate
366
-	// and delete anything that doesn't have a sequence entry:
367 374
 	idset := make(map[uint64]struct{}, cleanupRowLimit)
368 375
 	threshold := time.Now().Add(-age).UnixNano()
369 376
 	for rows.Next() {
370 377
 		var id uint64
371
-		var nanotime sql.NullInt64
372
-		err = rows.Scan(&id, &nanotime)
378
+		var seqNano, convNano sql.NullInt64
379
+		err = rows.Scan(&id, &seqNano, &convNano)
373 380
 		if err != nil {
374 381
 			return
375 382
 		}
376
-		if !nanotime.Valid || nanotime.Int64 < threshold {
383
+		nanotime := extractNanotime(seqNano, convNano)
384
+		// returns 0 if not found; in that case the data is inconsistent
385
+		// and we should delete the entry
386
+		if nanotime < threshold {
377 387
 			idset[id] = struct{}{}
378
-			if nanotime.Valid && nanotime.Int64 > maxNanotime {
379
-				maxNanotime = nanotime.Int64
388
+			if nanotime > maxNanotime {
389
+				maxNanotime = nanotime
380 390
 			}
381 391
 		}
382 392
 	}
@@ -675,10 +685,6 @@ func (mysql *MySQL) AddDirectMessage(sender, senderAccount, recipient, recipient
675 685
 	nanotime := item.Message.Time.UnixNano()
676 686
 
677 687
 	if senderAccount != "" {
678
-		err = mysql.insertSequenceEntry(ctx, senderAccount, nanotime, id)
679
-		if err != nil {
680
-			return
681
-		}
682 688
 		err = mysql.insertConversationEntry(ctx, senderAccount, recipient, nanotime, id)
683 689
 		if err != nil {
684 690
 			return
@@ -690,10 +696,6 @@ func (mysql *MySQL) AddDirectMessage(sender, senderAccount, recipient, recipient
690 696
 	}
691 697
 
692 698
 	if recipientAccount != "" && sender != recipient {
693
-		err = mysql.insertSequenceEntry(ctx, recipientAccount, nanotime, id)
694
-		if err != nil {
695
-			return
696
-		}
697 699
 		err = mysql.insertConversationEntry(ctx, recipientAccount, sender, nanotime, id)
698 700
 		if err != nil {
699 701
 			return
@@ -800,31 +802,24 @@ func (mysql *MySQL) Export(account string, writer io.Writer) {
800 802
 }
801 803
 
802 804
 func (mysql *MySQL) lookupMsgid(ctx context.Context, msgid string, includeData bool) (result time.Time, id uint64, data []byte, err error) {
803
-	// in theory, we could optimize out a roundtrip to the database by using a subquery instead:
804
-	// sequence.nanotime > (
805
-	//     SELECT sequence.nanotime FROM sequence, history
806
-	//     WHERE sequence.history_id = history.id AND history.msgid = ?
807
-	//     LIMIT 1)
808
-	// however, this doesn't handle the BETWEEN case with one or two msgids, where we
809
-	// don't initially know whether the interval is going forwards or backwards. to simplify
810
-	// the logic,  resolve msgids to timestamps "manually" in all cases, using a separate query.
811 805
 	decoded, err := decodeMsgid(msgid)
812 806
 	if err != nil {
813 807
 		return
814 808
 	}
815
-	cols := `sequence.nanotime`
809
+	cols := `sequence.nanotime, conversations.nanotime`
816 810
 	if includeData {
817
-		cols = `sequence.nanotime, sequence.history_id, history.data`
811
+		cols = `sequence.nanotime, conversations.nanotime, history.id, history.data`
818 812
 	}
819 813
 	row := mysql.db.QueryRowContext(ctx, fmt.Sprintf(`
820
-		SELECT %s FROM sequence
821
-		INNER JOIN history ON history.id = sequence.history_id
814
+		SELECT %s FROM history
815
+		LEFT JOIN sequence ON history.id = sequence.history_id
816
+		LEFT JOIN conversations ON history.id = conversations.history_id
822 817
 		WHERE history.msgid = ? LIMIT 1;`, cols), decoded)
823
-	var nanotime int64
818
+	var nanoSeq, nanoConv sql.NullInt64
824 819
 	if !includeData {
825
-		err = row.Scan(&nanotime)
820
+		err = row.Scan(&nanoSeq, &nanoConv)
826 821
 	} else {
827
-		err = row.Scan(&nanotime, &id, &data)
822
+		err = row.Scan(&nanoSeq, &nanoConv, &id, &data)
828 823
 	}
829 824
 	if err != sql.ErrNoRows {
830 825
 		mysql.logError("could not resolve msgid to time", err)
@@ -832,11 +827,24 @@ func (mysql *MySQL) lookupMsgid(ctx context.Context, msgid string, includeData b
832 827
 	if err != nil {
833 828
 		return
834 829
 	}
835
-
830
+	nanotime := extractNanotime(nanoSeq, nanoConv)
831
+	if nanotime == 0 {
832
+		err = sql.ErrNoRows
833
+		return
834
+	}
836 835
 	result = time.Unix(0, nanotime).UTC()
837 836
 	return
838 837
 }
839 838
 
839
+func extractNanotime(seq, conv sql.NullInt64) (result int64) {
840
+	if seq.Valid {
841
+		return seq.Int64
842
+	} else if conv.Valid {
843
+		return conv.Int64
844
+	}
845
+	return
846
+}
847
+
840 848
 func (mysql *MySQL) selectItems(ctx context.Context, query string, args ...interface{}) (results []history.Item, err error) {
841 849
 	rows, err := mysql.db.QueryContext(ctx, query, args...)
842 850
 	if mysql.logError("could not select history items", err) {

+ 5
- 1
irc/server.go 查看文件

@@ -862,6 +862,10 @@ func (server *Server) setupListeners(config *Config) (err error) {
862 862
 // we may already know the channel we're querying, or we may have
863 863
 // to look it up via a string query. This function is responsible for
864 864
 // privilege checking.
865
+// XXX: call this with providedChannel==nil and query=="" to get a sequence
866
+// suitable for ListCorrespondents (i.e., this function is still used to
867
+// decide whether the ringbuf or mysql is authoritative about the client's
868
+// message history).
865 869
 func (server *Server) GetHistorySequence(providedChannel *Channel, client *Client, query string) (channel *Channel, sequence history.Sequence, err error) {
866 870
 	config := server.Config()
867 871
 	// 4 cases: {persistent, ephemeral} x {normal, conversation}
@@ -901,7 +905,7 @@ func (server *Server) GetHistorySequence(providedChannel *Channel, client *Clien
901 905
 		}
902 906
 	} else {
903 907
 		status, target = client.historyStatus(config)
904
-		if query != "*" {
908
+		if query != "" {
905 909
 			correspondent, err = CasefoldName(query)
906 910
 			if err != nil {
907 911
 				return

+ 6
- 6
irc/znc.go 查看文件

@@ -18,6 +18,8 @@ const (
18 18
 	zncPlaybackCommandExpiration = time.Second * 30
19 19
 
20 20
 	zncPrefix = "*playback!znc@znc.in"
21
+
22
+	maxDMTargetsForAutoplay = 128
21 23
 )
22 24
 
23 25
 type zncCommandHandler func(client *Client, command string, params []string, rb *ResponseBuffer)
@@ -150,9 +152,7 @@ func zncPlaybackPlayHandler(client *Client, command string, params []string, rb
150 152
 	} else {
151 153
 		targets = make(utils.StringSet)
152 154
 		for _, targetName := range strings.Split(targetString, ",") {
153
-			if targetName == "*self" {
154
-				playPrivmsgs = true
155
-			} else if strings.HasPrefix(targetName, "#") {
155
+			if strings.HasPrefix(targetName, "#") {
156 156
 				if cfTarget, err := CasefoldChannel(targetName); err == nil {
157 157
 					targets.Add(cfTarget)
158 158
 				}
@@ -165,7 +165,7 @@ func zncPlaybackPlayHandler(client *Client, command string, params []string, rb
165 165
 	}
166 166
 
167 167
 	if playPrivmsgs {
168
-		zncPlayPrivmsgs(client, rb, "*", start, end)
168
+		zncPlayPrivmsgs(client, rb, "", start, end)
169 169
 	}
170 170
 
171 171
 	rb.session.zncPlaybackTimes = &zncPlaybackTimes{
@@ -188,13 +188,13 @@ func zncPlaybackPlayHandler(client *Client, command string, params []string, rb
188 188
 	}
189 189
 }
190 190
 
191
-func zncPlayPrivmsgs(client *Client, rb *ResponseBuffer, target string, after, before time.Time) {
191
+func zncPlayPrivmsgs(client *Client, rb *ResponseBuffer, target string, start, end time.Time) {
192 192
 	_, sequence, _ := client.server.GetHistorySequence(nil, client, target)
193 193
 	if sequence == nil {
194 194
 		return
195 195
 	}
196 196
 	zncMax := client.server.Config().History.ZNCMax
197
-	items, err := sequence.Between(history.Selector{Time: after}, history.Selector{Time: before}, zncMax)
197
+	items, err := client.privmsgsBetween(start, end, maxDMTargetsForAutoplay, zncMax)
198 198
 	if err == nil && len(items) != 0 {
199 199
 		client.replayPrivmsgHistory(rb, items, "")
200 200
 	}

Loading…
取消
儲存