Lors de l'exécution de requêtes de base de données, nous rencontrons souvent des requêtes de pagination. Cependant, lorsque les requêtes paginées sont annulées, des inexactitudes temporelles peuvent parfois en résulter. Ce problème est critique pour les applications qui nécessitent un timing précis. Dans cet article, l'éditeur PHP Baicao vous présentera comment résoudre ce problème pour garantir l'exactitude et la précision du timing. Nous explorerons quelques causes possibles et solutions pour vous aider à mieux comprendre et gérer ce problème.
J'ai un objet utilisé pour faire une requête SQL paginée qui permet d'exécuter la requête de manière asynchrone :
type pagedquery[t any] struct { results chan []*t errors chan error done chan error quit chan error client *sql.db } func newpagedquery[t any](client *sql.db) *pagedquery[t] { return &pagedquery[t]{ results: make(chan []*t, 1), errors: make(chan error, 1), done: make(chan error, 1), quit: make(chan error, 1), client: client, } } func (paged *pagedquery[t]) requestasync(ctx context.context, queries ...*query) { conn, err := client.conn(ctx) if err != nil { paged.errors <- err return } defer func() { conn.close() paged.done <- nil }() for i, query := range queries { select { case <-ctx.done(): return case <-paged.quit: return default: } rows, err := conn.querycontext(ctx, query.string, query.arguments...) if err != nil { paged.errors <- err return } data, err := sql.readrows[t](rows) if err != nil { paged.errors <- err return } paged.results <- data } }
J'essaie de tester ce code, en particulier la partie annulation. Mon code de test ressemble à ceci :
svc, mock := createServiceMock("TEST_DATABASE", "TEST_SCHEMA") mock.ExpectQuery(regexp.QuoteMeta("TEST QUERY")). WithArgs(...). WillReturnRows(mock.NewRows([]string{"t", "v", "o", "c", "h", "l", "vw", "n"})) ctx, cancel := context.WithCancel(context.Background()) go svc.requestAsync(ctx, query1, query2, query3, query4) time.Sleep(50 * time.Millisecond) cancel() results := make([]data, 0) loop: for { select { case <-query.Done: break loop case err := <-query.Errors: Expect(err).ShouldNot(HaveOccurred()) case r := <-query.Results: results = append(results, r...) } } Expect(results).Should(BeEmpty()) Expect(mock.ExpectationsWereMet()).ShouldNot(HaveOccurred()) // fails here
Le problème que j'ai est que ce test échoue parfois à la ligne indiquée par mon commentaire, car lorsque cancel()
est appelé, il n'y a aucune garantie que lorsque je vérifie < à l'instruction code>switch
- ctx.done ou <-exit
. L'exécution peut avoir lieu n'importe où dans la boucle jusqu'à ce que j'envoie les résultats au canal results
. Mais cela n'a aucun sens car l'exécution devrait se bloquer jusqu'à ce que je reçoive des données du canal results
, ce que je ne ferai pas tant que j'appellerai cancel()
. De plus, je m'appuie sur le package sqlmock pour les tests SQL, qui ne permet aucun type de fuzzing des requêtes SQL. Pourquoi ai-je ce problème et comment puis-je le résoudre ? cancel()
时,不能保证在我检查 < 的 <code>switch
语句处执行 - ctx.done 或 <-退出
。执行可以在循环中的任何位置进行,直到我将结果发送到 results
通道。但这没有意义,因为执行应该阻塞,直到我从 results
通道接收到数据,直到我调用 cancel()
后我才会这样做。此外,我依靠 sqlmock 包进行 sql 测试,它不允许对 sql 查询进行任何类型的模糊检查。为什么我会遇到此故障以及如何修复它?
我的问题是由于我自己对 go 通道缺乏了解而导致的。我认为,通过创建 chan([]*t, 1)
意味着通道在满时(即当它包含单个项目时)会阻塞,但事实并非如此。相反,当我尝试在缓冲区已满时发送到通道时,会发生阻塞。因此,通过像这样修改 results
chan([]*t, 1)
cela signifiait que le canal se bloquerait lorsqu'il serait plein (c'est-à-dire lorsqu'il contient un seul élément), mais ce n'est pas le cas. Au lieu de cela, lorsque j'essaie d'envoyer vers le canal lorsque le tampon est plein, un blocage se produit. Donc en modifiant les results
comme ceci : 🎜
func NewPagedQuery[T any](client *sql.DB) *PagedQuery[T] { return &PagedQuery[T]{ Results: make(chan []*T), // Remove buffer here Errors: make(chan error, 1), Done: make(chan error, 1), Quit: make(chan error, 1), client: client, } }
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!