Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
This article explains how CopyStorm optimally decides which records are copied from Salesforce.
CopyStorm’s default behavior is to perform an incremental copy from Salesforce every time it runs. There are no special parameters to set — just use the defaults and CopyStorm will incrementally update your Salesforce backup.
CopyStorm minimizes the number of records processed during each run by using record timestamps stored in the CopyStorm database from previous runs.
For example, if the most recent timestamp for an Account in the CopyStorm database is 14-Jul-2013 at 12:00:00.000 then there is no need to read records from Salesforce that were modified before this timestamp.
The following diagram illustrates how CopyStorm reads records for each selected table:
The details are a bit more complex than the above diagram — for example, records are not read one at a time in the code. However, this is the exact logical process followed by CopyStorm.
This section provides a bit more detail on exactly how timestamp values are used by CopyStorm. You do not need to grasp this section to understand how CopyStorm works.
Salesforce has an interesting quirk — records are not committed to a Salesforce database in timestamp order. The implications of this are:
At 13:01 a SOQL query indicates that there are 1000 records created earlier than 13:00.
At 13:02 the same SOQL query may indicate that there are 1001 records created earlier than 13:00.
It seems that records written to Salesforce “take a little while” to be committed to the database AND are committed in no particular order.
CopyStorm overcomes this Salesforce quirk using two parameters on the Advanced tab:
The SFDC Commit Latency parameter indicates how long to wait for Salesforce to commit records. The default is 300 seconds.
This means that, by default, CopyStorm will not read a record from Salesforce until it has a timestamp at least 300 seconds old.
The Timestamp Latency parameter indicates how many seconds to subtract from the most recent timestamp found in CopyStorm.
To sum up the details, the SOQL used by CopyStorm really looks like:
SELECT * FROM TT WHERE TT.systemmodstamp>=(MostRecent – “TimestampLatency”) AND TT.systemdstamp <=(CURRENT_TIME – “SFDC Commit Latency”)
In Salesforce a query of the form “SELECT * FROM TT” will often fail with a timeout error on large tables. To avoid this problem CopyStorm runs every SOQL query with a LIMIT. For most tables the LIMIT value is set to 20,000 — but is configured to be smaller for a handful of tables known to have problems with this high of a limit.
The default LIMIT value can be overridden on the Advanced tab using the “Row LIMIT per Query” option — however, changing this parameter generally results in non-optimal behavior.
In the case where all records in a batch have exactly the same timestamp, CopyStorm dynamically increases the LIMIT value until the end of records is reached or not all records have the same timestamp. When this type of adjustment is required it is rarely noticed unless a Salesforce table has 100,000’s of records with exactly the same timestamp.
If you would like to know about further Salesforce quirks CopyStorm has to manage (and be convinced that you don’t want to write your own CopyStorm) take a look at this blog post: .
Metadata tables in a CopyStorm database are primarily copies of the equivalent Salesforce metadata calls plus mappings from Salesforce to CopyStorm names for tables and fields. These tables are the “CopyForce” tables found in the CopyStorm database.
CopyForceTable maps the Salesforce API table name to the CopyStorm created database table name. It contains the same fields as a Salesforce DescribeSObjectResult. Where possible CopyStorm uses the same name, but there are several reasons why CopyStorm may use a different table name:
Names that are the same as reserved words in the target database will be modified.
For example, User is renamed to User2 in SQL/Server.
In general, CopyStorm uses the minimal number of API calls possible to do a backup.
Detailed information can be found in this table:
Yes you can — CopyStorm supports log4j2. This article contains the instructions you will need.
CopyStorm comes packaged with log4j2 — configuring configuring CopyStorm to use it is a two-step process:
Select the “Use Apache Logging” option on the configuration section of CopyStorm’s Configuration tab.
When checked, CopyStorm will use the Apache Commons Logging system rather than the default.
The short answer is “Yes, CopyStorm backs up all Salesforce metadata.”
In Salesforce, metadata is stored in two different ways:
In Tables (SObject) — much of the key Salesforce metadata is stored in tables.
Examples include Apex Classes, Apex Pages, Apex Triggers, and Workflows/Processes
Not In Tables — this type of data is only accessible as zip archives via the Salesforce Metadata API.
CopyStorm backs up all metadata stored in Tables and, just as importantly, maintains revision history as the records change.
For example:
If an ApexClass changes, the old version of the class is saved in a field history tracking table.
If an ApexPage is deleted, the definition of the ApexPage just before it was deleted is saved in a field history tracking table.
Since metadata tables used by CopyStorm are defined by Salesforce’s ERD, the data in them is readily accessible within the CopyStorm database. For example, the definition of the ApexClass “MyFavoriteClass” can be found with the following SQL:
SELECT body FROM ApexClass WHERE name=”MyFavoriteClass”
Starting with CopyStorm 7.37.2, metadata accessible only via the Salesforce metadata API is backed up to the CopyStorm table SFMetaFileProperties and can be restored using CopyStorm/Restore.

Table and column name size limitations in Oracle dictate shortening longer names.
For more information, see https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_calls_describesobjects_describesobjectresult.htm
CopyForceTableField maps table field names to CopyStorm created field names. It contains the same fields as a Salesforce field.
For more information, see: http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_describesobjects_describesobjectresult.htm#i1427375
CopyForceTableChildRelationship maps parent table names to child table names. It contains the same fields as a Salesforce ChildRelationship.
For more information, see: http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_describesobjects_describesobjectresult.htm#i1427334
Setup
3
In this step CopyStorm reads Salesforce table definitions for all tables being backed up. By default it takes a single API call per 100 tables.
Backup
1 * Number of Tables
CopyStorm uses at least 1 API call for each Salesforce table in the backup set. If there are no records to be backed up or if there are fewer than 2000 records to be backed up then this is the only API call used.
The number of records that can be read per API call varies by table in Salesforce. For most tables, between 1000 and 2000 can be returned for each API call. However, there are a few tables that return a single record per API call. This includes:
Attachment
ContentVersion
EventLogFile
For example:
A frequent incremental CopyStorm backup of 100 tables that includes Attachments would usually take a number of API calls equal to:
3 + 3 + 99 + The Number of Changed Attachments
Bootstrap
3
There are several operations that take place each time CopyStorm connects to Salesforce:
Find the name of all visible tables in Salesforce (1 API Call)
Cache the definition of the User and Profile tables (1 API Call)
Read the User record for the current User (1 API Call)
Since CopyStorm keeps a copy of Salesforce metadata in local database tables it is a fairly trivial task to compare metadata from multiple CopyStorm backups.
The key CopyStorm tables required to perform table and field comparisons are CopyForceTable and CopyForceTableField. These are the tables CopyStorm uses to keep the CopyStorm database structure in sync with Salesforce. Rather than bore you with details, download the following SQL script for an example of how to exhaustively search for table and field differences.
This solution compares elements like Apex Classes, Page Layouts, Settings, etc. from two difference CopyStorm backups. The key table in CopyStorm is SFMetaFileProperties since this table contains the most recently backed version of each metadata type. Note that the companion table, SFMetaFilePropertiesHistory, contains historical values for each metadata type. Download the following script for an example of how to exhaustively compare metadata.
When a new version of CopyStorm is released, adjustments to the internal CopyStorm data model may be required. By default, CopyStorm automatically handles database version upgrades. To disable automatic schema upgrades, refer to the documentation.
CopyStorm’s default behavior is to display a Schema Upgrade Notice dialog to the user:
From this dialog, you can:
View all pending schema upgrades.
View specific details about a schema upgrade, including the underling SQL to be executed.
Approve or Disapprove of the changes.
Approving the changes will cause CopyStorm to execute all upgrade SQL.
Disapproving the changes will cause CopyStorm to terminate.
Note: By default, CopyStorm automatically applies the upgrades.
View the progress of the approved changes.
CopyStorm offers users the ability to view details about an individual internal schema upgrade.
Users have the ability to see:
Schema Upgrade Description
High level information about the purpose of the upgrade.
SQLassociated with the Schema Upgrade
This provides users with the ability to copy & run the SQL manually.
Note: For some upgrades, SQL can also be generated dynamically by CopyStorm. In these cases, it is not appropriate to run upgrade SQL manually. Please contact before proceeding to run any upgrade SQL manually.
*Note:*The Schema Upgrade shown in the above screenshots are for an update to introduce support for off-database partitions. To learn more about how off-database partitions work, refer to the .
When Schema Upgrades are not approved, the following dialog will be shown:
From this point, users have the options to:
Exit
Terminate the CopyStorm application
Cancel
Return to the Schema Upgrade Notice Dialog
In Salesforce all Date/Time data is stored in UTC — Coordinated Universal Time. UTC is not a time zone but rather a time standard used commonly around the world.
For practical purposes, we can assume that UTC is the same as the older GMT (Greenwich Mean Time) standard and is the current time in London, UK.
CopyStorm also stores all Date/Time values in UTC and it is up to an application to convert UTC values to the timezone of each application using the data. This makes a lot of sense for systems with users accessing the same data from wildly different timezones. Each user expects to see Date/Time values displayed in their local time zone!
This section lists a few techniques for converting from UTC to the local timezone of the computer hosting the CopyStorm database.
Example query:
SELECT systemmodstamp ,DATEADD(hh,((DATEDIFF(ss,systemmodstamp,GETDATE())-DATEDIFF(ss,systemmodstamp,GETUTCDATE()))/3600), systemmodstamp) AS LocalTimestamp FROM Account
There are two things to note with this solution:
SQL/Server 2016 has a direct solution to this problem ().
This example does not take daylight savings time into consideration.
See for a solution that takes daylight savings time into consideration.
Example query:
SELECT systemmodstamp, systemmodstamp + (systimestamp – SYS_EXTRACT_UTC(systimestamp)) LocalTime FROM Account
Example query:
SELECT systemmodstamp, systemmodstamp + INTERVAL (TIMESTAMPDIFF(SECOND, UTC_TIMESTAMP() , NOW())) SECOND FROM Account
Example query:
SELECT systemmodstamp, systemmodstamp AT TIME ZONE ‘UTC’ FROM Account
This article explains how Capstorm license management works and provides tips on diagnosing license management problems.
When a Capstorm product connects to Salesforce it also checks for a valid product license in the file “license.txt”. The “license.txt” file will be located in the same directory as the script used to launch the Capstorm product. If a license.txt file is not found or a valid non-expired license is not found in the file, the Capstorm product will issue a request to the Capstorm license service to retrieve (or generate) a license.
When issuing the license request to the Capstorm license server, the file will be used to identify the installation. If the CapstormCustomerKey.txt file is not valid, a message indicating the issue will be shown.
If a Capstorm product fails to retrieve/generate a license it is usually because the connection is blocked by a firewall rule. Diagnosing and confirming this condition can be done using a web browser. Here’s how.
To retrieve or generate a license, a Capstorm product makes a https request in the following form:
Having multiple CopyStorm jobs run on the same database at the same time is perfectly OK if the two jobs are processing different Salesforce tables. However, what if two CopyStorm jobs are running at the same time on the same set of tables? How can this happen?
CopyStorm is scheduled to run every 30 minutes.
A CopyStorm job starts at 12:00.
At 12:30 the first CopyStorm job is still running because there were a LOT of updates in Salesforce.
CopyStorm copies both active and deleted records from Salesforce to the target database by inspecting the Salesforce recycle bin. When a deleted record is discovered its “isDeleted” column is set to true in the target database.
The retention policy for deleted records in the target database is set using the Advanced tab parameter “Delete Older Than”. Available values can range from:
Remove deleted records immediately.
Remove deleted records after they have been deleted in Salesforce for 7 days.




The scheduler starts a second CopyStorm job at 12:30.
In practice this is unlikely to cause an issue because the second job will not catch up with the first job (e.g. the two jobs will never be operating on the same table at the same time by happenstance). However, this is a condition that is easily AND SHOULD BE avoided.
To prevent the same job running twice (or more times) is the flock command. The following script ensures that the CopyStorm job never runs twice at the same time:
If the script is run twice, the second script will wait (at the flock) command until the first script completes. Once the first invocation of the script completes, the flock command will return and the second invocation will continue.
The easiest way to prevent a job from running twice on Windows is to use the Windows scheduler. By default, the Windows Scheduler will not start a scheduled task again if a previous invocation is still running.
(
flock -x 9
sh /home/greg/bin/CopyStorm/CopyStorm.sh -run /home/greg/copyStormConfig/MyJob.copyStorm
) 9>/tmp/MyJob.lockWhere:
salesforceOrgId is the full 18 character unique identifier of your Salesforce organization.
productName is the name of the product for which a license is desired.
productVersion is the version number of the product.
For example:
If the request is successful the license server will return a JSON string in the following form:
Information from the JSON string will be added to the product’s license.txt file.
If a firewall is blocking access to the Capstorm license server then pasting a license request into a browser will almost always provide a meaningful message. If you receive a “Connection Refused” response then a firewall is likely blocking access to license.capstorm.com. You need to tell your network administrator to open ports 80 and 443 to license.capstorm.com (75.2.67.235).
Here are a few URLs you can click on to test the connection to the license manager:
CopyStorm/MySQL
CopyStorm/SQL Server
CopyStorm/Oracle
{“license”:{“id”:1,”ipAddress”:”38.114.70.254″,”licenseKey”:”2520b633-13fd-4488-800e-449526035835″,”product”:”CopyStorm/MySQL”,”version”:”Test”,”sfdcOrganization”:”CAPSTORMTEST000000″,”grantDate”:”2013-08-10″,”createdDate”:”2013-08-10″,”modifiedDate”:”2014-04-02″,”lock”:”q/nI2XtrIaof6u0CdEVSe28EDxjiUUW21uB8h9mpQfE\u003d”,”note”:””,”ipOrganization”:”Capstorm”,”ipDomain”:”wisperisp.com”,”ipCity”:”Edwardsville”,”ipCountry”:”United States”,”ipRegion”:”Illinois”},”successful”:true}0
Any deleted records copied from Salesforce are immediately deleted from the target database.
7
Deleted records are removed from the target database after they have been deleted in Salesforce for 7 days.
There are also situations where a record is deleted in Salesforce but is not discovered by CopyStorm. This situation may occur when:
A user deletes records in Salesforce and empties the recycle bin BEFORE CopyStorm has run.
A system administrator empties the global recycle bin.
A Salesforce API program does a hard delete on Salesforce records.
A hard delete bypasses the Salesforce recycle bin mechanism.
Salesforce performs a hard delete.
There are a few tables that Salesforce hard deletes by default.
When a Salesforce record is hard-deleted it is usually wise to use the CopyStorm/Medic Fix IsDeleted tool to mark the appropriate records as deleted in the target database. For information on when and how the CopyStorm/Medic tool should be used see this best practice article.
All programming languages, databases, and most applications have limits on the maximum size of values they can store or manipulate. For example, Salesforce has a 2 GB limit the size of an uploaded file, and PostgreSQL has a 1 GB limit on the size of a column value.
When CopyStorm downloads large files from Salesforce, CopyStorm streams the Base64-encoded file body to the file system (which has no size limits other than available disk space). Without using CopyStorm’s Base64 Chunking feature the Base64-encoded file body will be written directly to the database, subject to the size limitations (e.g. 1 GB in PostgreSQL).
The CopyStorm Base64 Chunking feature allows large files to be broken apart into chunks, storing these chunks in a separate table named CopyForceTableFieldChunk. By default, files will be broken into 100 MB chunks – but this is configurable. For example, if the Body field of a ContentVersion record is 500 MB, and the Base64 Chunking feature is enabled, then the 500 MB file will be broken into 5 parts that are stored as 5 separate entries in the CopyForceTableFieldChunk table.
With Base64 Chunking enabled, there are no practical database storage limits to the size of field values in SalesForce objects that can be brought into the CopyStorm database, and CopyStorm is not subject to the maximum size limitations of the database.
The Base64 Chunking feature is disabled by default when CopyStorm is initially configured – to enable this feature, check the Chunk Base64 Values checkbox on the configuration editor.
When this checkbox is:
Unchecked, then Base64 field values are always stored in-line in the primary CopyStorm data tables.
Checked, then Base64 field values that are larger than the chunk size will be stored in the CopyForceTableFieldChunk table.
In this case, a pointer value will be stored in the primary CopyStorm data table.
The pointer is the string representation of a JSON object containing field metadata. For example:
All chunked values will be prefixed with the value “#AAA:”. Inlined Base64 values will never begin with this value because the “#” character is not valid in Base64 strings.
All of the Base64 chunking information within this page also applies to CopyStorm Snapshots, with two differences:
CopyStorm Snapshots store Base64 values in a secondary chunked data table named CopyForceArchiveFieldChunk.
CopyStorm Snapshots will always chunk Base64 values, regardless of whether Base64 chunking is enabled in the primary CopyStorm database.
To override the system default chunk size, create a Base64ChunkParameters.xml file and place it in the [CopyStorm]/config/ directory. This file is used to provide the chunk size to CopyStorm. In the example below, the default chunk size has been changed to 5 MB (5000000):
All chunks created after updating this file will be created using the newly provided chunk size.
CopyStorm will automatically delete records from the CopyForceTableFieldChunk table as parent records are deleted from the primary CopyStorm data tables. If the CopyStorm tables are truncated, or if records are deleted from the CopyStorm tables without CopyStorm performing the deletions, then the CopyStorm/Medic Delete Orphaned Base64 Chunks tool can be used to scan for and delete any CopyForceTableFieldChunk records that point to records that are no longer present in the primary CopyStorm data tables.
Snapshot chunked records contained in the CopyForceArchiveFieldChunk table will be automatically cascade-deleted when their parent records are deleted.
The Salesforce API presents all SObjects (Account,Contact, etc.) as tables to the outside world even though quite a number of SObjects are really subsets/views of other SObjects. The most common example is Salesforce feed tables — there are dozens of feed tables (AccountFeed, ContactFeed, etc.) that are simply views of the table that contains all feeds: FeedItem.
The AccountFeed table is really a view on the base table FeedItem.
SELECT * FROM FeedItem WHERE parentId IS an Account Record.
There are also cases where the Salesforce API does not support a direct query on an SObject but the SObject can be constructed from other SObjects.
The ActivityHistory table is a union of the Event and Task SObjects but cannot be queried directly using SOQL.
There are other issues of this kind, but lets get to the important part. When Database Views are enabled in CopyStorm, then CopyStorm will use views in the target database rather than copying a table. This can lead to much faster backups for each table that can be represented as a view.
Database Views are enabled by default in CopyStorm and when enabled:
If a Salesforce table does not exist in the target database and can be represented as a view, then CopyStorm will create it as a view.
If a Salesforce table already exists in the target database, then CopyStorm will not re-create it as a view but will continue to treat it as a table.
If Database Views are disabled in CopyStorm:
Database Views will not be created for new tables.
If a Database View already exists in the target database, then CopyStorm will not change it.
The toggle that enables/disables Database Views is found in the configuration section of the CopyStorm Configuration tab.
This section is for anyone wanting to know exactly how the logic for determining whether a Database View should be used works.
If your CopyStorm database was created before Database Views were introduced then are likely many database tables in your backup that could be represented as database views instead. For safety, CopyStorm will not convert an existing table to a view UNLESS you follow the instructions in this article.
The technique to force CopyStorm to switch to a database view for a table is simple:
Manually drop the table in the CopyStorm database that should be a view. The next time CopyStorm runs it will create the view.
For example:
DROP TABLE AccountFeed
A blank value means that deleted records are never removed from the target database.
CopyStorm/PostgreSQL
CopyStorm/H2
CopyStorm/Restore



DROP TABLE ActivityHistory
will cause CopyStorm to recreate AccountFeed and ActivityHistory to views the next time CopyStorm runs.
The CopyStorm/Medic Table Editor tool provides a simple GUI for dropping tables from a CopyStorm database.
Go to the documentation for the CopyStorm/Medic Table Editor
All tables ending with Feed. Examples:
AccountFeed
MyCustomObject__Feed
ActivityHistory
AccountUserTerritory2View
#AAA:{
"numChunks": "5",
"chunkSize": "100000000",
"contentSize": "500000000",
"salesforceId": "0686300000190d9AAA",
"hash": "bcfc403cba99fda2224395a843d9397b"
}<Base64ChunkParameters>
<DataSourceType name="SQLServer" chunkSize="5000000" />
<DataSourceType name="Oracle" chunkSize="5000000" />
<DataSourceType name="MySQL" chunkSize="5000000" />
<DataSourceType name="H2" chunkSize="5000000" />
<DataSourceType name="PostgreSQL" chunkSize="5000000" />
<DataSourceType name="RedShift" chunkSize="5000000" />
<DataSourceType name="Snowflake" chunkSize="5000000" />
<DataSourceType name="Unknown" chunkSize="5000000" />
</Base64ChunkParameters>When metadata backup is enabled for CopyStorm a table named SFMetaFileProperties will be created in the target database to hold metadata backups. In addition a table named SFMetaFilePropertiesHistory will be created to hold past revisions of metadata.
The columns in SFMetaFileProperties mimic the data structure returned from the Salesforce Metadata API:
The companion table “SFMetaFilePropertiesHistory” has the same structure as SFMetaFileProperties but with two additional columns:
It is doubtful that you will want to access these tables directly. However, CopyStorm’s companion product, CopyStorm/Restore, uses these tables to provide a metadata report experience that can be performed with surgical accuracy.
id
The ID of this metadata record — unlike other IDs in Salesforce it is not guaranteed to be non-null OR unique. In short, internal Salesforce objects violate the normal non-null uniqueness rule.
metatype
The type of metadata in this record (e.g. CustomObject, ApexTrigger, etc…). Note that if a meta type is contained within another meta type (e.g. CustomField lives within CustomObject) then it will not have an entry in this table. The data will be contained in the parent record for the object.
fileName
The name of the file the metadata API requires for the record.
fullName
The full API name of the record.
manageableState
This field will contain “unmanaged” for types of metadata that can be redeployed.
namespacePrefix
For installed packages this contains the name of the package’s namespace.
metaxml
For records that require a companion “-meta.xml” this field contains the content.
content
The raw base64 encoded data for this meta record.
body
For records in UTF-8, contains the decoded value of the content column.
createdById
ID of the user in Salesforce who created this metadata record. NOTE: this value may be null.
createdByName
Name of the user in Salesforce who created this metadata record. NOTE: this value may be null.
createdDate
Timestamp of when the record was created.
lastModifiedById
ID of the user in Salesforce who last modified this metadata record. NOTE: this value may be null.
lastModifiedByName
Name of the user in Salesforce who last modified this metadata record. NOTE: this value may be null.
lastModifiedDate
Timestamp of when the record was last modified.
historyDate
Timestamp of when this history record was written. Older versions have older historyDate values.
version
The history date represented as the number of milliseconds since 1-Jan-1970.
There are a number of Salesforce quirks that make creating incremental backup software more difficult than a person might expect. The content of this article will help you understand some of the quirks and give you information that makes you happy that Capstorm backs up Salesforce for you. Note, the text may look familiar to an article published originally on blogspot in 2014 — that’s OK since the blogspot author is one of the founders of Capstorm.
This article is not about how to be a better SOQL programmer. It is about pitfalls in SOQL that are obscure and often difficult to diagnose and understand when encountered.
The SOQL WHERE clause cannot be trusted.
The SOQL LIMIT parameter is not always optional.
Timestamps returned by SOQL believe in time travel.
SOQL can return field data that is invalid based on the field definition.
SOQL has a love/hate relationship with tables containing lots of custom fields.
SOQL does not treat all table equally – not knowing the exceptions can take down your org.
SOQL chokes on tables with lots of functional columns.
Here is a trick question. Will the following SOQL statement return any records that were modified today?
SELECT id, name FROM Account WHERE systemModStamp < LAST_YEAR
The answer is……usually not. However it will return a record modified today when:
The record fit the WHERE criteria when the record set to return was determined.
The record is modified before it was fetched from Salesforce.
This “sin” is easy to reproduce with a Python script and a web browser.
In the web browser open the Salesforce GUI on the record that will be the most recent one to match the LAST_YEAR criteria.
Start a Python script that fetches records using:
SELECT id,name,systemModStamp FROM Account WHERE systemModStamp < LAST_YEAR ORDER BY systemModStamp ASC
Modify the record in the Salesforce GUI after the Python script starts fetching records.
One of the last records you read from Salesforce will have a modification date of today
Though the SOQL documentation says that the LIMIT parameter is optional there are a handful of Salesforce tables where it is nearly impossible to run a large query without the dreaded Query Timeout error. If you are not a believer then try running these queries on a mature Salesforce instance.
SELECT id, accountId, description FROM Task ORDER BY SystemModStamp ASC
SELECT application,browser,loginTime FROM LoginHistory ORDER BY loginTime ASC
Short of begging Salesforce for a max timeout value longer than the default, a solution has to “know” good limit values for problem tables. Here are good values for a handful of the more troublesome table.
Here’s another SOQL quiz. In the following code snippet, is it possible for the Exception to be thrown? For the quiz assume that all Account records have unique timestamp values and no records are deleted.
Account accts1[] = [SELECT id, systemmodstamp FROM Account ORDER BY systemmodstamp ASC;
Account maxAcct = accts1[acct1.size() – 1]
Account accts2[] = [SELECT id, systemmodstamp FROM Account WHERE systemmodstamp <=:maxAcct.systemmodstamp ORDER BY systemmodstamp ASC;
if( accts2.size() > accts2.size()) {
throw new Exception(‘Timewarp – new account created in the past!’);
}
The answer….usually no. However, Salesforce does not guarantee that records are committed in timestamp order.
It is possible for a record with a SystemModStamp of 2014-12-31T00:12:12.000Z to be committed to Salesforce after a record in the same table with a SystemModStamp of 2014-12-31T00:13:12.000Z.
This is a hard “sin” to reproduce because it depends on the current load of the Salesforce servers. We discovered the problem by monitoring a high volume production instance for three weeks – the problem occurred just a couple of times during this period. It was sort of like searching for a Higgs particle.
The moral: Do not assume that the MAX(SystemModStamp) or MAX(LastModifiedDate) value is a good constraint to find all values that are older.
The solution: Do not rely on timestamps that are fresh. Give Salesforce a little while to commit all recent transactions. In practice this odd state seems to settle out on Salesforce servers after 10-120 seconds.
This quiz should be easy for experienced SOQL users.
Is it possible for an invalid email address to be stored and returned in a Salesforce Email field?
Is is possible to store a 132 character string in a 128 character Salesforce field?
The answer to #1 is, of course, possible. Check out contact Ms. Barbara Levy in any developer force instance. Her email is: b.levy@expressl&t.net Try saving the record and Salesforce will tell you that the email format is invalid (using the API will throw the same error).
The answer to #2 is also “possible.” In fact Salesforce creates an email template with an invalid length subject when Sites is enabled.
There is other “invalid” data (related to currency values) lurking in the developer force instance – you will have to find this one yourself.
Thing to remember:
Just because Salesforce meta data says a field is 80 characters long does not mean it may have a longer value.
Just because Salesforce meta data says a number is DECIMAL(5,2) do not trust it. An API program may have inserted data that does not match the spec.
Do not be surprised if you read data from one Salesforce instance and it fails to upsert into another instance – the source data may be bad.
Though Salesforce allows many custom fields to be created on a table, using too many columns in a SOQL statement will cause a QUERY_TOO_COMPLICATED error. At least this “sin” is one Salesforce documents but after 10 years one would assume they would have a work-around in the back-end.
If you really need to do the equivalent of “SELECT * FROM Account” and SOQL will not allow the read in a single statement what needs to be done? There are two problem to solve.
Figure out the minimum number of SOQL queries to run. If none of the columns are formulas then this can usually be done by using 500 columns for each query. If formula columns are involved the problem is much harder – in this case see Sin #7.[
Join the results of multiple queries together.
Realistically, grab the data from a Salesforce dump file or use a tool like Capstorm’s CopyStorm to extract the table into a relational database. Doing it yourself is hard.
The transaction cost of querying a table with SOQL varies a lot. The first time you query a high cost table and cause your production org to run out of transactions will probably be the last time.
Here a SOQL quiz. How many Salesforce transactions does it take to run the following SOQL statement?
SELECT id, name FROM TableName LIMIT 2000
The answer, of course, depends on TableName and is either 2000, 200, 40, 20, 10, 4, 2, or 1.
The “1 transaction per record” rule for Attachment records is documented. The same rule applies to QuoteDocument and ContentVersion but we have learned this from experience rather than documentation. In general 2000 and 1000 are the common number of records per transaction but the only way to know for certain is the write a bit of code and see how many come back in a bunch.
When Salesforce runs a SOQL query it turns it into SQL first. Once in SQL there is a 64K character query limit. If the 64K SQL query limit is passed then SOQL will return a QUERY_TOO_COMPLICATED exception.
Here a SOQL quiz: Given a SOQL statement how can you accurately (or inaccurately even) guess that it will generated a SQL statement less than 64 characters?
The answer is……if table has a lot of formula columns you probably cannot guess accurately.
In practice, the addition of formula columns in a SOQL statement can cause the size of the resultant SQL to explode – sometimes. It all depends on the complexity and number of tables referenced by the formula.
The complexity of the formula seems to matter. My guess is that Saleforce is in-lining the formula calculation into the generated SQL.
A formula that references other tables likely causes the table expression and predicate portion of the generated SQL to grow rapidly.
The solution to this problem is nearly the same as Sin #5 but the first step, determining the minimum number of SOQL query to run can difficult. The only solution we have found that works reliably is iterative.
Sort column in ascending order by the length of their name or length of their corresponding formula.
Set maxColumn = total # of columns
Run a query run using columns 1 thru maxColumn with a LIMIT 1.
If the query succeeds then save the query, remove the query’s columns from the list of available columns.
If the query fails then set maxColumn = maxColumn – 1 and start step 3 again.
Set maxColumn = # remaining columns
If maxColumn > 0 goto Step #3, otherwise all queries have been determined.
The “SOQL Sins” described in this article were accumulated over a number years while developing:
– keep a hot backup of your Salesforce instance in SQL/Server, Oracle, MySQL, PostgreSQL, and H2
– restore a backup to any production, development, or sandbox instance. This is a restore tool even your manager could use.
– a standard Python module for Salesforce. Works with Python 2.8. 3.3, and 3.4. Use SQLForce when Apex is just too hard.
For more information see
The following table describes how Salesforce column data types are mapped to database data types:
Table Name (or name pattern)
Good LIMIT Value
.*History
5000
.*History__c
5000
.*Feed
The compound type ADDRESS is ignored but its data is picked up in component fields.
The compound type LOCATION is ignored but its data is picked up in its component Latitude and Longitude fields.
If the length of any string type column exceeds the “Maximum Varchar” parameter then its data type will be the same as used for BASE64 types.
For Oracle Only:
CopyStorm can be forced to use a VARCHAR2 rather than NVARCHAR2 by unchecking the “Use NVARCHAR2” option in the Advanced Database Options on the Advanced tab. When CopyStorm creates or modifies a column it chooses a VARCHAR2 or NVARCHAR2 Oracle data type based on the current setting of the “Use NVARCHAR2” option. Note: CopyStorm will not change the data type of an existing column unless the column’s length increases in Salesforce.
5000
.*Share
5000
FeedComment
5000
Task
5000
EmailMessage
5000
CaseComment
50
FieldPermissions
2000
LoginHistory
2000
ANYTYPE
CLOB
TEXT
NCLOB
TEXT
NVARCHAR(MAX)
BASE64
CLOB
TEXT, MEDIUMTEXT, or LONGTEXT
CLOB
TEXT
VARCHAR(MAX)
BOOLEAN
BOOLEAN
BOOLEAN
DECIMAL(1)
BOOLEAN
BIT
COMBOBOX
NVARCHAR
VARCHAR
NVARCHAR2
VARCHAR
NVARCHAR
CURRENCY
DECIMAL
DECIMAL
NUMBER
NUMERIC
DECIMAL
DATACATEGORYGROUPREFERENCE
CHAR
CHAR
CHAR
CHAR
CHAR
DATE
TIMESTAMP
DATE
DATE
DATE
DATE
DATETIME
TIMESTAMP
DATETIME
TIMESTAMP(3)
TIMESTAMP
DATETIME
DOUBLE
DECIMAL
DECIMAL
NUMBER
NUMERIC
DECIMAL
NVARCHAR
VARCHAR
NVARCHAR2
VARCHAR
NVARCHAR
ENCRYPTEDSTRING
NVARCHAR
VARCHAR
NVARCHAR2
VARCHAR
NVARCHAR
ID
CHAR
CHAR
CHAR
CHAR
CHAR
INT
INTEGER
INTEGER, DECIMAL, or BIGINT
NUMBER
INTEGER
INT or BIGINT
MULTIPICKLIST
NVARCHAR
VARCHAR
NVARCHAR2
VARCHAR
NVARCHAR
PERCENT
DECIMAL
DECIMAL
NUMBER
NUMERIC
DECIMAL
PHONE
NVARCHAR
VARCHAR
NVARCHAR2
VARCHAR
NVARCHAR
PICKLIST
NVARCHAR
VARCHAR
NVARCHAR2
VARCHAR
NVARCHAR
REFERENCE
CHAR
CHAR
CHAR
CHAR
CHAR
STRING
NVARCHAR
VARCHAR
NVARCHAR2
VARCHAR
NVARCHAR
TEXTAREA
NVARCHAR
VARCHAR
NVARCHAR2
VARCHAR
NVARCHAR
TIME
TIME
DATETIME
TIMESTAMP(3)
TIME
DATETIME
URL
NVARCHAR
VARCHAR
NVARCHAR2
VARCHAR
NVARCHAR
Each time CopyStorm runs it records a variety of data into tables in the CopyStorm database:
A summary of records written to the database and any exceptions.
The content and structure of the tables will be familiar because they mimic what is displayed as progress within the CopyStorm GUI.
Changes to field values (see CopyForceFieldHistory in this article).
DDL statements (e.g. ALTER TABLE) that were issued against the CopyStorm database.
CopyStorm database logging tables were introduced starting with the 7.*.* version of CopyStorm.
The following tables are used for logging:
These logging tables are created automatically by CopyStorm. The only changes that should ever be performed by a customer is the deletion of log records — this should be done using .
The simplest way to view and manage CopyStorm log data is often the tool in CopyStorm/Medic.
The remainder of this article describes the column structure for each table.
The CopyForceJob table contains one record for each time CopyStorm successfully connects to Salesforce, connects to the target database, and starts a copy.
The table contains these columns:
The CopyForceJobTable table contains one record for each table that was modified during a CopyStorm run.
The table contains these columns:
The CopyForceJobException table contains one record for each exception that occurred during a CopyStorm run.
The table contains these columns:
The CopyForceDDLLog table contains a record of each DDL statement ran against the CopyStorm database.
The table contains these columns:
The CopyForceFieldHistory table records changes to field value. This table will be empty unless field history tracking is explicitly enabled.
History records will be maintained even if the referenced table / field is modified or deleted in Salesforce.
The table contains these columns:
The CopyForceMedicJob table contains one record for each time a CopyStorm/Medic job runs.
The table contains these columns:
The CopyForceMedicJobException table contains one record for each exception that occurred during a CopyStorm/Medic run.
The table contains these columns:
The CopyStorm “Copying…” tab has real-time metrics which help identify a components that are limiting the speed of a database backup. The metrics, located in the lower right hand corner of a “Copying…” tab are described in this article.
There are two primary factors which limit the speed of a CopyStorm job:
The rate at which Salesforce delivers data to CopyStorm.
The rate at which the target database accepts data from CopyStorm.
Metrics highlighted in the following image provide data indicating which component is the bottleneck at your site:
Metrics are divided into three sections:
In a balanced system:
The “S” threads will be blinking blue almost all of the time.
This means that a steady stream of data is being delivered from Salesforce.
The “D” threads will be grey most of the time but blinking blue rapidly.
The data throughput values will be less than 50ms and the database write speed will be less than 20ms.
In a system where the database is the bottleneck:
The “S” threads will often turn grey.
The “D” threads will be blue most of the time.
The database write speed will be large.
This value is elapsed time for a single request to the database to write a block of records.
In a system where Salesforce is the bottleneck:
The “S” threads will be solid blue most of the time.
This means that they are waiting for a response from Salesforce.
The “D” threads will be grey most of the time.
The first data throughput number will be large.
The most common issue we see is that CopyStorm and the database are not in the same data center. The underlying issue is that the protocols used to talk to relational databases require close proximity between the application and the database for great performance. Until we discover a way to increase the speed of light, CopyStorm running in India and talking to a database in Canada will give slow performance. You can solve this problem by running CopyStorm and its corresponding database in the same data center.
CopyStorm will write data to a database as quickly as Salesforce delivers it. If the “S” threads are grey, the “D” threads are blue, the data throughput numbers are large, and you are sure that CopyStorm and the database are in the same data center; then the database is likely overloaded. Using system monitoring tools on the database computer will quickly pinpoint the issue — often the database computer will be page-faulting like crazy.
If the CopyStorm “S” threads are solid blue, the “D” threads are almost always grey, and the first data throughput number is large; then Salesforce is not delivering data rapidly. Even with a modest internet bandwidth of 20M/s, CopyStorm can easily transfer 20,000 – 30,000 accounts per minute. If you are sure that your available bandwidth is high then see your system administrators. There may be network software inspecting and slowing packets from Salesforce.
Though CopyStorm’s runtime requirements are modest, it cannot control demands made by other applications. Use a local system monitor to see which applications are using a high amount of CPU time. On a balanced system CopyStorm will always be consuming CPU — reading from Salesforce and writing to the target database.
CopyForceJob
Contains a single record for each time CopyStorm runs.
CopyForceJobTable
Contains a single record for each table modified in a CopyStorm run.
CopyForceJobException
jobId
integer
Unique ID for this job. This value is the job’s primary key.
startTime
jobId
integer
The unique ID of the job that modified the table.
tableName
jobId
integer
The unique ID of the job where the exception occurred.
exceptionId
string
tableName
string
The name of the table addressed by the DDL statement.
command
string
id
string
The unique Salesforce record ID of the record that was the source of the data.
tableName
string
jobId
integer
The unique ID of the job.
startTime
jobId
integer
The unique ID of the job where the exception occurred.
exceptionId
integer
For example, if the “Destination Batch Size” is 100 and the database write speed is 4500ms then this means that the database is taking 4.5 seconds to write 100 records (e.g. terrible performance).
This number indicates how frequently a buffer is ready to be written to the database.
The second data throughput number will be small.
Read/Write Threads
S D
The “S” and “D” values indicate thread execution status:
“S”, “R”, and “B” threads read data from Salesforce.
“D” threads write data to the target database.
When a “S” thread is BLUE then it is either reading data from Salesforce or waiting for Salesforce to respond to a read request.
When a “D” thread is BLUE then it is writing data to the target database.
When either type of thread is GREY then it has nothing to do or is waiting for the other thread type to catch up.
Data Throughput
44ms/16ms

Contains a record of each exception (problem) encountered during a CopyStorm run.
CopyForceDDLLog
Contains a record of all DDL type statements issued against the database.
CopyForceFieldHistory
If field history tracking is enabled, this table will record the old and new value each time a field changes (except on record insert).
CopyForceMedicJob
Contains a single record for every time a CopyStorm/Medic job runs.
CopyForceMedicJobException
Contains a record of each exception (problem) encountered during a CopyStorm/Medic run.
timestamp
The time when the job started. Note that this time will be slightly after the start of the CopyStorm executable because it takes a few seconds for CopyStorm to initialize and connect to its data sources.
endTime
timestamp
The time when the job completed. This time is updated as each table finishes and after the entire job completes.
nCopied
integer
The total number of records copied to the target database.
nImagesCopied
integer
The total number of image records copied to the target database.
nImagesRead
integer
The total number of image records read from the target Salesforce Org.
nDeleted
integer
The total number of records deleted from the target database in accordance of the configured retention policies.
nFieldHistory
integer
The total number of field history records inserted into the target database.
nArchived
integer
The total number of record snapshots inserted into the target database via the Snapshot feature.
nTransactions
integer
The number of Salesforce transactions used during the run.
nExceptions
integer
The number of exceptions encountered during the run. If this value is non-zero then details for each exception will be recorded in the CopyStormJobException table.
configFileName
string
The base name of the CopyStorm configuration file used for the run. This value will be NULL if there was no active configuration file.
configFileDir
string
The directory containing the CopyStorm configuration file used for the run.
copyStormVersion
string
The version of CopyStorm that created the log record (e.g. 7.34.1).
copyStormDir
string
The directory in which CopyStorm was installed when the job ran.
elapsedTime
float
The total number of seconds taken by the job to run.
processId
integer
The system process id for the CopyStorm process.
string
The Salesforce name of the modified table.
startTime
timestamp
The time when CopyStorm started processing the table.
endTime
timestamp
The time when CopyStorm finished processing the table.
startTimestamp
timestamp
The timestamp of the first record copied from Salesforce when CopyStorm started processing the table.
endTimestamp
timestamp
The timestamp of the last record read from Salesforce when CopyStorm finished processing the table.
nRead
integer
The total number of records read from Salesforce.
nCopied
integer
The number of records inserted or updated in the CopyStorm database.
nSkipped
integer
The number of records read from Salesforce and skipped because they were already up to date in the CopyStorm database.
nDeleted
integer
The number of records deleted from the CopyStorm database.
nImagesCopied
integer
The total number of image records copied to the target database table.
nImagesRead
integer
The total number of image records read from the target Salesforce Org.
nFieldHistory
integer
The total number of field history records inserted into the target database for this table.
nArchived
integer
The total number of record snapshots inserted into the target database for this table via the Snapshot feature.
nPerMinute
integer
The average number of records processed per minute.
elapsedTime
float
The total number of seconds used to process the table.
startSize
integer
The size of the table in bytes when CopyStorm started processing the table.
endSize
integer
The size of the table in bytes when CopyStorm finished processing the table.
Order in which the exception occurred (starting at 0).
tableName
string
The name of the table being processed when the exception occurred.
message
string
A short message describing the exception.
traceback
string
The full traceback from the exception.
exceptionTime
timestamp
The time when the exception occurred.
The full DDL command.
traceback
string
If the DDL command failed, this field contains the full traceback from CopyStorm.
applyDate
timestamp
The date on which the DDL was executed.
The Salesforce API table name.
fieldName
string
The Salesforce API field name.
oldValue
string
The value of the field before it was updated.
newValue
string
The updated value of the field.
lastModifiedById
string
The unique Salesforce ID of the User that last modified the record.
lastModifiedDate
timestamp
The date on which the record was last modified.
createdDate
timestamp
The date on which the history record was created.
timestamp
The time when the CopyStorm/Medic job started.
endTime
timestamp
The time when the CopyStorm/Medic job finished.
elapsedTime
float
The total number of seconds that the job ran.
nTransactions
integer
The number of Salesforce transactions used by the job.
nExceptions
integer
The number of Exceptions (problems) reported by the job.
configFileName
string
The name of the configuration file used to start the job.
configFileDir
string
The path to the configuration file used to start the job.
medicVersion
string
The CopyStorm/Medic application version.
processId
integer
The system process id for the CopyStorm/Medic process.
toolName
string
The medic tool ran by the job. This corresponds to the value provided on the -tool command line switch.
status
string
The job status – one of Pending, Running, Cancelled, Finished, or Error.
output
CLOB
The job’s output. The format of this field will depend on the type of job that was ran.
Order in which the exception occurred (starting at 0).
message
string
A short message describing the exception.
traceback
string
The full traceback from the exception.
exceptionTime
timestamp
The time when the exception occurred.
These two numbers indicate:
How frequently a write request is being sent to a “D” thread. In this example, 44ms means that a write request was sent to a “D” thread 44 milliseconds ago.
How quickly the database processed the most recent write operation. In this example, 16ms means that the last write to the database took 16 milliseconds.
Memory Usage
84m/201m
These two numbers indicate:
The amount of virtual memory currently in use. In this example, 14 megabytes are in use.
The maximum amount of virtual memory used during this session. In this example, 201 megabytes is the maximum memory usage so far.
CopyStorm includes logic specific to Daylight Savings or Summer Time changeovers.
This logic accommodates both time changes and Salesforce record commit latency issues.
When a CopyStorm job finishes within a few hours of a time change, the next job will re-query for records modified before the time change.
The next job will re-scan an extra 90 minutes of record changes prior to the time change. This ensures that Salesforce commit latency issues and time change issues do not cause missing records in the database.
Jobs that run near a time change will take longer to complete.
This is because the job intentionally reads extra data to ensure that all records are copied. As a result, you will likely see a higher number of “Skipped” records – the job is re-checking data that is already up to date in the CopyStorm database.