All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.odysz.semantic.syn.ExessionPersist Maven / Gradle / Ivy

There is a newer version: 1.5.3
Show newest version
package io.odysz.semantic.syn;

import static io.odysz.common.LangExt.eq;
import static io.odysz.common.LangExt.isNull;
import static io.odysz.semantic.syn.ExessionAct.close;
import static io.odysz.semantic.syn.ExessionAct.exchange;
import static io.odysz.semantic.syn.ExessionAct.init;
import static io.odysz.semantic.syn.ExessionAct.mode_client;
import static io.odysz.semantic.syn.ExessionAct.mode_server;
import static io.odysz.semantic.syn.ExessionAct.ready;
import static io.odysz.semantic.syn.ExessionAct.restore;
import static io.odysz.semantic.syn.ExessionAct.signup;
import static io.odysz.semantic.syn.Nyquence.compareNyq;
import static io.odysz.semantic.syn.Nyquence.getn;
import static io.odysz.transact.sql.parts.condition.ExprPart.constVal;
import static io.odysz.transact.sql.parts.condition.ExprPart.constr;
import static io.odysz.transact.sql.parts.condition.Funcall.count;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

import io.odysz.common.CheapMath;
import io.odysz.common.Utils;
import io.odysz.module.rs.AnResultset;
import io.odysz.semantic.CRUD;
import io.odysz.semantic.DA.Connects;
import io.odysz.semantic.meta.PeersMeta;
import io.odysz.semantic.meta.SynChangeMeta;
import io.odysz.semantic.meta.SynSessionMeta;
import io.odysz.semantic.meta.SynSubsMeta;
import io.odysz.semantic.meta.SynchangeBuffMeta;
import io.odysz.semantic.meta.SynodeMeta;
import io.odysz.semantic.meta.SyntityMeta;
import io.odysz.semantic.util.DAHelper;
import io.odysz.semantics.SemanticObject;
import io.odysz.semantics.x.ExchangeException;
import io.odysz.semantics.x.SemanticException;
import io.odysz.transact.sql.Query;
import io.odysz.transact.sql.QueryPage;
import io.odysz.transact.sql.Statement;
import io.odysz.transact.sql.parts.Logic.op;
import io.odysz.transact.sql.parts.condition.ExprPart;
import io.odysz.transact.sql.parts.condition.Funcall;
import io.odysz.transact.x.TransException;

/**
 * Persisting exchange session with remote node, using temporary tables.
 * This is a session context, and different from {@link DBSyntext} which
 * is used for handling local data integration and database semantics. 
 * 
 * @author Ody
 */
public class ExessionPersist {
	final SynChangeMeta chgm;
	final SynSubsMeta subm;
	final SynchangeBuffMeta exbm;
	final SynodeMeta synm;
	final SynSessionMeta sysm;
	final PeersMeta pnvm;

	final String peer;

	final boolean debug;

	public AnResultset answerPage;

	public DBSyntableBuilder trb;

	// public HashMap nyquvect() { return trb.nyquvect; }
	// public Nyquence n0() { return trb.n0(); }

	/**
	 * Set nswers to the challenges, page {@link #challengeSeq}, with entities.
	 * 
	 * @param answer
	 * @param cols
	 * @return this
	 */
	ExessionPersist saveAnswer(AnResultset answer) {
		answerPage = answer;
		return this;
	}

	ExessionPersist commitAnswers(ExchangeBlock conf, String srcnode, long tillN0)
			throws SQLException, TransException {
		
		if (conf == null || conf.anspage == null || conf.anspage.getRowCount() <= 0)
			return this;
	
		List> stats = new ArrayList>();
	
		AnResultset rply = conf.anspage.beforeFirst();
		rply.beforeFirst();
		String recId = null;
		while (rply.next()) {
			if (compareNyq(rply.getLong(chgm.nyquence), tillN0) > 0)
				break; // FIXME continue? Or throw?
	
			SyntityMeta entm = trb.getEntityMeta(rply.getString(chgm.entbl));
			String change = rply.getString(ChangeLogs.ChangeFlag);
			HashMap entbuf = entities;
			
			String rporg  = rply.getString(chgm.domain);
			String rpent  = rply.getString(chgm.entbl);
			String rpuids = rply.getString(chgm.uids);
			String rpnodr = rply.getString(chgm.synoder);
			String rpscrb = rply.getString(subm.synodee);
			String rpcid  = rply.getString(chgm.pk);
	
			if (debug && !eq(change, CRUD.D)
				&& (entbuf == null || !entbuf.containsKey(entm.tbl) || entbuf.get(entm.tbl).rowIndex0(rpuids) < 0)) {
				Utils.warnT(new Object() {},
						"Missing entity. This happens when the entity is deleted locally.\n" +
						"entity name: %s\tsynode(peer): %s\tsynode(local): %s\tentity id(by peer): %s",
						entm.tbl, srcnode, trb.synode(), rpuids);
				continue;
			}
				
			stats.add(eq(change, CRUD.C)
				// create an entity, and trigger change log
				? !eq(recId, rpuids)
					? trb.insert(entm.tbl, trb.synrobot())
						.cols((String[])entm.entCols())
						.value(entm.insertChallengeEnt(rpuids, entbuf.get(entm.tbl)))
						.post(trb.insert(chgm.tbl)
							.nv(chgm.pk, rpcid)
							.nv(chgm.crud, CRUD.C)
							.nv(chgm.domain, rporg)
							.nv(chgm.entbl, rpent)
							.nv(chgm.synoder, rpnodr)
							.nv(chgm.uids, rpuids)
							// .nv(chgm.entfk, new Resulving(entm.tbl, entm.pk))
							.post(trb.insert(subm.tbl)
								.cols(subm.insertCols())
								.value(subm.insertSubVal(rply))))
					: trb.insert(subm.tbl)
						.cols(subm.insertCols())
						.value(subm.insertSubVal(rply))
	
				// remove subscribers & backward change logs's deletion propagation
				: trb.delete(subm.tbl, trb.synrobot())
					.whereEq(subm.changeId, rpcid)
					.whereEq(subm.synodee, rpscrb)
					.post(del0subchange(entm, rporg, rpnodr, rpuids, rpcid, rpscrb)
					));
			// entid = entid1;
			recId = rpuids;
		}
	
		Utils.logT(new Object() {}, "Locally committing answers to %s ...", peer);
		ArrayList sqls = new ArrayList();
		for (Statement s : stats)
			s.commit(sqls, trb.synrobot());
		Connects.commit(trb.synconn(), trb.synrobot(), sqls);
		
		// x.answer = null;
		return this;
	}

	/**
	 * Save local changes according to challenges by the peer.
	 * @param changes 
	 * @param cols [name, [NAME, index]]
	 * @param srcnv 
	 * @param entites 
	 * @return this
	 * @throws TransException 
	 * @throws SQLException 
	 */
	ExessionPersist saveChanges(AnResultset changes, HashMap srcnv,
			HashMap entites) throws SQLException, TransException {

		List> stats = new ArrayList>();

		changes.beforeFirst();

		HashSet missings = new HashSet(nyquvect.keySet());
		missings.removeAll(srcnv.keySet());
		missings.remove(trb.synode());

		while (changes.next()) {
			String change = changes.getString(chgm.crud);
			Nyquence chgnyq = getn(changes, chgm.nyquence);

			SyntityMeta entm = trb.getEntityMeta(changes.getString(chgm.entbl));

			String synodr = changes.getString(chgm.synoder);
			String chuids = changes.getString(chgm.uids);
			String chgid  = changes.getString(chgm.pk);

			HashMap entbuf = entites; // x.onchanges.entities;
			if (!eq(change, CRUD.D)
				&& (entbuf == null || !entbuf.containsKey(entm.tbl) || entbuf.get(entm.tbl).rowIndex0(chuids) < 0)) {
				Utils.warnT(new Object() {},
						"Missing entity. This happens when the peer has updated then deletd the entity.\n" +
						"entity name: %s\tsynode(answering): %s\tsynode(local): %s\tentity uid(by challenge): %s",
						entm.tbl, peer, trb.synode(), chuids);
				continue;
			}
			
			String domain = changes.getString(chgm.domain);
			String chentbl = changes.getString(chgm.entbl);
			
			// current entity's subscribes
			ArrayList> subscribeUC = new ArrayList>();

			if (eq(change, CRUD.D)) {
				String subsrb = changes.getString(subm.synodee);
				stats.add(trb.delete(subm.tbl, trb.synrobot())
					.whereEq(subm.synodee, subsrb)
					.whereEq(subm.changeId, chgid)
					.post(ofLastEntity(changes, chuids, chentbl, domain)
						? trb.delete(chgm.tbl)
							.whereEq(chgm.entbl, chentbl)
							.whereEq(chgm.domain, domain)
							.whereEq(chgm.synoder, synodr)
							.whereEq(chgm.uids, chuids)
							.post(trb.delete(entm.tbl)
								// .whereEq(entm.domain, domain)
								// .whereEq(entm.synoder, synodr)
								.whereEq(entm.synuid, chuids))
						: null));
			}
			else { // CRUD.C || CRUD.U
				boolean iamSynodee = false;

				while (changes.validx()) {
					String subsrb = changes.getString(subm.synodee);
					if (!nyquvect.containsKey(synodr))
						Utils.warn("This node (%s) don't care changes from %s, and sholdn't be here.",
								trb.synode(), synodr);

					if (compareNyq(chgnyq, nyquvect.get(synodr)) > 0
						&& eq(subsrb, trb.synode()))
						iamSynodee = true;
					else if (compareNyq(chgnyq, nyquvect.get(synodr)) > 0
						&& !eq(subsrb, trb.synode())
						)
						subscribeUC.add(trb.insert(subm.tbl)
							.cols(subm.insertCols())
							.value(subm.insertSubVal(changes))); 
					
					if (ofLastEntity(changes, chuids, chentbl, domain))
						break;
					changes.next();
				}

				appendMissings(stats, missings, changes);

				if (iamSynodee || subscribeUC.size() > 0) {
					stats.add(
					eq(change, CRUD.C)
					? trb.insert(entm.tbl, trb.synrobot())
						.cols(entbuf.get(entm.tbl).getFlatColumns0())
						.row(entbuf.get(entm.tbl).getColnames(),
								entbuf.get(entm.tbl).getRowById(chuids))
						.post(subscribeUC.size() <= 0 ? null :
							trb.insert(chgm.tbl)
							.nv(chgm.pk, chgid)
							.nv(chgm.crud, CRUD.C).nv(chgm.domain, domain)
							.nv(chgm.entbl, chentbl).nv(chgm.synoder, synodr)
							.nv(chgm.nyquence, changes.getLong(chgm.nyquence))
							.nv(chgm.seq, trb.incSeq())
							.nv(chgm.uids, chuids)
							.post(subscribeUC)
							.post(del0subchange(entm, domain, synodr, chuids, chgid, trb.synode())))
					: eq(change, CRUD.U)
					? trb.update(entm.tbl, trb.synrobot())
						.nvs(entm.updateEntNvs(chgm, chuids, entbuf.get(entm.tbl), changes))
						// .whereEq(entm.synoder, synodr)
						// .whereEq(entm.domain, domain)
						.whereEq(entm.synuid, chuids)
						.post(subscribeUC.size() <= 0
							? null : trb.insert(chgm.tbl)
							.nv(chgm.pk, chgid)
							.nv(chgm.crud, CRUD.U).nv(chgm.domain, domain)
							.nv(chgm.entbl, chentbl).nv(chgm.synoder, synodr)
							.nv(chgm.nyquence, chgnyq.n)
							.nv(chgm.seq, trb.incSeq())
							.nv(chgm.uids, constr(chuids))
							.nv(chgm.updcols, changes.getString(chgm.updcols))
							.post(subscribeUC)
							.post(del0subchange(entm, domain, synodr, chuids, chgid, trb.synode())))
					: null);
				}

				subscribeUC = new ArrayList>();
				iamSynodee  = false;
			}
		}

		if (Connects.getDebug(trb.synconn()))
			Utils.logT(new Object() {}, "saving changes to local entities...");

		ArrayList sqls = new ArrayList();
		for (Statement s : stats)
			if (s != null)
				s.commit(sqls, trb.synrobot());
		Connects.commit(trb.synconn(), trb.synrobot(), sqls);
		
		return this;
	}

	/**
	 * Create context at client side.
	 * @param tb 
	 * @param chgm
	 * @param localtb local transaction builder
	 * @param target
	 * @param exbm 
	 * @param subm 
	 * @param builder 
	 */
	public ExessionPersist(DBSyntableBuilder tb, String target) {
		this(tb, target, null);
	}

	/**
	 * Create context at server side.
	 * @param tb 
	 * @param session session id supplied by client
	 * @param chgm
	 * @param localtb
	 * @param target
	 */
	public  ExessionPersist(DBSyntableBuilder tb, String peer, ExchangeBlock ini) {

		if (tb != null && eq(tb.synode(), peer))
			Utils.warn("Creating persisting context for local builder, i.e. peer(%s) = this.synode?", peer);;

		this.trb = tb.xp(this);
		this.exbm = tb.exbm;
		this.session = ini == null ? null : ini.session;
		this.peer = peer;
		this.chgm = tb.chgm;
		this.subm = tb.subm;
		this.synm = tb.synm;
		this.sysm = new SynSessionMeta(tb.synconn());
		this.pnvm = tb.pnvm;
		this.exstate = new ExessionAct(mode_server, ready);
		this.chsize = 480;

		debug = trb == null ? true : Connects.getDebug(trb.synconn());
	}

	public ExchangeBlock signup(String admin) throws SQLException, TransException {
		nyquvect = loadNyquvect(trb);
		exstate.state = signup;
	
		return new ExchangeBlock(trb == null
				? null
				: trb.synode(), peer, session, exstate)
					.totalChallenges(1)
					.chpagesize(this.chsize)
					.seq(this);
	}

	/**
	 * Setup exchange buffer table.
	 * 
	 * exstate.state = init;
	 * expAnswerSeq = 0;
	 * challengeSeq = 0;
	 * answerSeq = 0;
	 * 
* @param i * @return * @return * @throws TransException * @throws SQLException * @throws SemanticException */ public ExchangeBlock init() throws TransException, SQLException { if (trb != null) { nyquvect = loadNyquvect(trb); Nyquence dn = nyquvect.get(peer); if (dn == null) { throw new ExchangeException(ready, this, "%s:%s(), doesn't have knowledge about %s.", trb.synode(), new Object(){}.getClass().getEnclosingMethod().getName(), peer); } } else Utils.warnT(new Object() {}, "Null transaction builder. - null builder only for test"); challengeSeq = -1; expAnswerSeq = -1; //challengeSeq; answerSeq = -1; if (trb != null) totalChallenges = DAHelper.count(trb, trb.synconn(), exbm.tbl, exbm.peer, peer); exstate = new ExessionAct(mode_client, init); return new ExchangeBlock(trb == null ? null : trb.synode(), peer, session, exstate) .totalChallenges(totalChallenges) .chpagesize(this.chsize) .seq(persistarting(peer)) .nv(nyquvect); } /** * insert into exchanges select * from change_logs where n > nyquvect[sx.peer].n * * @param ini * @return new message * @throws TransException * @throws SQLException */ public ExchangeBlock onInit(ExchangeBlock ini) throws TransException, SQLException { if (trb != null) { nyquvect = loadNyquvect(trb); String conn = trb.basictx().connId(); int total = ((SemanticObject) trb .insertExbuf(peer) .ins(trb.instancontxt(conn, trb.synrobot())) ).total(); if (total > 0 && Connects.getDebug(trb.synconn())) { Utils.logi("Changes in buffer for %s -> %s: %s", trb.synode(), peer, total); } } else Utils.warnT(new Object() {}, "Null transaction builder. - null builder only for test"); challengeSeq = -1; expAnswerSeq = ini.answerSeq; answerSeq = ini.challengeSeq; if (trb != null) totalChallenges = DAHelper.count(trb, trb.synconn(), exbm.tbl, exbm.peer, peer); chsize = ini.chpagesize > 0 ? ini.chpagesize : -1; exstate = new ExessionAct(mode_server, init); return new ExchangeBlock(trb == null ? ini.peer : trb.synode(), peer, session, exstate) .totalChallenges(totalChallenges) .chpagesize(ini.chpagesize) .seq(persistarting(peer)) .nv(nyquvect); } public void clear() { } private String session; public String session() { return session; } private ExessionAct exstate; public int exstate() { return exstate.state; } public ExessionPersist exstate(int state) { exstate.state = state; return this; } public ExessionAct exstat() { return exstate; } /** * Counted when in {@link #init()}, and not correct after * {@link DBSyntableBuilder#cleanStale(HashMap, String)} has been called. */ public int totalChallenges; public int expAnswerSeq; /** Challenging sequence number, i. e. current page */ public int challengeSeq; /** challenge page size */ protected int chsize; public int answerSeq; /** * Has another page in {@link SynchangeBuffMeta}.tbl to be send to. * @param b synchronizing transaction builder * @return true if has another page * @throws SQLException * @throws TransException */ public boolean hasNextChpages(DBSyntableBuilder b) throws SQLException, TransException { int pages = pages(); if (pages > 0 && challengeSeq + 1 < pages) return true; else return false; } public ExessionPersist expect(ExchangeBlock req) throws ExchangeException { if (req == null) return this; if (!eq(req.srcnode, this.peer) || !eq(session, req.session)) throw new ExchangeException(ExessionAct.unexpected, this, "Session Id or peer mismatched [%s : %s vs %s : %s]", this.peer, session, req.srcnode, req.session); if (req.challengeSeq >= 0 && (req.act != restore && answerSeq + 1 != req.challengeSeq)) throw new ExchangeException(ExessionAct.unexpected, this, "Challenge page lost, expecting %s", answerSeq + 1); if (req.act == restore || expAnswerSeq == 0 && req.answerSeq == -1 // first exchange || expAnswerSeq == req.answerSeq) { answerSeq = req.challengeSeq; return this; } throw new ExchangeException(ExessionAct.unexpected, this, "for challenge %s, got answer %s, expecting %s", req.challengeSeq, req.answerSeq, expAnswerSeq); } public ExchangeBlock nextExchange(ExchangeBlock rep) throws SQLException, TransException { ExessionPersist me = expect(rep); //.exstate(exchange); me.exstate(nextChpage() ? exchange : close); return trb == null // null for test ? new ExchangeBlock(rep.peer, peer, session, me.exstate).seq(this) : trb.exchangePage(this, rep); } private boolean nextChpage() throws TransException, SQLException { int pages = pages(); if (challengeSeq < pages) { challengeSeq++; if (trb != null) { // select ch.*, synodee from changelogs ch join syn_subscribes limit 100 * i, 100 QueryPage page = (QueryPage) trb .selectPage(trb .select(exbm.tbl, "bf") .col(exbm.pagex, "page") .cols_byAlias("bf", exbm.changeId) .col("sub." + subm.synodee) .je_(chgm.tbl, "chg", exbm.changeId, chgm.pk, exbm.peer, Funcall.constr(peer)) .je_(subm.tbl, "sub", "chg." + chgm.pk, subm.changeId) .whereEq(exbm.peer, peer)) .page(challengeSeq, chsize) .col(exbm.changeId); trb.update(exbm.tbl, trb.synrobot()) .nv(exbm.pagex, challengeSeq) .whereIn(exbm.changeId, page) .u(trb.instancontxt(trb.synconn(), trb.synrobot())) ; } } expAnswerSeq = challengeSeq < pages ? challengeSeq : -1; return challengeSeq < pages; } /** * Reset to last page * @return this */ ExessionPersist pageback() { if (challengeSeq < 0) return this; challengeSeq--; expAnswerSeq = challengeSeq; return this; } ExchangeBlock exchange(String peer, ExchangeBlock rep) throws TransException, SQLException { /* if (exstate.state == restore && rep.act == restore) ; // got answer else if (exstate.state != init && exstate.state != exchange) throw new ExchangeException(exchange, this, "Can't handle exchanging states from %s to %s.", ExessionAct.nameOf(exstate.state), ExessionAct.nameOf(exchange)); */ if (rep != null) answerSeq = rep.challengeSeq; expAnswerSeq = challengeSeq < pages() ? challengeSeq : -1; AnResultset rs = chpage(); // exstate.state = exchange; return new ExchangeBlock(trb == null ? rep.peer : trb.synode(), peer, session, exstate) .chpage(rs, entities) .totalChallenges(totalChallenges) .chpagesize(this.chsize) .seq(this) .nv(nyquvect); } ExchangeBlock onExchange(String peer, ExchangeBlock req) throws TransException, SQLException { if (req != null) answerSeq = req.challengeSeq; expAnswerSeq = challengeSeq < pages() ? challengeSeq : -1; exstate.state = exchange; return new ExchangeBlock(trb == null ? req.peer : trb.synode(), peer, session, exstate) .chpage(chpage(), entities) .totalChallenges(totalChallenges) .chpagesize(this.chsize) .seq(this) .nv(nyquvect); } public ExchangeBlock closexchange(ExchangeBlock rep) throws ExchangeException { /* enable? if (exstate.state != init && exstate.state != exchange) throw new ExchangeException(exchange, this, "Can't handle closing state on state %s", exstate.state); */ try { expAnswerSeq = -1; if (rep != null) answerSeq = rep.challengeSeq; else answerSeq = -1; challengeSeq = -1; exstate.state = ready; return new ExchangeBlock(trb == null ? rep.peer : trb.synode(), peer, session, new ExessionAct(exstate.mode, close)) .totalChallenges(totalChallenges) .chpagesize(this.chsize) .seq(this); } finally { if (trb != null) try { trb.delete(exbm.tbl, trb.synrobot()) .whereEq(exbm.peer, peer) .d(trb.instancontxt(trb.synconn(), trb.synrobot())); } catch (TransException | SQLException e) { e.printStackTrace(); } finally { // clean unaccepted subscriptions trb.cleanStaleSubs(peer); } } } public ExchangeBlock abortExchange() { try { expAnswerSeq = -1; answerSeq = -1; challengeSeq = -1; totalChallenges = 0; exstate.state = ready; return new ExchangeBlock(trb == null ? null : trb.synode(), peer, session, new ExessionAct(exstate.mode, close)) .totalChallenges(totalChallenges) .chpagesize(this.chsize) .seq(this); } finally { try { trb.delete(exbm.tbl, trb.synrobot()) .whereEq(exbm.peer, peer) .d(trb.instancontxt(trb.synconn(), trb.synrobot())); } catch (TransException | SQLException e) { e.printStackTrace(); } } } /** * Retry last page * @param peer * @return request message * @throws SQLException * @throws TransException */ public ExchangeBlock retryLast(String peer) throws TransException, SQLException { pageback(); nextChpage(); exstate.state = restore; // expAnswerSeq = challengeSeq; // see nextChpage() return new ExchangeBlock(trb == null ? null : trb.synode(), peer, session, exstate) .requirestore() .totalChallenges(totalChallenges) .chpagesize(this.chsize) .seq(this); } // public ExchangeBlock onRetryLast(String peer, ExchangeBlock req) throws TransException, SQLException { // if (!eq(session, req.session)) // throw new ExchangeException(ExessionAct.unexpected, this, // "[local-session, peer, req-session]:%s,%s,%s", session, peer, req.session); // // exstate.state = restore; // // if (req.challengeSeq > answerSeq) // // previous request has been lost // onExchange(peer, req); // // return new ExchangeBlock(trb == null ? req.peer : trb.synode(), peer, session, exstate) // .requirestore() // .totalChallenges(totalChallenges) // .chpagesize(this.chsize) // .seq(this); // } HashMap entities; /** * Information used by upper level, such as semantic.jser. */ public String[] ssinf; /** Nyquence vector {synode: Nyquence}*/ HashMap nyquvect; public Nyquence n0() { return nyquvect.get(trb.synode()); } protected ExessionPersist n0(Nyquence nyq) { nyquvect.put(trb.synode(), new Nyquence(nyq.n)); return this; } /** * this.n0++, this.n0 = max(n0, maxn) * @param maxn * @return n0 * @throws SQLException * @throws TransException */ public Nyquence incN0(Nyquence... nomoreThan) throws TransException, SQLException { n0().inc(isNull(nomoreThan) ? nyquvect.get(trb.synode()).n : nomoreThan[0].n); DAHelper.updateFieldByPk(trb, trb.synconn(), synm, trb.synode(), synm.nyquence, new ExprPart(n0().n), trb.synrobot()); return n0(); } public ExessionPersist loadNyquvect(String conn) throws SQLException, TransException { nyquvect = loadNyquvect(trb); return this; } public static HashMap loadNyquvect(DBSyntableBuilder t) throws SQLException, TransException { AnResultset rs = ((AnResultset) t.select(t.synm.tbl) .cols(t.synm.pk, t.synm.nyquence, t.synm.nstamp) .rs(t.instancontxt(t.synconn(), t.synrobot())) .rs(0)); HashMap nyquvect = new HashMap(rs.getRowCount()); while (rs.next()) { nyquvect.put(rs.getString(t.synm.synoder), new Nyquence(rs.getLong(t.synm.nyquence))); } return nyquvect; } /** *

Get a challenging page.

* * Entities affected are also be loaded, and the entity metas get a chance to handling * the loaded records by calling {@link SyntityMeta#onselectSyntities(Query)}. * * @return a change log page * @throws SQLException * @throws TransException */ AnResultset chpage() throws TransException, SQLException { // if (trb == null) return null; // test Nyquence dn = nyquvect.get(peer); AnResultset entbls = (AnResultset) trb.select(chgm.tbl, "ch") .je_(exbm.tbl, "bf", chgm.pk, exbm.changeId, "bf." + exbm.peer, constr(peer), constVal(challengeSeq), exbm.pagex) .col(chgm.entbl) .where(op.gt, chgm.nyquence, dn.n) // FIXME .groupby(chgm.entbl) .rs(trb.instancontxt(trb.synconn(), trb.synrobot())) .rs(0); while (entbls.next()) { String tbl = entbls.getString(chgm.entbl); SyntityMeta entm = trb.getSyntityMeta(tbl); AnResultset entities = ((AnResultset) entm .onselectSyntities(trb.select(tbl, "e").cols_byAlias("e", entm.entCols())) .je_(chgm.tbl, "ch", "ch." + chgm.entbl, constr(tbl), entm.synuid, chgm.uids) .je_(exbm.tbl, "bf", "ch." + chgm.pk, exbm.changeId, constr(peer), exbm.peer, constVal(challengeSeq), exbm.pagex) .rs(trb.instancontxt(trb.synconn(), trb.synrobot())) .rs(0)) .index0(entm.synuid); entities(tbl, entities); } return trb == null ? null : (AnResultset)trb .select(chgm.tbl, "ch") .cols(exbm.pagex, "ch.*", "sb." + subm.synodee) .je_(exbm.tbl, "bf", chgm.pk, exbm.changeId, constr(peer), exbm.peer, constVal(challengeSeq), exbm.pagex) .je_(subm.tbl, "sb", chgm.pk, subm.changeId) .orderby(chgm.synoder) .orderby(chgm.entbl) .orderby(chgm.seq) .rs(trb.instancontxt(trb.synconn(), trb.synrobot())) .rs(0); } public ExessionPersist entities(String tbl, AnResultset ents) { if (entities == null) entities = new HashMap(); entities.put(tbl, ents); return this; } public int pages() { return CheapMath.blocks(totalChallenges, chsize); } /** * update syn_node set session = {session, challengeSeq, answers, ...} * @return this * @throws SQLException * @throws TransException */ public ExessionPersist persisession() throws TransException, SQLException { if (trb != null) { sysm.update(trb.update(sysm.tbl, trb.synrobot())) .nv(sysm.chpage, challengeSeq) .nv(sysm.answerx, answerSeq) .nv(sysm.expansx, expAnswerSeq) .nv(sysm.mode, exstate.mode) .nv(sysm.state, exstate.state) .whereEq(sysm.peer, peer) .u(trb.instancontxt(trb.synconn(), trb.synrobot())); } return this; } /** * Save starting session. * @param peer * @return this * @throws TransException * @throws SQLException */ public ExessionPersist persistarting(String peer) throws TransException, SQLException { if (trb != null) { trb.delete(sysm.tbl, trb.synrobot()) .whereEq(sysm.peer, peer) .post(sysm.insertSession(trb.insert(sysm.tbl), peer)) .d(trb.instancontxt(trb.synconn(), trb.synrobot())); } return this; } /////////////////////////////////////////////////////////////////////////////////// ExessionPersist forcetest(int total, int... chsize) { totalChallenges = total; if (!isNull(chsize)) this.chsize = chsize[0]; return this; } /** * Append inserting statement for missing subscribes, for the knowledge * that the source node dosen't know. * * @param stats * @param missing missing knowledge of synodees that the source node doesn't know * @param chlog * @return {@code missing } * @throws TransException * @throws SQLException */ HashSet appendMissings(List> stats, HashSet missing, AnResultset chlog) throws TransException, SQLException { if(missing != null && missing.size() > 0 && eq(chlog.getString(chgm.crud), CRUD.C)) { String domain = chlog.getString(chgm.domain); String entbl = chlog.getString(chgm.entbl); String uids = chlog.getString(chgm.uids); for (String sub : missing) stats.add(trb.insert(subm.tbl) .cols(subm.insertCols()) .value(subm.insertSubVal(domain, entbl, sub, uids))); } return missing; } /** * Is next row in {@code chlogs} a change log for different entity? * @param chlogs * @param curEid * @param curEntbl * @param curDomain * @return true if next row is new enitity's change log. * @throws SQLException */ boolean ofLastEntity(AnResultset chlogs, String curEid, String curEntbl, String curDomain) throws SQLException { return !chlogs.hasnext() || !eq(curEid, chlogs.nextString(chgm.uids)) || !eq(curEntbl, chlogs.nextString(chgm.entbl)) || !eq(curDomain, chlogs.nextString(chgm.domain)); } boolean isAnotherEntity (AnResultset chlogs, String curUid, String curEntbl, String curDomain) throws SQLException { return !chlogs.hasprev() || !eq(curUid, chlogs.prevString(chgm.uids)) || !eq(curEntbl, chlogs.prevString(chgm.entbl)) || !eq(curDomain, chlogs.prevString(chgm.domain)); } /** * Generate delete statement when change logs don't have synodees. * * @param entitymeta * @param org * @param synoder * @param uids * @param deliffnode delete the change-log iff the node, i.e. only the subscriber, exists. * For answers, it's the node himself, for challenge, it's the source node. * @return the delete statement * @throws TransException */ Statement del0subchange(SyntityMeta entitymeta, String org, String synoder, String uids, String changeId, String deliffnode) throws TransException { return trb.delete(chgm.tbl) // delete change log if no subscribers exist .whereEq(chgm.pk, changeId) .whereEq(chgm.entbl, entitymeta.tbl) .whereEq(chgm.domain, org) .whereEq(chgm.synoder, synoder) .whereEq("0", (Query)trb.select(subm.tbl) .col(count(subm.synodee)) .whereEq(chgm.pk, changeId) .where(op.eq, chgm.pk, subm.changeId) .where(op.ne, subm.synodee, constr(deliffnode))) ; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy