2015-05-14 229 views
2

我有一个表的唯一约束偶尔PostgreSQL的“重复键值违反唯一约束”从根本错误插入

CREATE UNIQUE INDEX "bd_hash_index" ON "public"."bodies" USING btree ("hash"); 

我也有一个围棋程序,需要一个通道上的“身体”的价值观,过滤掉通过哈希重复,并且只将非重复项插入到数据库中。 像这样:

import (
    "crypto/md5" 
    "database/sql" 
    "encoding/hex" 
    "log" 
    "strings" 
    "time" 
) 

type Process struct { 
    DB     *sql.DB 
    BodiesHash   map[string]bool 
    Channel    chan BodyIterface 
    Logger    *log.Logger 
} 

func (pr *Process) Run() { 
    bodyInsert, err := pr.DB.Prepare("INSERT INTO bodies (hash, type, source, body, created_timestamp) VALUES ($1, $2, $3, $4, $5)") 
    if err != nil { 
     pr.Logger.Println(err) 
     return 
    } 
    defer bodyInsert.Close() 

    hash := md5.New() 

    for p := range pr.Channel { 
     nowUnix := time.Now().Unix() 

     bodyString := strings.Join([]string{ 
      p.GetType(), 
      p.GetSource(), 
      p.GetBodyString(), 
     }, ":") 
     hash.Write([]byte(bodyString)) 
     bodyHash := hex.EncodeToString(hash.Sum(nil)) 
     hash.Reset() 

     if _, ok := pr.BodiesHash[bodyHash]; !ok { 
      pr.BodiesHash[bodyHash] = true 

      _, err = bodyInsert.Exec(
       bodyHash, 
       p.GetType(), 
       p.GetSource(), 
       p.GetBodyString(), 
       nowUnix, 
      ) 
      if err != nil { 
       pr.Logger.Println(err, bodyString, bodyHash) 
      } 
     } 
    } 
} 

但周期性我得到的错误

"pq: duplicate key value violates unique constraint "bd_hash_index""

在我的日志文件。我无法想象它是如何可能的,因为我在插入之前检查哈希的唯一性。 我确信当我呼叫go processDebugBody.Run()时,主体表是空的。

通道与缓冲通道创建:

processDebugBody.Channel = make(chan BodyIterface, 1000) 
+0

你确定你没有重复哈希? –

+0

我相信,当我打电话'去processDebugBody.Run()'表'体'是空的。 – user2024300

+0

该表可能为空,但插入的记录可能具有重复散列。你检查过了吗? –

回答

1

当你执行一个查询事务之外与sql.DB,它会自动在用户与连接有问题重试。在目前的实施中,最多10次。例如,请注意在sql.Exec

现在,它真的发生,只有当底层驱动程序返回driver.ErrBadConn和规范声明如下:

ErrBadConn should be returned by a driver to signal to the sql package that a driver.Conn is in a bad state (such as the server having earlier closed the connection) and the sql package should retry on a new connection.

To prevent duplicate operations, ErrBadConn should NOT be returned if there's a possibility that the database server might have performed the operation.

我认为驱动程序实现在实施这个规则有点粗心,但也许有一些背后的逻辑。前几天我一直在研究lib/pq的实施,并注意到这种情况是可能的。

正如您在评论中指出的那样,您在看到重复内容之前发出了一些SSL错误,所以这看起来像是一个合理的猜测。

要考虑的一件事是使用事务。如果在提交事务之前丢失连接,则可以确定它会回滚。此外,交易声明不会在不良连接中自动重新传输,因此可能会解决此问题 - 尽管您很可能会将SSL错误直接传播给您的应用程序,但您仍然需要自行重试。

我必须告诉你我也在使用Go 1.3看到postgres上的SSL重新协商错误,这就是为什么我暂时关闭了内部数据库的SSL(连接字符串中的sslmode=disable)。我想知道1.4版本是否已经解决了这个问题,因为变更日志上的一件事是crypto/tls软件包现在支持RFC 7301(应用层协议协商扩展的ALPN状态)中定义的ALPN。

相关问题