Though the Entity Sync mechanism can be used with the POS, it was mostly destined to it. It works well on a reliable network, it's harder on Internet...
POS is now in Attic
With - OFBIZ-7804Getting issue details... STATUS the POS has been pushed in OFBiz Attic.
The last released version with the POS available was R12.04.06. It's though still available in the R14.12 and 15.12 non released branches. It'll not be availble with the R16 release. All the documentation below is reliable and usable before revision 1754402
Sync Setup Notes and Example
Using Entity Synchronization
The general idea behind the Entity Sync stuff is to re-create transactions from a central database into one or more other databases, each represented by a set of record in the EntitySync and related entities.
The four fields (lastUpdatedStamp, lastUpdatedTxStamp, createdStamp, createdTxStamp) are added to each entity and automatically populated so that queries can be done on each entity to get all records created, updated, or removed in a certain transaction, and so that in time order those transactions can be reproduced remotely to get the data into the remote database in the proper order.
The enable-lock attribute on the entity element in the XML entity definitions only loosely relates to entity synchronization. This optimistic locking mechanism uses the lastUpdatedStamp of the entity to be stored and the current entity in the database to see if it was modified by another process before the current thread gets to it. If it was modified between the read and write an exception is thrown. This check is only done if enable-lock="true".
The no-auto-stamp attribute is used to tell the Entity Engine not to automatically add these stamp fields, and not to automatically set the stamps when creating and updating the entities. If it is not set to false, or if it is set to true then these values will be automatically set, and when records are removed the primary key will be saved in the EntitySyncRemove entity's table so that the record can be removed in the remote database.
When runEntitySync is called you pass an entitySyncId that is used to look up an EntitySync record and its corresponding EntitySyncInclude records to see what should be sync'ed. The package on the EntitySyncInclude refers to the package-name attribute on the entity definition XML element, and partial package names can be included with an implied wild-card at the end. The EntitySyncHistory entity is used to store information about what has happened in each entity sync run, which means one runEntitySync call.
For the remote call to save the sync data, which protocol to use depends on your requirements. The most efficient by far is the RMI remote dispatcher. If you only have port 80, or another HTTP port open, then the HTTP remote dispatcher may be your only choice.
When a runEntitySync call fails the runStatusId will stay in ESR_RUNNING. When the server crashes or the process is interrupted in some other way the thread has no real way to reset the status. This needs to be manually monitored and reset when necessary to ESR_NOT_STARTED or some other status that will be picked up for running. It could perhaps be automatically resetted...
Default example in OFBiz
The example in OFBiz (in specialpurpose/pos/data/PosSyncSettings.xml) is designed for a 3 levels architecture, where Per Store Servers (PSS) are used between POS terminals and the Master Central Server (MCS).
Here is a short explanation on how it works
This setting is intended to synchronise a main database (on the MCS) with the databases of local stores servers (PSS) each of them being synchronised with their local POS terminals. Each of these OFBiz instances use a database. Store here means a physical store where POS terminals are.
There are 4 types of flow :
- MCS -> PSS
- PSS -> POS
- POS -> PSS
- PSS -> MCS
Where
- MCS is a central OFBiz server (with one database, the central database where all products information are maintained and where the consolidation is done)
- PSS is an instance of a server store (with one database, the store database where all store inventory informations are stored)
- POS is an instance of a POS terminal (with one database, the POS database where terminal information are collected)
- The flow MCS -> PSS is used to update catalogs, from the central database where the main catalogs are maintained, to the stores. This include roughly pricing, promotions, basic product info, etc.
- The flow PSS -> POS is used to roughly update catalogs of the POS terminals
- The flow POS -> PSS is used to roughly update orders, payments , etc. of the store
- The flow PSS -> MCS is used to roughly update orders, payments, inventory, etc. in the main database.
This scheme is very powerful and help you to extend your net of stores has needed. Remember that this is only an example and you can extend it from your needs.
In the commented examples the MCS update the PSS, hence POS catalogs, every 2 hours. And the POS terminals update MCS database about orders every 5 minutes .
Another example with some questions (extracted from the OFBiz wiki archive)
EntitySync configuration
configuring peers - 'HOW ?'
configuring what entities to sync - 'HOW ?'
Say one wants to set up a few servers in the following way:
HOSM : Head-office of master-data (maintained there)
HOST : Head-office of transactional-data (collected from sites)
BOS1, BOS2, ... BOSx: Back-office (many small sites)
(most important) flows:
HOSM => HOST, BOSx: Product, ProductCategory, Facility, Parties for the facilities (and at least 50 other Entities)
BOSx => HOST: Order, and Party, Person, ContactMech, etc... related to the orders (and some other)
Filtering what to sync
With the above approach it is possible to configure at entity level: fully sync some entities and not sync others. If the HOSM and HOST were merged to a HOS (which I don't like for other reasons), there would be a need to filter (at least) Party,Person records:
HOS => BOSx : 'only send Person, Party, ContactMech records that are related to Facility (not those objects that are related to Order originated at other BOSy)'.
BOSx => HOS : transfer everything, easy
Would this be possible with current implementation of EntitySync? How is this configurable? Note that even if referred objects are synced automagically (to preserve foreign-keys):
syncing Facility (HOS => BOSx direction) could bring the Party objects referred by the Facility objects
but Party does not refer to Person (because of the direction of the relation: Person has the partyId), but the Person, ContactMech objects (that belong to the Facility) would be desirable to sync as well
configuration on Entity level: does not support the above filtering
configuration on Entity and entity-relations level: entity-relations could be switched to 'follow / not follow' (naturally, there is no choice for direct references, those are always "follow" to preserve foreign key integrity). These flags would determine if the related objects are to be synced or not. Note: final decision is for Objects, not for classes (all objects of an Entity).
A simple but more detailled example
The following example is based on the PosSyncSettings.xml file. It's intended to be used where POS terminals are directly connected to a Master Central Server (MCS)
Here are some explanation and step by step instructions on setting up synchronization.
Entities used
- EntiyGroup
EntityGroup and EntityGroupEntry records are only loaded on Master Central Server (MCS).
When the "PULL" sync occurs those records are copied down to the POS terminal(s).
This allows for changes to be made on MCS and propagate down to terminal(s).
Both sets of EntityGroup records (5501 and 5506) are reused across terminal(s). No need to have separate EntityGroup records for each terminal.
- EntitySync (PULL)
EntitySync record is loaded on MCS.
entitySyncId must be unique for each terminal so we use the POS terminal ID.
- EntitySync (PUSH)
EntitySync record is loaded on POS terminal.
Can use the same entitySyncId since it is loaded on each terminal, not MCS.
- RecurrenceRule, RecurrenceInfo, RuntimeData
Both of these sets of records are loaded on the POS terminal(s).
This initiates PUSH and PULL transfers and these are always initiated from the terminal(s), not MCS.
Step by Step Instructions
- Setup POS terminal with clean checkout from trunk
- In startofbiz.sh/bat set RMIIF to localhost (uncomment the line.)
- Create empty ofbiz database and populate with seed data (ant run-install-seed).
Note that for testing/learning you can load demo data as well.
For a production environment you will need to only load seed, then do all of your organization specific setup such as accounting, facilities, stores, etc. - framework/service/config/serviceengine.xml - enity-sync-rmi set to MCS.
- Start POS instance(s) (the OFBiz instance(s) which runs the POS, aka POS terminal(s)) in standard mode (web interface)
- Load EntitySync record for PULL on MCS instance (the OFBiz instance which runs the MCS)
- Load XML data you want to setup on POS instance to trigger PULL sync
- Load EntitySync PUSH on POS instance(s).
- When pull is finished, load XML data you want to setup to trigger PUSH
- Shutdown POS instance(s)
- pos-containers.xml on POS instance(s) - set xui session id = <facilityId-pos terminal number> ex. 10020-1.
- pos-containers.xml on POS instance(s) - set faclility ID = <facilityId) ex. 10020.
Don't forget to change entityengine.xml on all POS instance(s) to include a prefix for record id's.
Otherwise you will get record id conflicts when instance(s) push records to the MCS.
This is done by adding the "sequenced-id-prefix" attribute to the default delegator. For example:<delegator name="default" entity-model-reader="main" entity-group-reader="main" entity-eca-reader="main" distributed-cache-clear-enabled="false" sequenced-id-prefix="10020-1"> <group-map group-name="org.ofbiz" datasource-name="localpostgres"/> <group-map group-name="org.ofbiz.olap" datasource-name="localpostgres"/> </delegator>
- Add PosTerminal record to MCS database.
You must create a PosTerminal record for each POS instance you need to synchronize with MCS.
Once this record is added you can start your POS instance and attempt a synchronization. - Start your POS instance in rich client mode. (add -pos after ofbiz.jar in startup file.)
Special note on facilityId and terminal numbers.
The facilityId is generated automatically when facilities are created.
The terminal number must be unique so for our implementation we used <facilityId-1,2,n> as a convention for identifying terminals.
This is not required. Use whatever convention suits your purpose.
We also used this number as the prefix in entityengine.xml but it makes record id's rather long.
<entity-engine-xml> <!-- MCS->POS Per Store Server to POS Terminal This data is loaded on the Master Central Server --> <EntityGroup entityGroupId="5501" entityGroupName="POS: Per Store Server to POS Terminal"/> <!-- these are used to update sync settings on the POS terminals from the central server --> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.entity.group" applEnumId="ESIA_INCLUDE"/> <!-- this is the general product information needed for pricing, promotions, basic product info, etc --> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.product.product" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.product.price" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.product.promo" applEnumId="ESIA_INCLUDE"/> <!-- org.ofbiz.product.category is needed for certain promotions, probably won't be used much for category browsing in the POS --> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.product.category" applEnumId="ESIA_INCLUDE"/> <!-- org.ofbiz.party.party may be needed for certain promotions --> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.party.party" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.pos.PosTerminal" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.product.storage" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.product.store" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.accounting.tax" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.security.login" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.security.securitygroup" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.party.contact" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5501" entityOrPackage="org.ofbiz.product.inventory" applEnumId="ESIA_INCLUDE"/> <!-- POS->MCS Per Store Server to Central Server --> <EntityGroup entityGroupId="5506" entityGroupName="POS: Per Store Server to Central Server"/> <!-- org.ofbiz.party.party may be needed for order related information, like party roles or contact info etc --> <EntityGroupEntry entityGroupId="5506" entityOrPackage="org.ofbiz.party.party" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5506" entityOrPackage="org.ofbiz.party.contact" applEnumId="ESIA_INCLUDE"/> <!-- all order and related information, including payment, etc info --> <EntityGroupEntry entityGroupId="5506" entityOrPackage="org.ofbiz.order" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5506" entityOrPackage="org.ofbiz.accounting.invoice" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5506" entityOrPackage="org.ofbiz.accounting.payment" applEnumId="ESIA_INCLUDE"/> <!-- include inventory and issuance info --> <EntityGroupEntry entityGroupId="5506" entityOrPackage="org.ofbiz.shipment.issuance" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5506" entityOrPackage="org.ofbiz.product.inventory" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5506" entityOrPackage="org.ofbiz.pos.PosTerminalLog" applEnumId="ESIA_INCLUDE"/> <EntityGroupEntry entityGroupId="5506" entityOrPackage="org.ofbiz.pos.PosTerminalState" applEnumId="ESIA_INCLUDE"/> <!-- include POS transactions --> <EntityGroupEntry entityGroupId="5506" entityOrPackage="org.ofbiz.pos.PosTerminalInternTx" applEnumId="ESIA_INCLUDE"/> <!-- Some examples of EntitySync settings for pulling and pushing data around --> <!-- POS example meant to be used for pushing data from POS to MCS --> <EntitySync entitySyncId="POSPUSH" runStatusId="ESR_NOT_STARTED" syncSplitMillis="600000" targetServiceName="remoteStoreEntitySyncDataRmi" keepRemoveInfoHours="24" forPushOnly="Y"/> <EntitySyncIncludeGroup entitySyncId="POSPUSH" entityGroupId="5506"/> <!-- POS example meant to be used for pulling data from MCS to POS --> <!-- NOTE: for pulling setup the EntitySync record lives on the server the data is pulled from and should ONLY BE USED FOR ONE CLIENT! --> <EntitySync entitySyncId="<POS Terminal ID>" runStatusId="ESR_NOT_STARTED" syncSplitMillis="600000" keepRemoveInfoHours="24" forPullOnly="Y"/> <EntitySyncIncludeGroup entitySyncId="<POS Terminal ID>" entityGroupId="5501"/> <!-- POS Push Scheduled Service --> <!-- <RecurrenceRule recurrenceRuleId="POSPUSH" untilDateTime="" frequency="MINUTELY" intervalNumber="5" countNumber="-1"/> <RecurrenceInfo recurrenceInfoId="POSPUSH" startDateTime="2000-01-01 00:00:00.000" recurrenceRuleId="POSPUSH" recurrenceCount="0"/> <RuntimeData runtimeDataId="POSPUSH"> <runtimeInfo><![CDATA[<?xml version="1.0" encoding="UTF-8"?> <ofbiz-ser> <map-HashMap> <map-Entry> <map-Key> <std-String value="entitySyncId"/> </map-Key> <map-Value> <std-String value="POSPUSH"/> </map-Value> </map-Entry> </map-HashMap> </ofbiz-ser> ]]></runtimeInfo> </RuntimeData> <JobSandbox jobId="POSPUSH" jobName="Push POS Data" runtimeDataId="POSPUSH" runTime="2000-01-01 00:00:00.000" serviceName="runEntitySync" poolId="pool" runAsUser="system" recurrenceInfoId="POSPUSH"/> --> <!-- POSPULL Scheduled Service --> <RecurrenceRule recurrenceRuleId="POSPULL" untilDateTime="" frequency="HOURLY" intervalNumber="2" countNumber="-1"/> <RecurrenceInfo recurrenceInfoId="POSPULL" startDateTime="2000-01-01 00:15:00.000" recurrenceRuleId="POSPULL" recurrenceCount="0"/> <RuntimeData runtimeDataId="POSPULL"> <runtimeInfo><![CDATA[<?xml version="1.0" encoding="UTF-8"?> <ofbiz-ser> <map-HashMap> <map-Entry> <map-Key> <std-String value="entitySyncId"/> </map-Key> <map-Value> <std-String value="<POS Terminal ID>"/> </map-Value> </map-Entry> <map-Entry> <map-Key> <std-String value="remotePullAndReportEntitySyncDataName"/> </map-Key> <map-Value> <std-String value="remotePullAndReportEntitySyncDataRmi"/> </map-Value> </map-Entry> </map-HashMap> </ofbiz-ser> ]]></runtimeInfo> </RuntimeData> <JobSandbox jobId="POSPULL" jobName="Pull Data from MCS" runtimeDataId="POSPULL" runTime="2000-01-01 00:00:00.000" serviceName="runPullEntitySync" poolId="pool" runAsUser="system" recurrenceInfoId="POSPULL"/> </entity-engine-xml>
Syncing issues
If, for any reasons, the syncing has failed and did not recover automatically, you will need to check the status and reset it. For that, use the Entity Sync Status feature in Webtools. If it's still in running state, put it back to not started status using "Reset Run Status" (it depends of translation), in order for the job to pick things again. Note that these jobs should run only on the client(s) and you should not run multi-instances of them. So you only have to care about this issue from the client(s) side.
Avoid TimeStamp issues
- Fix the timezone on all machines to be the same (sorry if this is an issue but I can't find another way)
- use an (preferably the same) NTP server to adjust the time on each machine. Remember that OOTB the minimun period of time used is 5 mins for push (1 hour for pull). So you should not get any issues as long as your machines have the "same time" between this window.
- If you get an issue with foreign keyd because the NTP server adjusted time inside a transaction, use the Check/Update Database feature in Webtools
Fixing Timestamps issues
This is related to this thread and we recommend to read it before applying the tips below.
You need to make sure you aren't doing a PUSH in the other direction that overwrites the records that did not pull correctly.
Changing the timestamps is just a tool to help you isolate your problem, not a solution. The timestamps to be changed are the ones on the records that did not pull correctly, NOT the runtime entities or job.
It has been quite a while since I did this, but here are the steps I think you should take:
- Initiate your PULL with a recurrence of about 5 minutes (do not setup any PUSH processes yet).
- Locate a record known to not PULL correctly and verify that it did not pull this time.
- Update the timestamp on the MCS for that record. Have the xml ready to load thru web tools before starting the PULL process. The key here is to just make sure the date and time is set to something later than when the job ran.
- Let the PULL run again and check the record on the POS client to see if it updated properly this time.
Finally since you went so far, you might be interested by this article There is No Now - Problems with simultaneity in distributed systems