Delete command not removing records from Aerospike - aerospike

I'm using the following method from Java Aerospike client in order to delete records/bins:
def truncate(startTime: Long, durableDelete: Boolean): List[AtomicInteger] = {
logger.info(s"truncate($startTime) Triggered")
val calendar = new GregorianCalendar()
calendar.setTimeInMillis(startTime)
// Define Scan and Write Policies
val scanPolicy = new ScanPolicy()
scanPolicy.filterExp = Exp.build(Exp.le(Exp.lastUpdate(), Exp.`val`(calendar)))
val writePolicy = client.writePolicyDefault
writePolicy.durableDelete = durableDelete
// Scan all records such as LUT <= startTime
for (recoverBins <- config.binsToRecover) yield {
val recordCount = new AtomicInteger(0)
client.scanAll(scanPolicy, recoverBins.namespace, recoverBins.set, new ScanCallback() {
override def scanCallback(key: Key, record: Record): Unit = {
recoverBins.specificBins match {
// multi-bin scenario
case Some(specificBins) =>
specificBins foreach (bin => client.put(writePolicy, key, Bin.asNull(bin)))
logger.debug(s"Bins $specificBins of record: $record with key: $key are set to NULL")
// single-bin scenario
case None =>
client.delete(writePolicy, key)
logger.debug(s"Record: $record with key: $key DELETED")
}
recordCount.incrementAndGet()
}
})
duruableDelete is set to true
The problem is that when I'm removing bins (Bin.asNull) i can see the results immediately but when checking the "deleted" key i can still see them (aql> select * from ns.set where PK = <ShouldBeDeleted>
any ideas why? what I'm doing wrong?
Another question: How does duruableDelete is preventing "Zombie records"? what happening behind the scene?
Thanks!

Zombie Record: A record that was earlier removed from the cluster but comes back alive in the cluster at a later time. There are various scenarios under which this can happen. Durably deleting records prevents all but one of these scenarios. Durable deletes do this by saving a tombstone in the cluster. Example: You have a record on master and replica. You shutdown master node, then delete record in the cluster non-durably. When you restart the master, the record will be resurrected. If you durably deleted, the cluster will have a tombstone (assuming you restarted master within tombstone life period - 1 day default) and when incoming master compares record metadata upon joining the cluster, it will not resurrect the its copy of the record.

Related

ChronicleMap cannot store/use the defined No of Max.Entries after removing a few entries?

Chronicle Map Versions I used - 3.22ea5 / 3.21.86
I am trying to use ChronicleMap as an LRU cache.
I have two ChronicleMaps both equal in configuration with allowSegmentTiering set as false. Consider one as main and the other as backup.
So, when the main Map gets full, few entries will be removed from the main Map and in parallel the backup Map will be used. Once the entries are removed from main Map, the entries from the backup Map will be refilled in the Main Map.
Shown below a sample code.
ChronicleMap<ByteBuffer, ByteBuffer> main = ChronicleMapBuilder.of(ByteBuffer.class, ByteBuffer.class).name("main")
.entries(61500)
.averageKey(ByteBuffer.wrap(new byte[500]))
.averageValue(ByteBuffer.wrap(new byte[5120]))
.allowSegmentTiering(false)
.create();
ChronicleMap<ByteBuffer, ByteBuffer> backup = ChronicleMapBuilder.of(ByteBuffer.class, ByteBuffer.class).name("backup")
.entries(100)
.averageKey(ByteBuffer.wrap(new byte[500]))
.averageValue(ByteBuffer.wrap(new byte[5120]))
.allowSegmentTiering(false)
.create();
System.out.println("Main Heap Size -> "+main.offHeapMemoryUsed());
SecureRandom random = new SecureRandom();
while (true)
{
System.out.println();
AtomicInteger entriesAdded = new AtomicInteger(0);
try
{
int mainEntries = main.size();
while /*(true) Loop until error is thrown */(mainEntries < 61500)
{
try
{
byte[] keyN = new byte[500];
byte[] valueN = new byte[5120];
random.nextBytes(keyN);
random.nextBytes(valueN);
main.put(ByteBuffer.wrap(keyN), ByteBuffer.wrap(valueN));
mainEntries++;
}
catch (Throwable t)
{
System.out.println("Max Entries is not yet reached!!!");
break;
}
}
System.out.println("Main Entries -> "+main.size());
for (int i = 0; i < 10; i++)
{
byte[] keyN = new byte[500];
byte[] valueN = new byte[5120];
random.nextBytes(keyN);
random.nextBytes(valueN);
backup.put(ByteBuffer.wrap(keyN), ByteBuffer.wrap(valueN));
}
AtomicInteger removed = new AtomicInteger(0);
AtomicInteger i = new AtomicInteger(Math.max( (backup.size() * 5), ( (main.size() * 5) / 100 ) ));
main.forEachEntry(c -> {
if (i.get() > 0)
{
c.context().remove(c);
i.decrementAndGet();
removed.incrementAndGet();
}
});
System.out.println("Removed "+removed.get()+" Entries from Main");
backup.forEachEntry(b -> {
ByteBuffer key = b.key().get();
ByteBuffer value = b.value().get();
b.context().remove(b);
main.put(key, value);
entriesAdded.incrementAndGet();
});
if(backup.size() > 0)
{
System.out.println("It will never be logged");
backup.clear();
}
}
catch (Throwable t)
{
// System.out.println();
// t.printStackTrace(System.out);
System.out.println();
System.out.println("-------------------------Failed----------------------------");
System.out.println("Added "+entriesAdded.get()+" Entries in Main | Lost "+(backup.size() + 1)+" Entries in backup");
backup.clear();
break;
}
}
main.close();
backup.close();
The above code yields the following result.
Main Entries -> 61500
Removed 3075 Entries from Main
Main Entries -> 61500
Removed 3075 Entries from Main
Main Entries -> 61500
Removed 3075 Entries from Main
Max Entries is not yet reached!!!
Main Entries -> 59125
Removed 2956 Entries from Main
Max Entries is not yet reached!!!
Main Entries -> 56227
Removed 2811 Entries from Main
Max Entries is not yet reached!!!
Main Entries -> 53470
Removed 2673 Entries from Main
-------------------------Failed----------------------------
Added 7 Entries in Main | Lost 3 Entries in backup
In the above result, The Max Entries of the Main map got decreased in the subsequent iterations and the refilling from the backup Map also got failed.
In the Issue 128, it was said the entries are deleted properly.
Then why the above sample code fails? What am I doing wrong in here? Is the Chronicle Map not designed for such usage pattern?
Even If I use one Map only, the max Entries the Map can hold gets reduced after each removal of entries.

Entity Framework Serializable Transaction Deadlock

I have to insert a row into the database but the problem is that the primary key is generated based on the total counts of rows.
E.g. if the db has 25601 rows, the ID of the newly inserted record would be CT25602.
I want to use transactions for primary key collisions.
Here is the code I wrote.
public void CreateContact(ContactViewModel input)
{
var transactionScopeOptions = new TransactionOptions
{
IsolationLevel = IsolationLevel.Serializable,
Timeout = TimeSpan.MaxValue
};
using (TransactionScope transaction = new TransactionScope(TransactionScopeOption.Required, transactionScopeOptions))
{
var contactNo = GenerateIdentity();
var contact = MapContactFields(new NavContact { No_ = contactNo }, input);
_db.Contacts.InsertOnSubmit(contact);
_db.SubmitChanges();
transaction.Complete();
}
}
This code gives me deadlocks if two persons are trying to insert a contact in a small timespan.
Any suggestions ? Thank you
Yes, the scenario you described is very likely to deadlock. I would recommend using a sequence instead. If not, then one solution is to acquire an exclusive app lock in the transaction, before scannig for the next identity. See sp_getapplock.

SCAN command performance with phpredis

I'm replacing KEYS with SCAN using phpredis.
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
$it = NULL;
while($arr_keys = $redis->scan($it, "mykey:*", 10000)) {
foreach($arr_keys as $str_key) {
echo "Here is a key: $str_key\n";
}
}
According to redis documentation, I use SCAN to paginate searches to avoid disadvantage of using KEYS.
But in practice, using above code costs me 3 times lower than using just a single $redis->keys()
So I'm wondering if I've done something wrong, or I have to pay speed to avoid KEYS's threat?
Note that I totally have 400K+ keys in my db, and 4 mykey:* keys
A word of caution of using the example:
$it = NULL;
while($arr_keys = $redis->scan($it, "mykey:*", 10000)) {
foreach($arr_keys as $str_key) {
echo "Here is a key: $str_key\n";
}
}
That can return empty array's if none of the 10000 keys scanned matches and then it will give up, and you didn't get all the keys you wanted! I would recommend doing more like this:
$it = null;
do
{
$arr_keys = $redis->scan($it, $key, 10000);
if (is_array($arr_keys) && !empty($arr_keys))
{
foreach ($arr_keys as $str_key)
{
echo "Here is a key: $str_key\n";
}
}
} while ($arr_keys !== false);
And why it takes so long, 400k+ keys, 10000, that's 40 scan requests to redis, if it's not on the local machine, add latency for every 40 redis query to your speed.
Since using keys in production environments is just forbidden because it blocks the entire server while iterating global space keys, then, there's no discussion here about use or not to use keys.
In the other hand, if you want to speed up things, you should go further with Redis: you should index your data.
I doubt that these 400K keys couldn't be categorized in sets or sorted sets, or even hashes, so when you need a particular subset of your 400K-keys-database you could run any scan-equivalent command against a set of 1K items, instead of 400K.
Redis is about indexing data. If not, you're using it like just a simple key-value store.

Entity Framework Transactions and Deadlock

When SaveChanges() is called on the context, all insert/delete/update operations are executed in a single transaction. It is also possible to use DbContextTransaction for transactions. I am trying to simulate deadlock using both of these approaches. When I use DbContextTransaction, I get the deadlock exception right away but SaveChanges() alone does not throw any deadlock exceptions even after an hour. Am I doing something wrong?
Here is the code with DbContextTransaction. I try to update the first row and then the second row in the main thread. I also start another task which tries to update the second row first and then the first row.
while (true)
{
using (var context = new SchoolDBEntities())
{
using (System.Data.Entity.DbContextTransaction dbTran = context.Database.BeginTransaction())
{
Random r = new Random();
int r1 = r.Next();
int r2 = r.Next();
Student std1 = context.Students.First();
std1.StudentName = "test"+r1;
context.SaveChanges();
Student std2 = context.Students.Find(2);
std2.StudentName = "test"+r2;
context.SaveChanges();
dbTran.Commit();
}
}
}
But when I try it with just SaveChanges() it does not generate deadlock:
while (true)
{
using (var context = new SchoolDBEntities())
{
try
{
Random r = new Random();
int r1 = r.Next();
int r2 = r.Next();
Student std1 = context.Students.First();
std1.StudentName = "test" + r1;
Student std2 = context.Students.Find(2);
std2.StudentName = "test" + r2;
context.SaveChanges();
}
}
}
I am using SQL Profiler to trace the transactions. I even added more updates to the second approach just to make that transaction's duration equal to the DbContextTransaction case thinking it might be the reason but still no luck! When I look at the trace, I see that updates belonging to a particular transaction start only after the previous transaction is committed. What could be the reason?
Upon further investigation, I found out that regadless of the order of changes I have made in the context, the order in which SaveChanges() method always sends update queries to the SQL Server is based on the primary key of the table. In other words, even though I try to reverse the order of update request by first changing row 2 and then row 1, SaveChanges() first executes the update query for row 1 and then for row 2. That's why I don't get a deadlock by using just SaveChanges() method. It does not reverse the order of the queries.

Using ActiveRecord/NHibernate, can I Delete and Refresh without a Flush?

I have the following Unit Test method:
void TestOrderItemDelete()
{
using (new SessionScope())
{
var order = Order.FindById(1234);
var originalItemCount = order.OrderItems.Count;
Assert.IsTrue(originalCount > 0);
var itemToDelete = order.OrderItems[0];
itemToDelete.DeleteAndFlush(); // itemToDelete.Delete();
order.Refresh();
Assert.AreEqual(originalCount - 1, order.OrderItems.Count);
}
}
As you can see from the comment after the DeleteAndFlush command, I had to change it from a simple Delete to get the Unit test to pass. Why is this? The same is not true for my other unit test for adding an OrderItem. This works just fine:
void TestOrderItemAdd()
{
using (new SessionScope())
{
var order = Order.FindById(1234);
var originalItemCount = order.OrderItems.Count;
var itemToAdd = new OrderItem();
itemToAdd.Order = order;
itemToAdd.Create(); // Notice, this is not CreateAndFlush
order.Refresh();
Assert.AreEqual(originalCount + 1, order.OrderItems.Count);
}
}
All of this came up when I started using Lazy Instantiation of the Order.OrderItems relationship mapping, and had to add the using(new SessionScope) block around the test.
Any ideas?
This is difficult to troubleshoot without knowing the contents of your mappings, but one possibility is that you have the ID property of the OrderItem mapped using an identity field (or sequence, etc.) in the DB. If this is the case, NHibernate must make a trip to the database in order to generate the ID field, so the OrderItem is inserted immediately. This is not true of a delete, so the SQL delete statement isn't executed until session flush.