Christian Thaeter's persistence library
git-svn-id: https://yap.svn.sf.net/svnroot/yap/trunk@1547 b08c6af1-5177-4d33-ba66-4b1c6b8b522a
This commit is contained in:
parent
7f360393f4
commit
b07720e147
309
LGPL/persistence.yap
Normal file
309
LGPL/persistence.yap
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
persistence.yap - make assertions and retracts persistent
|
||||
|
||||
Copyright (C) 2006, Christian Thaeter <chth@gmx.net>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, contact me.
|
||||
|
||||
*/
|
||||
|
||||
:- module(persistence,
|
||||
[
|
||||
persistent_open/3,
|
||||
persistent_close/1,
|
||||
persistent_assert/1,
|
||||
persistent_retract/1
|
||||
]).
|
||||
|
||||
:- use_module(library(system),[]).
|
||||
|
||||
:- dynamic(persistent_desc/2).
|
||||
|
||||
/*
|
||||
persistent_open(PredDesc, File, Opts).
|
||||
|
||||
declare Module:Functor/Arity (Functor/Arity) to be persistent
|
||||
stored in File's (*.db *.log *log.$PID *.lock *.bak)
|
||||
|
||||
Opts are:
|
||||
db - use dbfile (flat file containing all persistent predicates)
|
||||
log - use logfile (logfile with either +(Term) for asserts and -(Term) for retracts)
|
||||
bak - make backupfiles when regenerating the dbfile
|
||||
sync - flush data always
|
||||
ro - readonly, can load locked files, never changes data on disk
|
||||
wo - (planned) writeonly, implies [log], data is only written to the log and not
|
||||
asserted into prolog, the database will not be loaded at persistent_open.
|
||||
conc - (planned) concurrency, extends the locking for multiple readers/single writer locks
|
||||
trans - (planned) support for transactions (begin/commit/abort)
|
||||
|
||||
Guides:
|
||||
- if the data mutates a lot, use [db,log].
|
||||
- if you mostly append data [log] suffices.
|
||||
- if the data is not important (can be regenerated) and mostly readonly then [db] is ok.
|
||||
- when using only [db] you must not forget to persistent_close!
|
||||
- for extra security against failures add [bak,sync].
|
||||
- don't use [bak] if you need to conserve disk space and the database is huge.
|
||||
- don't use [sync] if you need very fast writes.
|
||||
- turning all on [db,log,bak,sync] is probably the best, if you are undecided.
|
||||
- [ro,db] loads only the last saved db file.
|
||||
- [ro,log] loads the last saved db file if it exists and replays the log.
|
||||
- note that [ro] will fail if the db is not intact (.bak file present).
|
||||
|
||||
(planned features)
|
||||
- [wo] is very limited and only useful if you want to log data to a file
|
||||
- [wo,db] will replay the log at close
|
||||
- [conc] is useful for shareing data between prolog processes, but this is not a
|
||||
high performance solution.
|
||||
- [trans] can improve performance of concurrent access somewhat
|
||||
*/
|
||||
persistent_open(PredDesc, File, Opts) :-
|
||||
module_goal(PredDesc, Module:Functor/Arity),
|
||||
atom(Functor), integer(Arity), atom(File),
|
||||
\+ persistent_desc(Module:Functor/Arity,_),
|
||||
|
||||
atom_concat(File,'.db',DBfile),
|
||||
assertz(persistent_desc(Module:Functor/Arity,dbfile(DBfile))),
|
||||
|
||||
atom_concat(File,'.bak',Backupfile),
|
||||
assertz(persistent_desc(Module:Functor/Arity,backupfile(Backupfile))),
|
||||
|
||||
atom_concat(File,'.log',Logfile),
|
||||
assertz(persistent_desc(Module:Functor/Arity,logfile(Logfile))),
|
||||
|
||||
system:pid(Pid),
|
||||
assertz(persistent_desc(Module:Functor/Arity,pid(Pid))),
|
||||
|
||||
number_atom(Pid,P),
|
||||
atom_concat(Logfile,P,Mylogfile),
|
||||
assertz(persistent_desc(Module:Functor/Arity,mylogfile(Mylogfile))),
|
||||
|
||||
atom_concat(File,'.lock',Lockfile),
|
||||
assertz(persistent_desc(Module:Functor/Arity,lockfile(Lockfile))),
|
||||
|
||||
persistent_opts_store(Module:Functor/Arity,Opts),
|
||||
persistent_load(Module:Functor/Arity),
|
||||
|
||||
( \+ persistent_desc(Module:Functor/Arity, ro), persistent_desc(Module:Functor/Arity, log)
|
||||
-> open(Logfile, append, Log),
|
||||
assertz(persistent_desc(Module:Functor/Arity,logstream(Log)))
|
||||
; true
|
||||
).
|
||||
|
||||
/*
|
||||
closes the database associated with PredDesc ([Module:]Functor/Arity)
|
||||
*/
|
||||
persistent_close(PredDesc0) :-
|
||||
module_goal(PredDesc0,PredDesc),
|
||||
( persistent_desc(PredDesc, logstream(Log))
|
||||
-> close(Log)
|
||||
; true
|
||||
),
|
||||
persistent_save(PredDesc),
|
||||
persistent_desc(PredDesc, backupfile(Backupfile)),
|
||||
(system:delete_file(Backupfile,[ignore]); true),
|
||||
persistent_lock_release(PredDesc),
|
||||
retractall(persistent_desc(PredDesc,_)).
|
||||
|
||||
/*
|
||||
assert data to the database, this is always an assertz, if you need some ordering,
|
||||
then store some kind of key within your data.
|
||||
rules can be asserted too
|
||||
*/
|
||||
persistent_assert(Term) :-
|
||||
Term = (Head0 :- Body),
|
||||
module_goal(Head0, Module:Head),
|
||||
functor(Head, Functor, Arity),
|
||||
once(persistent_desc(Module:Functor/Arity,_)),!,
|
||||
( persistent_desc(Module:Functor/Arity, logstream(Log))
|
||||
-> writeq(Log,+(((Module:Head):-Body))), write(Log,'.\n'),
|
||||
( persistent_desc(Module:Functor/Arity, sync)
|
||||
-> flush_output(Log)
|
||||
; true
|
||||
)
|
||||
; true
|
||||
),
|
||||
assertz((Module:Head:-Body)).
|
||||
persistent_assert(Term0) :-
|
||||
module_goal(Term0, Module:Term),
|
||||
functor(Term,Functor,Arity),
|
||||
once(persistent_desc(Module:Functor/Arity,_)),!,
|
||||
( persistent_desc(Module:Functor/Arity,logstream(Log))
|
||||
-> writeq(Log,+(Module:Term)), write(Log,'.\n'),
|
||||
( persistent_desc(Module:Functor/Arity, sync)
|
||||
-> flush_output(Log)
|
||||
; true
|
||||
)
|
||||
; true
|
||||
),
|
||||
assertz(Module:Term).
|
||||
|
||||
/*
|
||||
retract a persistent Term
|
||||
*/
|
||||
persistent_retract(Term0) :-
|
||||
module_goal(Term0, Module:Term),
|
||||
functor(Term,Functor,Arity),
|
||||
once(persistent_desc(Module:Functor/Arity,_)),!,
|
||||
retract(Module:Term),
|
||||
( persistent_desc(Module:Functor/Arity, logstream(Log))
|
||||
-> writeq(Log,-(Module:Term)), write(Log,'.\n'),
|
||||
( persistent_desc(Module:Functor/Arity, sync)
|
||||
-> flush_output(Log)
|
||||
; true
|
||||
)
|
||||
; true
|
||||
).
|
||||
|
||||
% transaction support for future
|
||||
persistent_begin.
|
||||
persistent_commit.
|
||||
persistent_abort.
|
||||
|
||||
|
||||
/*
|
||||
|
||||
PRIVATE PREDICATES, DONT USE THESE
|
||||
|
||||
*/
|
||||
|
||||
% save all data to a .db file
|
||||
persistent_save(PredDesc) :-
|
||||
\+ persistent_desc(PredDesc,ro),
|
||||
( persistent_desc(PredDesc,db)
|
||||
-> persistent_desc(PredDesc,dbfile(DBfile)),
|
||||
(
|
||||
persistent_desc(PredDesc,bak)
|
||||
-> persistent_desc(PredDesc,backupfile(Backupfile)),
|
||||
( system:file_exists(DBfile)
|
||||
-> system:rename_file(DBfile,Backupfile)
|
||||
; true
|
||||
)
|
||||
; true
|
||||
),
|
||||
open(DBfile, write, S),
|
||||
persistent_writeall(PredDesc,S),
|
||||
close(S),
|
||||
persistent_desc(PredDesc,logfile(Logfile)),
|
||||
(system:delete_file(Logfile,[ignore]); true)
|
||||
; true
|
||||
).
|
||||
|
||||
% write all predicates matching Functor/Arity to stream S
|
||||
persistent_writeall(PredDesc, S) :-
|
||||
module_goal(PredDesc, Module:Functor/Arity),
|
||||
functor(Clause, Functor, Arity),
|
||||
clause(Module:Clause, Body),
|
||||
( Body = true
|
||||
-> writeq(S,Module:Clause)
|
||||
; writeq(S,(Module:Clause:-Body))
|
||||
),
|
||||
write(S,'.\n'),
|
||||
fail.
|
||||
persistent_writeall(_,_).
|
||||
|
||||
% load a database, recover logfile, recreate .db
|
||||
persistent_load(PredDesc) :-
|
||||
persistent_desc(PredDesc,dbfile(DBfile)),
|
||||
persistent_desc(PredDesc,backupfile(Backupfile)),
|
||||
persistent_desc(PredDesc,logfile(Logfile)),
|
||||
|
||||
( persistent_desc(PredDesc,ro)
|
||||
-> \+ system:file_exists(Backupfile),
|
||||
( system:file_exists(DBfile)
|
||||
-> persistent_load_file(DBfile)
|
||||
; true
|
||||
),
|
||||
( persistent_desc(PredDesc,log), system:file_exists(Logfile)
|
||||
-> persistent_load_file(Logfile)
|
||||
; true
|
||||
)
|
||||
;
|
||||
persistent_lock_exclusive(PredDesc),
|
||||
( system:file_exists(Backupfile)
|
||||
-> system:rename_file(Backupfile, DBfile)
|
||||
; true
|
||||
),
|
||||
( system:file_exists(DBfile)
|
||||
-> persistent_load_file(DBfile)
|
||||
; true
|
||||
),
|
||||
( system:file_exists(Logfile)
|
||||
-> persistent_load_file(Logfile),
|
||||
( persistent_desc(PredDesc, db)
|
||||
-> persistent_save(PredDesc)
|
||||
; true
|
||||
)
|
||||
; true
|
||||
)
|
||||
).
|
||||
|
||||
% load a .db file or replay a .log file
|
||||
persistent_load_file(File) :-
|
||||
open(File, read, S),
|
||||
repeat,
|
||||
read(S, TermIn),
|
||||
(
|
||||
TermIn == end_of_file,
|
||||
close(S),
|
||||
!
|
||||
;
|
||||
(
|
||||
TermIn = +(Term),
|
||||
assertz(Term)
|
||||
;
|
||||
TermIn = -(Term),
|
||||
retract(Term)
|
||||
;
|
||||
assertz(TermIn)
|
||||
),
|
||||
fail
|
||||
).
|
||||
|
||||
%lock handling, so far only exclusive locks
|
||||
persistent_lock_exclusive(PredDesc) :-
|
||||
persistent_desc(PredDesc,lockfile(Lockfile)),
|
||||
persistent_desc(PredDesc,pid(Pid)),
|
||||
open(Lockfile, append, Lockappend),
|
||||
write(Lockappend,lock(write,Pid)),write(Lockappend,'.\n'),
|
||||
close(Lockappend),
|
||||
open(Lockfile, read, Lockread),
|
||||
read(Lockread,LPid),
|
||||
close(Lockread),
|
||||
LPid = lock(_,Pid).
|
||||
|
||||
% recover lock
|
||||
persistent_lock_exclusive(PredDesc) :-
|
||||
persistent_desc(PredDesc, lockfile(Lockfile)),
|
||||
open(Lockfile, read, Lockread),
|
||||
read(Lockread,lock(_,LPid)),
|
||||
close(Lockread),
|
||||
\+ catch(kill(LPid,0),_,fail),
|
||||
(system:delete_file(Lockfile,[ignore]); true),
|
||||
%system:sleep(1),
|
||||
persistent_lock_exclusive(PredDesc).
|
||||
|
||||
persistent_lock_release(PredDesc) :-
|
||||
persistent_lock_exclusive(PredDesc),
|
||||
persistent_desc(PredDesc,lockfile(Lockfile)),
|
||||
(system:delete_file(Lockfile,[ignore]); true).
|
||||
|
||||
|
||||
persistent_opts_store(_,[]).
|
||||
persistent_opts_store(PredDesc,[H|T]) :-
|
||||
assertz(persistent_desc(PredDesc,H)),
|
||||
persistent_opts_store(PredDesc,T).
|
||||
|
||||
module_goal(Module:Goal,Module:Goal) :-
|
||||
callable(Goal), nonvar(Module),!.
|
||||
module_goal(Goal,Module:Goal) :-
|
||||
callable(Goal), prolog_flag(typein_module,Module).
|
Reference in New Issue
Block a user