2010-04-25 3 views
3

Pouvez-vous me montrer un moyen de simuler l'accès simultané à memcache sur Google App Engine? J'essaye avec LocalServiceTestHelpers et les discussions mais n'ai pas de chance. Chaque fois que je tente d'accéder Memcache dans un fil, alors je reçois cette erreur:Google App Engine: test d'unité d'accès simultané à memcache

ApiProxy$CallNotFoundException: The API package 'memcache' or call 'Increment()' was not found 

Je suppose que la bibliothèque de tests de GAE SDK a essayé d'imiter l'environnement réel et donc la configuration de l'environnement pour un seul fil (la thread qui exécute le test) qui ne peut pas être vu par un autre thread.

Voici un morceau de code qui peut reproduire le problème

package org.seamoo.cache.memcacheImpl; 

import org.testng.Assert; 
import org.testng.annotations.AfterMethod; 
import org.testng.annotations.BeforeMethod; 
import org.testng.annotations.Test; 

import com.google.appengine.api.memcache.MemcacheService; 
import com.google.appengine.api.memcache.MemcacheServiceFactory; 
import com.google.appengine.tools.development.testing.LocalMemcacheServiceTestConfig; 
import com.google.appengine.tools.development.testing.LocalServiceTestHelper; 

public class MemcacheTest { 
LocalServiceTestHelper helper; 

public MemcacheTest() { 
    LocalMemcacheServiceTestConfig memcacheConfig = new LocalMemcacheServiceTestConfig(); 
    helper = new LocalServiceTestHelper(memcacheConfig); 
} 

/** 
    * 
    */ 
@BeforeMethod 
public void setUp() { 
    helper.setUp(); 
} 

/** 
    * @see LocalServiceTest#tearDown() 
    */ 
@AfterMethod 
public void tearDown() { 
    helper.tearDown(); 
} 

@Test 
public void memcacheConcurrentAccess() throws InterruptedException { 
    final MemcacheService service = MemcacheServiceFactory.getMemcacheService(); 
    Runnable runner = new Runnable() { 

    @Override 
    public void run() { 
    // TODO Auto-generated method stub 
    service.increment("test-key", 1L, 1L); 
    try { 
    Thread.sleep(200L); 
    } catch (InterruptedException e) { 
    // TODO Auto-generated catch block 
    e.printStackTrace(); 
    } 
    service.increment("test-key", 1L, 1L); 
    } 
    }; 

    Thread t1 = new Thread(runner); 
    Thread t2 = new Thread(runner); 
    t1.start(); 
    t2.start(); 
    while (t1.isAlive()) { 
    Thread.sleep(100L); 
    } 
    Assert.assertEquals((Long) (service.get("test-key")), new Long(4L)); 
} 
} 

Répondre

5

Essayez-vous de tester votre application ou l'implémentation memcache d'App Engine? La sémantique de memcache sous les lectures et les écritures simultanées est bien comprise - vous feriez mieux de simplement simuler les conditions qui peuvent survenir afin de vérifier que votre application les gère correctement.

+0

Hmm, je pense que vous avez fait un bon point ici. Je n'ai pas vraiment besoin de tester l'accès concurrent car il devrait être garanti par Google. Ce que je dois tester, c'est si ma logique métier fonctionne bien avec un accès synchronisé. –

0

I guess that the testing library of GAE SDK tried to mimic the real environment and thus setup the environment for only one thread (the thread that running the test) which cannot be seen by other thread.

Cette estimation est à peu près correct: depuis GAE exécute lui-même jamais plusieurs threads au sein d'un processus, donc de la même ni ne le SDK - il n'est donc pas clair pour moi pourquoi vous voulez tester un accès multithread qui ne peut tout simplement pas se produire dans GAE.

+0

« depuis GAE exécute lui-même jamais plusieurs threads au sein d'un processus ». Nitpicking, mais il n'y a jamais plus d'une requête manipulant le thread, et vous ne pouvez pas démarrer votre propre thread, mais il y a des choses qui peuvent arriver de manière asynchrone (comme tous les appels RPC), donc je suppose qu'il y a plusieurs threads. – Thilo

+0

@Thilo, pas vraiment: le fonctionnement asynchrone n'est pas forcément synonyme de threading (rappelons que le threading est étonnamment limité en Python). Un bon point est que j'aurais pu limiter mon assertion à GAE _en Python_ - le runtime Java est différent (et je ne le connais pas vraiment, mais je crois qu'il permet une utilisation multithread). –

+0

GAE est essentiellement un moteur de servlet. Il a une requête par thread dédié (pourrait être implémenté en tant que processus) pour la durée de l'appel. –

1

GAE ne prend pas en charge les threads utilisateur. Je pense que vous essayez de trop compliquer. L'une des façons de tester cela consiste à créer un client Thread externe et à générer autant de threads que vous le souhaitez pour accéder à GAE à distance. Si cela fonctionne sur votre boîte locale, pointez là.

+0

+1. La seule manière significative de tester est d'avoir plusieurs clients externes martelant le service Web simultanément. – Thilo

+0

Je fais face à un scénario où mon application est exécutée sur deux serveurs différents et essaie de modifier la même entrée memcache (en même temps) et le filetage est ce que je trouve disponible pour imiter ce scénario. –

+0

Je ne veux vraiment pas créer un client thread externe, car cela demanderait à mes tests unitaires de dépendre d'un serveur web (qui, bien, demande peu de frais de démarrage, et ne peut pas être fait d'une manière unitaire). –

2

Le problème est de savoir comment les fils sont mis en place, et peuvent être résolus à l'aide du ThreadManager fourni dans les SDKs:

private final LocalServiceTestHelper helper = 
     new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()); 

@Before 
public void setUp() throws Exception { 
    helper.setUp(); 
} 

@After 
public void tearDown() throws Exception { 
    helper.tearDown(); 
} 

AtomicInteger threadsRemaining = new AtomicInteger(NUM_USERS); 
AtomicInteger numFailingThreads = new AtomicInteger(0); 

@Test 
public void testManyUsers() throws Exception { 


    ArrayList<TestUser> testUsers = new ArrayList<TestUser>(); 

    // Create something to test in parallel (in this case a "user") 
    for (int i = 0; i < NUM_USERS; ++i) { 
     testUsers.add(new TestUser()); 
    } 

    // Fork off a thread for each user 
    runInParallel(testUsers); 

    // Wait for threads to complete. 
    while (threadsRemaining.intValue() != 0) { 
     Thread.sleep(10); // TODO: Clean up with callbacks or whatever. 
    } 

    assertEquals(numFailingThreads.intValue(), 0); 
} 

private void runInParallel(final ArrayList<TestUser> testUsers) { 


    for (final TestUser u : testUsers) { 

     Runnable myTask = new Runnable() { 

      @Override 
      public void run() { 
       try { 

        // The user tries to kill my server 

       } catch (Exception e) { 

        // The user triggered badness in my server 

        e.printStackTrace(); 
        numFailingThreads.incrementAndGet(); 
       } 
       threadsRemaining.decrementAndGet(); 
      } 
     }; 

     ThreadFactory threadFactory = ThreadManager.currentRequestThreadFactory(); 

     Thread t = threadFactory.newThread(myTask); 

     t.start(); 
    } 


}