Realm - Async Transaction Insertion Order - java

I use for loop to saving in Realm. Insertion order is very important. With main thread everything is ok but I want to save that data in background threads. So using multiple threads is losing insertion order.
for (int index = 1; index < upperNumber; index++) {
saveDB(index);
}
private void saveDB(final int index) {
try {
realm.executeTransactionAsync(new Realm.Transaction() {
#Override public void execute(Realm bgRealm) {
User user = bgRealm.createObject(User.class, index);
user.setName(String.valueOf(index) + " Name");
user.setAge(index);
}
}, new Realm.Transaction.OnSuccess() {
#Override public void onSuccess() {
Log.d(TAG, "Transaction Success: " + index);
}
});
} catch (Exception e) {
Log.e(TAG, "Error during Transaction: " + e.getMessage());
}
}
There is a Realm documentation about auto-increment id and insertion order but this is also not working with Async Transaction;
https://realm.io/docs/java/latest/#auto-incrementing-ids
How i guarantee insertion order with Async Transaction?
Thanks.

Related

RxJava zip operator failed to execute onComplete listener

I want to send multiple API requests, so I'm using RxJava's Zip operator, I want to know the success rate of the API requests to show it to the user, but here, whenever one request getting failed, I couldn't see any logs inside the complete method,
How to listen over all the responses together (success/fail) and find the success rate?
List<io.reactivex.rxjava3.core.Observable<Object>> requests = new ArrayList<>();
requests.add(
RetrofitInstance.getRetrofitClient()
.create(IService.class)
.sendMessage("123", com)); // my custom model class
Observable
.zip(requests, // list of API requests
new Function<Object[], List<Object>>() {
#Override
public List<Object> apply(Object[] objects) throws Exception {
Log.d("onSubscribe", "apply: " + objects.length);
for (Object o : objects) {
Log.d(TAG, "apply: %%%%% " + o.toString());
messageResponse.add((Object) o);
}
if (messageResponse.size() == requests.size()) {
Log.d(TAG, "apply: req size " + requests.size());
}
Log.d(TAG, "apply: ##4");
msgResponse[0] = true;
return messageResponse;
}
})
.subscribeOn(Schedulers.io())
.subscribe(new Observer<List<Object>>() {
#Override
public void onSubscribe(
#io.reactivex.rxjava3.annotations.NonNull Disposable d) {
Log.d(TAG, "onSubscribe: ");
}
#Override
public void onNext(
#io.reactivex.rxjava3.annotations.NonNull List<Object> objects) {
Log.d(TAG, "onNext: ");
}
#Override
public void onError(
#io.reactivex.rxjava3.annotations.NonNull Throwable e) {
Log.d(TAG, "onError: ");
}
#Override
public void onComplete() {
Log.d(TAG, "onComplete: ");
}
});

Wait until data is fetched from database and then return android

I am writing a class in java which is going to fetch results from a MongoDB database and then return the results back to the main program. My code is as follows:
public Document[] search(Document query, String databaseName, String collectionName) {
Log.i(TAG, "Search query: " + query);
_mongoClient.getDatabase(databaseName).getCollection(collectionName).find(query).continueWith(
new Continuation<List<Document>, Object>() {
#Override
public Object then(#NonNull final Task<List<Document>> task) throws Exception {
if (!task.isSuccessful()) {
Log.e(TAG, "Failed to execute query");
} else {
if (task.getResult().size() == 0) {
Log.i(TAG, "Query failed to return any results");
return null;
}
results = new Document[task.getResult().size()];
for (Integer i = 0; i < task.getResult().size(); i++) {
results[i] = task.getResult().get(i);
Log.i("test", results[i].getString("username").toString());
}
}
return null;
}
});
return results;
}
The issue with my code is that it doesn't wait for the code inside the method for retrieving the database to run. Instead it jumps straight to the return statement.
I have tried using AsyncTask like this:
private class WaitForDB extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
super.onPreExecute();
pd = new ProgressDialog(context);
pd.setMessage("Please wait");
pd.setCancelable(false);
pd.show();
}
protected Void doInBackground(Void... params) {
Document query = new Document("username", "a");
_mongoClient.getDatabase("gapp").getCollection("parent").find(query).continueWith(
new Continuation<List<Document>, Object>() {
#Override
public Object then(#NonNull final Task<List<Document>> task) throws Exception {
if (!task.isSuccessful()) {
Log.e(TAG, "Failed to execute query");
} else {
if (task.getResult().size() == 0) {
Log.i(TAG, "Query failed to return any results");
return null;
}
results = new Document[task.getResult().size()];
for (Integer i = 0; i < task.getResult().size(); i++) {
results[i] = task.getResult().get(i);
Log.i("test", results[i].getString("username").toString());
}
}
return null;
}
});
return null;
}
#Override
protected void onPostExecute(Void result) {
if (pd.isShowing()) {
pd.dismiss();
}
}
}
However, this does not work either. So, I would like to know, is there a way for the program to wait until the data is retrieved from the database? And if yes, how?
I will appreciate any kind of help.
Thanks in advance.
The first code doesn't wait only because you wrote it that way. If you want to observe that, put more log statements outside of error conditions or where you are building the result list, as well as before the return statements. The result list will be built after the return results.
What you can do is pass the "continuation" through to the method as a parameter
public void search(Document query, String databaseName, String collectionName, Continuation<List<Document>, Object> continuation) {
Log.i(TAG, "Search query: " + query);
_mongoClient.getDatabase(databaseName)
.getCollection(collectionName)
.find(query)
.continueWith(continuation);
} //end method
And that's it for that method. Do your processing elsewhere where you need the documents
For example, rather than trying to make this a blocking call for results
Document[] documents = search(...);
// process documents
You need to invoke the function, which waits for results to be received, then executes the inner callback function
// Do not define documents here
search(query, database, collection,
new Continuation<List<Document>, Object>() {
#Override
public Object then(#NonNull final Task<List<Document>> task) throws Exception {
if (!task.isSuccessful()) {
Log.e(TAG, "Failed to execute query");
} else {
List<Document> documents = task.getResults();
// process documents
}
});
// if you did define documents outside this, it'll still be empty
If you want to reduce all this code just to get an actual Document array, you need to define your own interface, but this is the most flexible working solution without extra classes

Delete batch operation in Azure Storage

I have been trying to implement a DAO method for delete operation for Azure Storage entities. Delete using TableOperation was ok.
TableOperation deleteEntity = TableOperation.delete(entity);
But when I tried it using Batch Operation, It was not supported.
Any suggestions to overcome this issue is highly appreciated.
But when I tried it using Batch Operation, It was not supported.
I assumed that you could group your items for deleting by partition key, then execute the TableBatchOperation.
Here I wrote a helper class via C# language for achieving this purpose, you could refer to it:
public class TableBatchHelper<T> where T : ITableEntity
{
const int batchMaxSize = 100;
public static IEnumerable<TableBatchOperation> GetBatchesForDelete(IEnumerable<T> items)
{
var list = new List<TableBatchOperation>();
var partitionGroups = items.GroupBy(arg => arg.PartitionKey).ToArray();
foreach (var group in partitionGroups)
{
T[] groupList = group.ToArray();
int offSet = batchMaxSize;
T[] entities = groupList.Take(offSet).ToArray();
while (entities.Any())
{
var tableBatchOperation = new TableBatchOperation();
foreach (var entity in entities)
{
tableBatchOperation.Add(TableOperation.Delete(entity));
}
list.Add(tableBatchOperation);
entities = groupList.Skip(offSet).Take(batchMaxSize).ToArray();
offSet += batchMaxSize;
}
}
return list;
}
public static async Task BatchDeleteAsync(CloudTable table, IEnumerable<T> items)
{
var batches = GetBatchesForDelete(items);
await Task.WhenAll(batches.Select(table.ExecuteBatchAsync));
}
}
Then, you could you execute the batch deleting as follows:
await TableBatchHelper<ClassName>.BatchDeleteAsync(cloudTable,items);
Or
var batches = TableBatchHelper<ClassName>.GetBatchesForDelete(entities);
Parallel.ForEach(batches, new ParallelOptions()
{
MaxDegreeOfParallelism = 5
}, (batchOperation) =>
{
try
{
table.ExecuteBatch(batchOperation);
Console.WriteLine("Writing {0} records", batchOperation.Count);
}
catch (Exception ex)
{
Console.WriteLine("ExecuteBatch throw a exception:" + ex.Message);
}
});
No, That was the code without using block operation. Following is the code that includes block operation. Sorry for not mentioning that
TableBatchOperation batchOperation = new TableBatchOperation();
List<TableBatchOperation> list = new ArrayList<>();
if (partitionQuery != null) {
for (AzureLocationData entity : cloudTable.execute(partitionQuery)) {
batchOperation.add(TableOperation.delete(entity));
list.add(batchOperation); //exception thrown line
}
try {
cloudTable.execute((TableOperation) batchOperation);
} catch (StorageException e) {
e.printStackTrace();
}
}
public void deleteLocationsForDevice(String id) {
logger.info("Going to delete location data for Device [{}]", id);
// Create a filter condition where the partition key is deviceId.
String partitionFilter = TableQuery.generateFilterCondition(
PARTITION_KEY,
TableQuery.QueryComparisons.EQUAL,
id);
// Specify a partition query, using partition key filter.
TableQuery<AzureLocationData> partitionQuery =
TableQuery.from(AzureLocationData.class)
.where(partitionFilter);
if (partitionQuery != null) {
for (AzureLocationData entity : cloudTable.execute(partitionQuery)) {
TableOperation deleteEntity = TableOperation.delete(entity);
try {
cloudTable.execute(deleteEntity);
logger.info("Successfully deleted location records with : " + entity.getPartitionKey());
} catch (StorageException e) {
e.printStackTrace();
}
}
} else {
logger.debug("No records to delete!");
}
// throw new UnsupportedOperationException("AzureIotLocationDataDao Delete Operation not supported");
}

How to corect stop thread while inserting to database in Java?

This question continues this question.
I am trying to stop thread that inserts rows to dabase, but I always got an exception
This part of code shows my thread's logic
public void run() {
String number = "";
try {
while (!Thread.currentThread().isInterrupted()) {
number = generateNumber();
database.insertOrUpdateRow(number);
}
} catch (SQLException ex) {
LOGGER.error("exception while inserting number " + number + " " + ex.getMessage());
}
}
I have googled how to stop thread correctly and found that I should just flag that thread is stopped.
So, this is what I got
public boolean stop() {
try {
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
for (Thread t : threadSet) {
if (t != null) {
if (t.getName().contains("generate")) {
t.interrupt();
LOGGER.info(t.getName()+" is stopped");
}
}
}
} catch (Exception e) {
LOGGER.error("Exception when call stop: " + e.toString());
}
return true;
}
My stacktrace
[INFO] [qtp1804418913-32] INFO se.homework.hwbs.tasks.un.server.NumberQtyService
Impl - generate37 is stopped
[INFO] [generate37] ERROR se.homework.hwbs.tasks.un.server.threads.InsertRowThre
ad - exception while inserting number 34587 transaction rollback: serialization
failure
[INFO] [qtp1804418913-33] ERROR se.homework.hwbs.tasks.un.server.database.Databa
se - error select cnt by numberjava.sql.SQLTransactionRollbackException: transac
tion rollback: serialization failure
[INFO] [qtp1804418913-34] ERROR se.homework.hwbs.tasks.un.server.database.Databa
se - error select cnt by numberjava.sql.SQLTransactionRollbackException: transac
tion rollback: serialization failure
Thread stops, everything works, but this exceptions make me feel bad. Why are they happens? How to stop thread when I work with database in correct way?
Database is HsqlDB.
Instead of enumerating threads and selecting one by name, you might consider using a stop variable in your insert thread which can be directly controlled.
Consider this GeneratorThread:
public class GeneratorThread extends Thread {
protected static volatile boolean stop;
public static void stopExecution() {
stop = true;
}
public GeneratorThread() {
stop = false;
}
public void run() {
String number = "";
try {
while (!stop) {
number = generateNumber();
database.insertOrUpdateRow(number);
}
} catch (SQLException ex) {
//Any SQLException will terminate your thread. Is this wanted?!
LOGGER.error("exception while inserting number " + number + " " + ex.getMessage());
}
}
}
You can stop this by calling GeneratorThread.stopExecution() which will set the flag to false causing the current number to be the last inserted.
Of cause this implies that there will be only one GeneratorThread ever in your application.
Just as well, you might rethink your while and try-catch positions in run. This thread would end on any SQLException whatsoever. If you want to keep the thread running even if there are (temporary?) errors, you might want to do
while (!stop) {
String number = "";
try {
number = generateNumber();
database.insertOrUpdateRow(number);
} catch (SQLException ex) {
LOGGER.error("exception while inserting number " + number + " " + ex.getMessage());
}
}
I combined ideas of #Jan and #Fildor.
So, this is what I got
public class GeneratingThread extends Thread {
private final Logger LOGGER = LoggerFactory.getLogger(InsertRowThread.class);
private final Database database;
private boolean stop = false;
public GeneratingThread(Database database) {
this.database = database;
}
#Override
public void run() {
String number = "";
try {
while (!stop) {
number = generateNumber();
database.insertOrUpdateRow(number);
}
} catch (SQLException ex) {
LOGGER.error("exception while inserting number " + number + " " + ex.getMessage());
}
}
public void stopThread() {
this.stop = true;
}
private String generateNumber() {
int number = ThreadLocalRandom.current().nextInt(0, 100000);
String sb = Integer.toString(number);
while (sb.length() < 5) {
sb = "0" + sb;
}
return sb;
}
}
And this is my starting of thread
private final List<GeneratingThread> generatingThreads = new ArrayList<>();
GeneratingThread thread = new GeneratingThread(db);
thread.start();
generatingThreads.add(thread);
And stop it
for (GeneratingThread gt : generatingThreads) {
if (gt != null) {
gt.stopThread();
LOGGER.info("Thread is stopped");
}
}
Worked perfect for me. Thanks for discussion!

Auto-increment a value in Firebase

How do I auto increment a value stored in Firebase from an Android client?
Currently: I declare int id = 1. When I increment, I see the values 2, 3 etc. being stored. That's fine, but when I re-run the project, id is set equal to 1 again.
I want it to behave like a static variable, so I can create an id which will go from 1 to infinity without resetting.
UPDATED FAILED
I used the following to pass the Firebase reference and a string to the function incrementCounter.
if(language_chosen.equalsIgnoreCase("english"))
{
Firebase publRef = f.child("Language").child("English").child("Message");
Firebase newPublRef = publRef.push();
writeMsgActivity demo = new writeMsgActivity();
demo.incrementCounter(newPublRef,the_msg_enter);
}
Now I try to use the passed reference and string at public void incrementCounter(Firebase publref,String my_msg) in the oncomplete method but it gives me an error.
public void incrementCounter(Firebase publref,String my_msg) {
publref.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(final MutableData currentData) {
if (currentData.getValue() == null) {
currentData.setValue(1);
} else {
currentData.setValue((Long) currentData.getValue() + 1);
}
return Transaction.success(currentData);
}
public void onComplete(FirebaseError firebaseError, boolean committed, DataSnapshot currentData) {
if (firebaseError != null) {
System.out.println("Firebase counter increment failed.");
} else {
System.out.println("Firebase counter increment succeeded.");
Map<String, Object> publ = new HashMap<String, Object>();
publ.put("pubMsg", my_msg);
publ.put("id",currentData);
publref.setValue(publ);
}
}
});
}
UPDATED SOLVED
final Firebase upvoteref = new Firebase("https://shareurday.firebaseio.com/Message/"+msg_id+"/upvotes");
upvoteref.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(final MutableData currentData) {
if (currentData.getValue() == null) {
currentData.setValue(1);
} else {
currentData.setValue((Long) currentData.getValue() + 1);
}
return Transaction.success(currentData);
}
public void onComplete(FirebaseError firebaseError, boolean committed, DataSnapshot currentData) {
if (firebaseError != null) {
System.out.println("Firebase counter increment failed.");
} else {
System.out.println("Firebase counter increment succeeded.");
}
}
});
The variable msg_id is the random generated id from push.
Here's an example method that increments a single counter. The key idea is that you are either creating the entry (setting it equal to 1) or mutating the existing entry. Using a transaction here ensures that if multiple clients attempt to increment the counter at the same time, all requests will eventually succeed. From the Firebase documentation (emphasis mine):
Use our transactions feature when working with complex data that could be corrupted by concurrent updates
public void incrementCounter() {
firebase.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(final MutableData currentData) {
if (currentData.getValue() == null) {
currentData.setValue(1);
} else {
currentData.setValue((Long) currentData.getValue() + 1);
}
return Transaction.success(currentData);
}
#Override
public void onComplete(FirebaseError firebaseError, boolean committed, DataSnapshot currentData) {
if (firebaseError != null) {
Log.d("Firebase counter increment failed.");
} else {
Log.d("Firebase counter increment succeeded.");
}
}
});
}
A new feature is added to firestore to autoincrement values. As simple as
document("fitness_teams/Team_1").
updateData(["step_counter" : FieldValue.increment(500)])
refer link:
https://firebase.googleblog.com/2019/03/increment-server-side-cloud-firestore.html
Used below code in my project.
var firestore = Firestore.instance;
firestore.collection('student').document('attendance').updateData({'total_attendace': FieldValue.increment(1)});

Categories