Utilisation de la bibliothèque cliente Java (Google Cloud Client Library for Java) pour utiliser le magasin de données Il y a. J'ai étudié le fonctionnement de cette transaction dans le traitement multi-thread. Le document officiel avait l'explication de spécification suivante, donc je l'ai vérifié en exécutant le code.
Pour un ou plusieurs groupes d'entités communs, si plusieurs transactions tentent de modifier une entité en même temps, seule la première transaction qui valide le changement réussira et toutes les autres transactions échoueront. Devenir.
Nous avons également confirmé ce qui se passe lorsque le traitement via l'objet Transaction et le traitement via l'objet Datastore sont mélangés.
Documentation Documents sur les transactions
Vérifiez ce qui se passe lorsque vous essayez de mettre à jour une entité à partir de plusieurs threads dans les trois cas suivants
Obtenir une entité de Thread1 via Transaction et dormir -> Obtenir l'entité de Thread2 via Transaction et mise à jour -> Mise à jour via Transaction avec Thread1
Obtenir une entité de Thread1 via Transaction et dormir -> Obtenir l'entité de Thread2 via Datastore et mettre à jour -> Mise à jour via Transaction avec Thread1
Obtenir une entité de Thread1 via Datastore et dormir -> Obtenir l'entité de Thread2 via Transaction et mise à jour -> Mise à jour via Datastore avec Thread1
Dans le cas de transaction.update, mettez à jour après avoir confirmé si l'entité a changé depuis le moment de l'acquisition. L'exception est lorsque l'état de l'entité a changé. Peu importe que le processus de mise à jour se fasse via un objet de transaction ou un objet de banque de données.
code
package jp.ne.opt.spinapp.runner;
import com.google.cloud.datastore.*;
public final class DatastoreTest {
final static String NAME_SPACE = "dataflow";
final static String LOCK_KIND = "lock";
final static String LOCK_PROP_VALUE = "value";
final static Datastore DATASTORE = DatastoreOptions.getDefaultInstance().getService();
final static KeyFactory KEY_FACTORY = DATASTORE.newKeyFactory().setNamespace(NAME_SPACE).setKind(LOCK_KIND);
final static Transaction TRANSACTION = DATASTORE.newTransaction();
public static void main(final String[] args) throws InterruptedException {
MultiThread1 mt1 = new MultiThread1();
MultiThread2 mt2 = new MultiThread2();
mt1.start();
Thread.sleep(1000);
mt2.start();
}
}
class MultiThread1 extends Thread {
public void run() {
Entity lock = DatastoreTest.TRANSACTION.get(DatastoreTest.KEY_FACTORY.newKey("test"));
System.out.println("got lock from 1: " + lock.getLong((DatastoreTest.LOCK_PROP_VALUE)));
try {
Thread.sleep(3000);
System.out.println("sleep 1 ended");
Entity entity = Entity.newBuilder(lock).set(DatastoreTest.LOCK_PROP_VALUE, 1).build();
DatastoreTest.TRANSACTION.update(entity);
DatastoreTest.TRANSACTION.commit();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (DatastoreTest.TRANSACTION.isActive()) {
DatastoreTest.TRANSACTION.rollback();
}
}
System.out.println("thread1 done.");
}
}
class MultiThread2 extends Thread {
public void run() {
Entity lock = DatastoreTest.TRANSACTION.get(DatastoreTest.KEY_FACTORY.newKey("test"));
System.out.println("got lock from 2: " + lock.getLong((DatastoreTest.LOCK_PROP_VALUE)));
try {
Entity entity = Entity.newBuilder(lock).set(DatastoreTest.LOCK_PROP_VALUE, 2).build();
DatastoreTest.TRANSACTION.update(entity);
DatastoreTest.TRANSACTION.commit();
} finally {
if (DatastoreTest.TRANSACTION.isActive()) {
DatastoreTest.TRANSACTION.rollback();
}
}
System.out.println("thread2 done.");
}
}
got lock from 2: 0
got lock from 1: 0
thread2 done.
sleep 1 ended
[WARNING]
com.google.cloud.datastore.DatastoreException: transaction is no longer active
Après la validation de thread2, j'obtiens une erreur de transaction n'est plus active lors de la mise à jour sur thread1. La banque de données est mise à jour avec Thread2.
class MultiThread1 extends Thread {
public void run() {
Entity lock = DatastoreTest.TRANSACTION.get(DatastoreTest.KEY_FACTORY.newKey("test"));
System.out.println("got lock from 1: " + lock.getLong((DatastoreTest.LOCK_PROP_VALUE)));
try {
Thread.sleep(10000);
System.out.println("sleep 1 ended");
Entity entity = Entity.newBuilder(lock).set(DatastoreTest.LOCK_PROP_VALUE, 1).build();
DatastoreTest.TRANSACTION.update(entity);
DatastoreTest.TRANSACTION.commit();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (DatastoreTest.TRANSACTION.isActive()) {
DatastoreTest.TRANSACTION.rollback();
}
}
System.out.println("thread1 done.");
}
}
class MultiThread2 extends Thread {
public void run() {
Entity lock = DatastoreTest.DATASTORE.get(DatastoreTest.KEY_FACTORY.newKey("test"));
System.out.println("got lock from 2: " + lock.getLong((DatastoreTest.LOCK_PROP_VALUE)));
Entity entity = Entity.newBuilder(lock).set(DatastoreTest.LOCK_PROP_VALUE, 2).build();
DatastoreTest.DATASTORE.update(entity);
System.out.println("thread2 done.");
}
}
got lock from 1: 0
got lock from 2: 0
thread2 done.
sleep 1 ended
[WARNING]
com.google.cloud.datastore.DatastoreException: too much contention on these datastore entities. please try again. entity groups: [(app=b~spinapptest-151310!dataflow, lock, "test")]
Après la mise à jour avec thread2, une erreur lors de la tentative de mise à jour avec thread1. La banque de données est mise à jour avec Thread2.
class MultiThread1 extends Thread {
public void run() {
try {
Entity lock = DatastoreTest.DATASTORE.get(DatastoreTest.KEY_FACTORY.newKey("test"));
Thread.sleep(10000);
System.out.println("sleep 1 ended");
System.out.println("got lock from 1: " + lock.getLong((DatastoreTest.LOCK_PROP_VALUE)));
Entity entity = Entity.newBuilder(lock).set(DatastoreTest.LOCK_PROP_VALUE, 1).build();
DatastoreTest.DATASTORE.update(entity);
System.out.println("thread1 done.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MultiThread2 extends Thread {
public void run() {
Entity lock = DatastoreTest.TRANSACTION.get(DatastoreTest.KEY_FACTORY.newKey("test"));
System.out.println("got lock from 2: " + lock.getLong((DatastoreTest.LOCK_PROP_VALUE)));
try {
Entity entity = Entity.newBuilder(lock).set(DatastoreTest.LOCK_PROP_VALUE, 2).build();
DatastoreTest.TRANSACTION.update(entity);
DatastoreTest.TRANSACTION.commit();
} finally {
if (DatastoreTest.TRANSACTION.isActive()) {
DatastoreTest.TRANSACTION.rollback();
}
}
System.out.println("thread2 done.");
}
}
got lock from 2: 0
thread2 done.
sleep 1 ended
got lock from 1: 0
thread1 done.
Les deux processus Thread réussissent.
Recommended Posts