Qual è il modo "corretto" per memorizzare le funzioni in un database?

3

Nota : Sì, so che le funzioni di archiviazione nei database dovrebbero essere punite dalla legge.

Stiamo sviluppando un'applicazione web finanziaria che utilizza PostgreSQL, Sinatra e AngularJS. Come avrai intuito, un'applicazione finanziaria potrebbe aver bisogno di alcuni calcoli.

Esiste un certo modello di database (chiamato 'Hypothesis') che ha una certa funzione che deve essere eseguita durante la notte. Il problema è che questa funzione è diversa per ogni istanza del modello. Detto questo, ogni volta che viene aggiunta una nuova ipotesi, è necessario aggiungere anche una funzione corrispondente.

A causa della natura delle applicazioni finanziarie, la funzione può eseguire semplici operazioni aritmetiche o integrarsi su un'area. Questo ci lascia con le seguenti opzioni:

  1. Implementa un DSL (lingua specifica per il dominio) : Questo sarebbe l'ideale, ma aggiungere la logica di ramificazione e di loop a un DSL sembra più come creare un nuovo linguaggio di programmazione.

  2. pipe STDIO : consente semplicemente l'esecuzione di un programma in una sandbox su un server. Questo è più flessibile, ma gli dei della sicurezza del software non sarebbero misericordiosi. Docker Immagino?

  3. Codice rubino puro sandboxed : crea una sandbox che consente di eseguire solo codice Ruby funzionalmente puro. Quindi valuta semplicemente la funzione all'interno della sandbox quando richiesto. Anche questo sarebbe un difetto di sicurezza, ma non la metà delle opzioni 2. Per ora questa opzione sembra essere implementabile usando trusted-sandbox e pure .

  4. Usa la gemma di ottava-rubino : bella idea. Ma sembra che a nessuno importi molto del progetto e probabilmente avremo bisogno di biforcarlo e lavorarci sopra. Questa sembra l'opzione migliore, dal momento che scrivere espressioni matematiche complesse è banale in Octave.

  5. ???

  6. L'utile

Un'altra preoccupazione sarebbe quella di implementare la capacità di testare le funzioni con dati falsi nel pannello di amministrazione, ma questo non è obbligatorio.

Qualcuno può suggerire un'opzione migliore / più flessibile / più sicura? O almeno uno che non memorizza una stringa di funzione nel database? Spero di aver spiegato abbastanza bene il problema.

    
posta Ianis Vasilev 18.02.2015 - 13:37
fonte

4 risposte

1

Notiamo che il modo in cui il codice è memorizzato è irrilevante; come è eseguito è essenziale.

Molto probabilmente le tue funzioni hanno un repertorio limitato. Implementali correttamente, senza archiviarli nel DB. (Lascio solo la domanda se un'integrazione numerica implementata in Ruby sia una buona idea. Supponiamo che tu usi qualcosa come Numpy del mondo Ruby.)

Ora puoi avere le corrette revisioni del codice, i test, ecc.

Quindi puoi memorizzare una forma molto limitata di un programma nel tuo database: che funziona per chiamare con quali valori di campo. La scelta è limitata dal tuo benedetto insieme di funzioni.

Che cosa succede se è necessario memorizzare formule semplici ma imprevedibili?

Limiterò questa funzione a formule che eseguono semplici operazioni aritmetiche senza effetti collaterali, senza loop e senza definizioni di funzioni. È abbastanza facile scrivere un parser per le espressioni aritmetiche di base con parentesi; molto probabilmente esistono gemme già pronte.

Quindi memorizzerei queste espressioni nel DB come testo. Metteremo solo le espressioni che possono essere analizzate (nessun costrutto di codice oltre il minuscolo sottoinsieme consentito) e eseguirò solo le espressioni che possono essere analizzate di nuovo (in modo che l'inserimento del codice di exploit direttamente nel DB non funzioni). Questo è apparentemente quello che chiamate l'approccio DSL.

Se quanto sopra non è abbastanza nel tuo caso, sceglierei un linguaggio embeddable ben noto che può essere rimosso da parti non necessarie e consentire solo le funzioni che dici che dovrebbe consentire. Lua è una scelta ovvia, Python è una scelta leggermente meno ovvia . Sii preparato: ci vorrebbe un bel po 'di lavoro.

La finestra mobile potrebbe essere un approccio più semplice, tuttavia: puoi nascondere qualsiasi cosa non strettamente necessaria, come l'accesso alla rete e al disco, dal processo.

Ma impara prima se hai bisogno di queste complessità. È molto probabile che tu non lo faccia.

    
risposta data 04.09.2015 - 19:14
fonte
0

Non so quanto siano diverse le funzioni che vuoi memorizzare, ma se riesci a isolare alcuni diversi "tipi di funzioni" potresti fare qualcosa di simile:

  • Crea diversi metodi generici nel tuo modello che possono rendere ogni calcolo speciale necessario con l'aiuto di alcuni argomenti:

    def integrate_over_an_area(args={})
      #Do your calculus with your args.
    end
    
    def simple_arithmetics(args={})
      #Do your calculus with your args.
    end
    
  • Crea - per ogni istanza del modello - un hash che descrive quale funzione generica utilizzare e i parametri:

    { 
     function: "integrate_over_an_area",
     args: {
       arg1: "value",
       arg2: "value",
       ...
      }
    }
    
  • Salva gli hash in una colonna chiamata "special_function_hash" o qualcosa di simile, nella tabella del tuo modello

  • Crea un metodo "special_function" nel tuo modello che chiama la funzione descritta nell'hash, con gli argomenti descritti nell'hash
risposta data 04.09.2015 - 18:00
fonte
0

Userei Jupyter come motore di esecuzione backend & puoi quindi scrivere le tue funzioni in diverse lingue, ad es. Julia, pitone o ottava. link

    
risposta data 04.09.2015 - 19:47
fonte
0

Potresti incorporare un interprete (come Lua o GNU guile ) nel programma utilizzando il database.

(Non sono sicuro che la codifica dell'intero software in Ruby sia la cosa migliore, la codifica in C ++ potrebbe essere utile)

Memorizzeresti nelle espressioni del database (ovvero stringa contenente l'origine Guile o Lua) in Lua o Guile, oppure potresti memorizzare qualche codice byte o alcune chiusure serializzate.

    
risposta data 04.09.2015 - 18:19
fonte

Leggi altre domande sui tag