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();