Elaborazione di un flusso. Devono essere violati i livelli?

7

Situazione teorica:
Un trilione di foobar sono memorizzati in un file di testo (nessun database di fantasia). Su ogni foobar deve essere eseguita una logica di business. Un set di 1 trilione non si adatta alla memoria, quindi il livello dati non può restituire un grande set al livello aziendale. Invece devono essere trasmessi in streaming in 1 fobob alla volta e devono eseguire la logica aziendale su 1 fobob alla volta. Il flusso deve essere chiuso al termine.

Affinché lo stream venga chiuso, il livello aziendale deve chiudere il flusso (un dettaglio dell'operazione dati), violando così la separazione delle preoccupazioni.

È possibile elaborare in modo incrementale i dati senza violare i livelli?

    
posta Lord Tydus 05.10.2012 - 03:01
fonte

4 risposte

5

Buona domanda.

Questo è un codice C # -ish che ti consente di ottenerlo in entrambe le direzioni. Il trucco qui è la valutazione lazy (yield).

// Lazy producer that will auto-close the file.
// The OS and the disk do the caching for you.
public static IEnumerable<FooBar> GetFoobars(string fileName, long maxNumber = 1000000000000)
{
    using(File file = open(fileName)) // pseudocode
    {
        FooBar nextFooBar;
        do
        {
            nextFoobar = new Foobar(file.ReadByte()); // or something like that.
            yield return nextFoobar;
        }
        while(nextFooBar != null || /* reach max */); // pseudocode
    }
}

// Consumer ... stupid example. Oh well ...
public static int Consume()
{
    int result = (from foo in GetFoobars("foo.txt").AsParallel()
                 where foo.Depth % 10 == 0).Count();
    return result;
}

La funzione GetFoobars(...) sa quale file aprire, quante cose leggere e come chiudere il flusso. Devi solo tirare i risultati quando ne hai bisogno. Le preoccupazioni sono separate. Quello che sembra essere il problema? Probabilmente potresti fare il tutto come un solitario ... forse no, ma vicino.

P.S. Raccomando di consultare le conferenze video di SICP.

    
risposta data 05.10.2012 - 07:37
fonte
2

Is it possible to incrementally process data without violating layers?

Sì, lo è. Devi definire l'unità di lavoro che risolverà la tua confusione. Penso che l'elaborazione di set di record (100 o 1000 dipende dal tempo di elaborazione di un record medio) o un record alla volta abbia risultati più affidabili e gestibili.

Inoltre, vorrei assicurarmi che il processo di manipolazione dei dati (elaborazione aziendale) sia in una transazione , tutte le elaborazioni degli errori vengano registrate e ripristinate per le indagini. Potresti sempre avere delle situazioni eccezionali che devono essere prese in considerazione.

    
risposta data 05.10.2012 - 04:00
fonte
1

Definisci un'interfaccia astratta che presenti un foobar (o qualsiasi altra serie di foobar è necessaria), lasciandola fino all'implementazione dell'astrazione per determinare come i foobar vengono acquisiti e / o modificati.

Naturalmente, è necessario definire gli elementi di questa interfaccia attorno a quelle condizioni reali. Se l'unico modo per contare i foobars è quello di superare tutti i trilioni, non includere "ottieni il numero di foobar" nell'interfaccia. Ma probabilmente la tua interfaccia conterrà alcuni elementi come "start" "stop" e "abort" oltre a "get"

    
risposta data 05.10.2012 - 05:46
fonte
1

Il modo in cui lo vedo è questo:

  • Hai un livello dati che fornisce funzionalità per leggere i singoli foobar, uno alla volta.

  • Questa funzionalità ha un'API. Quell'API ha un contratto. Chiunque desideri utilizzare l'API deve aderire al contratto. Il contratto potrebbe specificare, ad esempio, che devi chiamare NextFoobar() finché non ottieni null o qualcosa del genere, e quindi devi smettere di chiamarlo, ma ovviamente potrebbe specificare qualcosa di più complesso e sarebbe semplicemente il contratto da usare.

  • Il livello aziendale non si cura di come hai implementato l'API, ha solo bisogno di utilizzare questa API per fare ciò che deve fare. L'utilizzo dell'API include che deve rispettare il contratto.

Ora, se guardi la risposta basata su C # pubblicata da @Job, segue tutti questi principi:

  • Il livello dati fornisce un IEnumerable<Foobar> - un oggetto che consente a chiunque di recuperare da esso i foobar, uno alla volta, senza doversi preoccupare di dove provengono i foobars.

  • L'API su IEnumerable<T> è ben definita. Puoi usarlo nel modo più duro (chiama GetEnumerator() , quindi sull'enumeratore continua a chiamare MoveNext() e Current finché MoveNext() restituisce false, quindi chiama Dispose() ) o utilizza una funzione o un metodo di linguaggio che utilizza l'API automaticamente (ad esempio foreach o, nel suo caso, .Count() ).

  • Il livello aziendale utilizza correttamente questa API. fa deve chiamare Dispose() (che indirettamente chiama .Count() ), ma non ha bisogno di preoccuparsi di ciò che effettivamente fa Dispose() , lo chiama solo per soddisfare il contratto.

(Incidentalmente, se enumerate tutti i foobar, il file non viene effettivamente chiuso dalla chiamata a Dispose() ma dall'ultima chiamata a MoveNext() ... ma Dispose() è ancora parte dell'API in modo che possa (e lo faccia) chiudere il file se si decide di interrompere l'enumerazione nel mezzo. La bellezza della separazione delle preoccupazioni è che il livello aziendale non deve preoccuparsi di nulla di tutto ciò.)

    
risposta data 07.10.2012 - 08:18
fonte

Leggi altre domande sui tag