C# 插入时违反主键约束

C# 插入时违反主键约束,c#,linq,primary-key,conflict,C#,Linq,Primary Key,Conflict,我有一张简单的桌子: IPAddress (PK, string) Requests (int) 这是洪水限制器。每分钟表数据都会被删除。对于给定的IPAddress,每个页面请求的请求数都会递增 由于我们的产品和网站的性质,我们确实遭受了一些意外/故意的有效DDoS攻击,因此我们的网站性能显著提高 唯一的问题是,无论出于何种原因,当一个IP每分钟向我们的网站发送数千个请求时,我们会出现以下错误: 违反主键约束“PK_v2SiteIPRequests”。无法在对象“dbo.v2SiteIPRe

我有一张简单的桌子:

IPAddress (PK, string)
Requests (int)
这是洪水限制器。每分钟表数据都会被删除。对于给定的
IPAddress
,每个页面请求的
请求数都会递增

由于我们的产品和网站的性质,我们确实遭受了一些意外/故意的有效DDoS攻击,因此我们的网站性能显著提高

唯一的问题是,无论出于何种原因,当一个IP每分钟向我们的网站发送数千个请求时,我们会出现以下错误:

违反主键约束“PK_v2SiteIPRequests”。无法在对象“dbo.v2SiteIPRequests”中插入重复的密钥。重复的键值是([IP_地址])。声明已终止

进行插入的代码是:

/// <summary>
/// Call everytime a page view is requested
/// </summary>
private static void DoRequest(string ipAddress)
{
    using (var db = new MainContext())
    {
        var rec = db.v2SiteIPRequests.SingleOrDefault(c => c.IPAddress == ipAddress);
        if (rec == null)
        {
            var n = new v2SiteIPRequest {IPAddress = ipAddress, Requests = 1};
            db.v2SiteIPRequests.InsertOnSubmit(n);
            db.SubmitChanges();
        }
        else
        {
            rec.Requests++;
            db.SubmitChanges();

            // Ban?
            if (rec.Requests >= Settings.MAX_REQUESTS_IN_INTERVAL)
            {
                BanIP(ipAddress);
            }
        }
    }
}
//
///每次请求页面视图时调用
/// 
私有静态void DoRequest(字符串ipAddress)
{
使用(var db=new MainContext())
{
var rec=db.v2SiteIPRequests.SingleOrDefault(c=>c.IPAddress==IPAddress);
if(rec==null)
{
var n=new v2SiteIPRequest{IPAddress=IPAddress,Requests=1};
db.v2SiteIPRequests.InsertOnSubmit(n);
db.SubmitChanges();
}
其他的
{
rec.Requests++;
db.SubmitChanges();
//禁令?
if(rec.Requests>=Settings.MAX_Requests_IN_INTERVAL)
{
BanIP(IP地址);
}
}
}
}

处理此异常的最佳方法是什么?为什么要抛出它?“try catch”是这里最好的吗?

如果同时收到两个请求,则会发生以下情况:

Request one: is it in the database?
Request two: is it in the database?

Request one: No, not yet
Request two: No, not yet

Request one: INSERT
Request two: INSERT

Request one: WORKS
Request two: FAILS (already inserted a split second before)

在这里,除了捕获异常并优雅地处理它之外,您无能为力。可能通过使用简单的“重试”逻辑。

如果同时收到两个请求,则会发生以下情况:

Request one: is it in the database?
Request two: is it in the database?

Request one: No, not yet
Request two: No, not yet

Request one: INSERT
Request two: INSERT

Request one: WORKS
Request two: FAILS (already inserted a split second before)

在这里,除了捕获异常并优雅地处理它之外,您无能为力。可能通过使用一个简单的“重试”逻辑。

这里有一些竞争条件,特别是当存在并发连接时


您可能需要更改方法,并始终存储每个请求,然后查询在时间范围内是否有超出允许范围的请求,并采取您需要的任何操作

您有一些竞争条件,特别是当存在并发连接时


您可能需要更改方法,并始终存储每个请求,然后查询时间范围内是否有超出允许范围的请求,并采取所需的任何操作。以下是基于建议的解决方案。这很难看,但据我所知效果很好

/// <summary>
/// Call everytime a page view is requested
/// </summary>
private static void DoRequest(string ipAddress)
{
    using (var db = new MainContext())
    {
        var rec = db.v2SiteIPRequests.SingleOrDefault(c => c.IPAddress == ipAddress);
        if (rec == null)
        {
            // Catch insert race condition for PK violation.  Especially susceptible when being hammered by requests from 1 IP
            try
            {
                var n = new v2SiteIPRequest {IPAddress = ipAddress, Requests = 1};
                db.v2SiteIPRequests.InsertOnSubmit(n);
                db.SubmitChanges();
            }
            catch (Exception e)
            {
                try
                {
                    // Can't reuse original context as it caches
                    using (var db2 = new MainContext())
                    {
                        var rec2 = db2.v2SiteIPRequests.Single(c => c.IPAddress == ipAddress);
                        rec2.Requests++;
                        db2.SubmitChanges();
                        if (rec2.Requests >= Settings.MAX_REQUESTS_IN_INTERVAL)
                        {
                            BanIP(ipAddress);
                        }
                    }
                }
                catch (Exception ee)
                {
                    // Shouldn't reach here
                    Error.Functions.NewError(ee);
                }
            }
        }
        else
        {
            rec.Requests++;
            db.SubmitChanges();

            // Ban?
            if (rec.Requests >= Settings.MAX_REQUESTS_IN_INTERVAL)
            {
                BanIP(ipAddress);
            }
        }
    }
}
//
///每次请求页面视图时调用
/// 
私有静态void DoRequest(字符串ipAddress)
{
使用(var db=new MainContext())
{
var rec=db.v2SiteIPRequests.SingleOrDefault(c=>c.IPAddress==IPAddress);
if(rec==null)
{
//PK冲突的捕获插入竞争条件。当受到来自1 IP的请求的重击时特别容易受到影响
尝试
{
var n=new v2SiteIPRequest{IPAddress=IPAddress,Requests=1};
db.v2SiteIPRequests.InsertOnSubmit(n);
db.SubmitChanges();
}
捕获(例外e)
{
尝试
{
//无法在缓存原始上下文时重用它
使用(var db2=newmaincontext())
{
var rec2=db2.v2SiteIPRequests.Single(c=>c.IPAddress==IPAddress);
rec2.Requests++;
db2.SubmitChanges();
if(rec2.Requests>=Settings.MAX\u Requests\u IN\u INTERVAL)
{
BanIP(IP地址);
}
}
}
捕获(异常ee)
{
//我不应该到这里
Error.Functions.NewError(ee);
}
}
}
其他的
{
rec.Requests++;
db.SubmitChanges();
//禁令?
if(rec.Requests>=Settings.MAX_Requests_IN_INTERVAL)
{
BanIP(IP地址);
}
}
}
}

以下是基于建议的解决方案。这很难看,但据我所知效果很好

/// <summary>
/// Call everytime a page view is requested
/// </summary>
private static void DoRequest(string ipAddress)
{
    using (var db = new MainContext())
    {
        var rec = db.v2SiteIPRequests.SingleOrDefault(c => c.IPAddress == ipAddress);
        if (rec == null)
        {
            // Catch insert race condition for PK violation.  Especially susceptible when being hammered by requests from 1 IP
            try
            {
                var n = new v2SiteIPRequest {IPAddress = ipAddress, Requests = 1};
                db.v2SiteIPRequests.InsertOnSubmit(n);
                db.SubmitChanges();
            }
            catch (Exception e)
            {
                try
                {
                    // Can't reuse original context as it caches
                    using (var db2 = new MainContext())
                    {
                        var rec2 = db2.v2SiteIPRequests.Single(c => c.IPAddress == ipAddress);
                        rec2.Requests++;
                        db2.SubmitChanges();
                        if (rec2.Requests >= Settings.MAX_REQUESTS_IN_INTERVAL)
                        {
                            BanIP(ipAddress);
                        }
                    }
                }
                catch (Exception ee)
                {
                    // Shouldn't reach here
                    Error.Functions.NewError(ee);
                }
            }
        }
        else
        {
            rec.Requests++;
            db.SubmitChanges();

            // Ban?
            if (rec.Requests >= Settings.MAX_REQUESTS_IN_INTERVAL)
            {
                BanIP(ipAddress);
            }
        }
    }
}
//
///每次请求页面视图时调用
/// 
私有静态void DoRequest(字符串ipAddress)
{
使用(var db=new MainContext())
{
var rec=db.v2SiteIPRequests.SingleOrDefault(c=>c.IPAddress==IPAddress);
if(rec==null)
{
//PK冲突的捕获插入竞争条件。当受到来自1 IP的请求的重击时特别容易受到影响
尝试
{
var n=new v2SiteIPRequest{IPAddress=IPAddress,Requests=1};
db.v2SiteIPRequests.InsertOnSubmit(n);
db.SubmitChanges();
}
捕获(例外e)
{
尝试
{
//无法在缓存原始上下文时重用它
使用(var db2=newmaincontext())
{
var rec2=db2.v2SiteIPRequests.Single(c=>c.IPAddress==IPAddress);
rec2.Requests++;
db2.SubmitChanges();
if(rec2.Requests>=Settings.MAX\u Requests\u IN\u INTERVAL)
{
BanIP(IP地址);
}
}
}
捕获(异常ee)
{
//我不应该到这里
Error.Functions.NewError(ee);
}
}
}
其他的
{
rec.Requests++;
db.SubmitChanges();