HiLo
implemetation with Web API
Author : JB
Author : JB
1. Create table schema (with Sql Server)
CREATE TABLE
HiLo
(
KEY_NAME varchar(100) NOT NULL,
NEXT_HI bigint NOT NULL,
MAX_VAL bigint NOT NULL default 0
);
ALTER TABLE
HiLo ADD CONSTRAINT PK_HiLo PRIMARY KEY (KEY_NAME);
|
2. SQL
■ Insert (Create HiLo) :
■ Insert (Create HiLo) :
//Get
internal void BindGetNextHiSql(ref Adaman.Infra.Lib.ISql sqlObj,String keyName)
{
sqlObj.SqlCommand = String.Format(@"
SELECT NEXT_HI,MAX_VAL FROM {0}
WHERE KEY_NAME=@KEY_NAME ", base.HILO_TABLE);
sqlObj.SqlParams = new SqlParameter[]{
new SqlParameter("@KEY_NAME", keyName)
};
}
//Get
internal void BindUptNextHiSql(
ref Adaman.Infra.Lib.ISql sqlObj, String keyName, long interval)
{
sqlObj.SqlCommand = String.Format(@"
UPDATE {0}
SET
NEXT_HI = CASE WHEN (NEXT_HI+1)*{1}
> MAX_VAL THEN 0 ELSE (NEXT_HI+1) END
WHERE KEY_NAME=@KEY_NAME ", base.HILO_TABLE,
interval);
sqlObj.SqlParams = new SqlParameter[]{
new SqlParameter("@KEY_NAME", keyName)
};
}
|
3. Domain
■ Create HiLo :
Just call the sql to create a HiLo record into the database. No other business logic.
■ Create HiLo :
Just call the sql to create a HiLo record into the database. No other business logic.
/// HiLoCreateManager
public class HiLoCreateManager : IDisposable
{
private IDbModule _dbModule = null;
public bool CreateSequence(Adaman.Infra.HiLo.HiLo hl)
{
bool isCreatedOk = false;
//Check if the Key name exists ?
if (this.ChkIfKeyNameExist(hl.KeyName))
isCreatedOk = false;
else {
this.CreateNewHiLoInstance(hl); //Use Get HiLo sql
isCreatedOk = true;
}
return isCreatedOk;
}
}
/// Create new HiLo
instance in database
private void
createNewHiLoInstance(Adaman.Infra.HiLo.HiLo hl)
{
ISql sqlObj = new Sql();
using (SqlHiLoDomainManager sqlMnger = new SqlHiLoDomainManager())
{
sqlMnger.BindCreateNewHiLoInstanceSql(ref sqlObj, hl);
}
//insert
this._dbModule.ParaExecuteNonQuery(sqlObj.SqlCommand,
sqlObj.SqlParams);
}
|
■ Get Next Hi :
In HiLo algorithm, we have a Singleton instance with minHi and maxHi. Every time one try to get the nextHi value, it comes from minHi to maxHi.
After the nextHi between the interval of these two values runs out, it will get another minHi and maxHi.
For example, when minHi = 0 and maxHi = 99, we will get nextHi like …
0, 1, … 99. After 99 is used, the minHi and maxHi will set to 100 and 199 as the interval is set to 100.
Besides, when we get the minHi/maxHi value, we must update the NEXT_HI for future use.
public sealed class HiLoGetValManager:IDisposable
{
private String _keyName = String.Empty; //紀錄Key Name
private long _minHiVal = 0;
private long _maxHiVal = 0;
private static long INTERVAL = 1000; //minHi~maxHi
private static bool isCreated = false; //是否已intial min/max
value
private static object block = new object();
/// Get Next HiLo value
public long GetNextVal(String keyName)
{
//First time loaded or change the key name
if (!isCreated || !keyName.Equals(this._keyName))
{
this.syncSetInstance(keyName); //Get minHi/maxHi
isCreated = true;
this._keyName = keyName;
}
try
{
if (_minHiVal < _maxHiVal)
{
_minHiVal++;
}
else //If minHi exceeds maxHi, get
minHi/maxHi again!
{
this.syncSetInstance(keyName);
_minHiVal++;
}
return this._minHiVal;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 取得NEXT HI
/// </summary>
private void setMinMaxHi(String keyName)
{
long nextHi = 0;
long maxVal = 0;
try
{
TransactionOptions _transOptions = new TransactionOptions();
_transOptions.IsolationLevel
= System.Transactions.IsolationLevel.Serializable;
_transOptions.Timeout = new TimeSpan(0, 1, 0); //timeout : 1 hr
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required,
_transOptions))
using (SqlHiLoGetValueSingleton
sqlHiLoGetValueSingleton = new SqlHiLoGetValueSingleton())
{
//Get current Hi value
ISql sqlObj = new Adaman.Infra.Lib.Sql();
sqlHiLoGetValueSingleton.BindGetNextHiSql(ref sqlObj, keyName);
DataTable dtNextHi = this._dbModule.ParaGetDataTable(sqlObj.SqlCommand,
sqlObj.SqlParams);
if (dtNextHi != null &&
dtNextHi.Rows.Count > 0)
{
long.TryParse(dtNextHi.Rows[0].ItemArray[0].ToString(),
out nextHi); //current Hi value
long.TryParse(dtNextHi.Rows[0].ItemArray[1].ToString(),
out maxVal); //Max Value
}
sqlObj.Dispose();
//Set minHi/maxHi value with HiLo
algorithm
this.setMinMaxHiValStrategy(ref nextHi, maxVal);
//Update NEXT HI in database
ISql sqlUpt = new Adaman.Infra.Lib.Sql();
sqlHiLoGetValueSingleton.BindUptNextHiSql(ref sqlUpt, keyName, INTERVAL);
this._dbModule.ParaGetDataTable(sqlUpt.SqlCommand,
sqlUpt.SqlParams);
sqlUpt.Dispose();
scope.Complete();
}
}
catch (Exception)
{
throw;
}
}
/// lock the resource and avoid the
instance be changed at the same time
private void syncSetInstance(String keyName)
{
lock (block){
this.setMinMaxHi(keyName);
}
}
/// Set minHi/maxHi value
private void setMinMaxHiValStrategy(ref long nextHi, long maxVal)
{
try
{
//Normal case
this._minHiVal = nextHi * INTERVAL;
this._maxHiVal = nextHi * INTERVAL +
(INTERVAL - 1);
//Check the MAX VALUE constraint
if (this._minHiVal > maxVal)
{
nextHi = 0;
this._minHiVal = nextHi * INTERVAL;
this._maxHiVal = nextHi * INTERVAL + (INTERVAL
- 1);
}
else if (this._minHiVal <= maxVal && this._maxHiVal >
maxVal)
{
this._maxHiVal = maxVal;
}
}
catch (Exception)
{
throw;
}
}
}
|
4. Web API
I skipped the codes in Web API.
The important thing is that we must initial a singleton instance (HiLoGetValManager) in Web API, in order to make sure everyone get the unique HiLo value with it.
So in WebApiConfig.cs
I skipped the codes in Web API.
The important thing is that we must initial a singleton instance (HiLoGetValManager) in Web API, in order to make sure everyone get the unique HiLo value with it.
So in WebApiConfig.cs
//Singleton
instance
public static
Icash.HiLo.Domain.HiLoGetValManager HiLoGetValMnger = null;
public static void Register(HttpConfiguration config)
{
//…
HiLoGetValMnger=new Domain.HiLoGetValManager();
}
|
沒有留言:
張貼留言