From 5a5e2a3682f51a6572808aad861f33ff7abc5da0 Mon Sep 17 00:00:00 2001 From: Paul Prescod Date: Wed, 14 Sep 2022 14:43:07 -0700 Subject: [PATCH] Cache records for performance --- docs/salesforce.md | 7 ++++++- snowfakery/standard_plugins/Salesforce.py | 4 +++- tests/test_with_cci.py | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/salesforce.md b/docs/salesforce.md index 88d895b8..2b3c79a9 100644 --- a/docs/salesforce.md +++ b/docs/salesforce.md @@ -112,9 +112,14 @@ Perhaps you do not care which Campaign you connect to: ``` As you can see, `find_record` looks for a particular record, and returns the first -one that Salesforce finds. `random_record` looks for an random record out of the +one that Salesforce finds. The output of `find_record` is cached to reduce +unnecessary calls to Salesforce, but the precise rules for how long the +cache lasts are undocumented and may change based on performance testing. + +`random_record` looks for an random record out of the first 2000 Salesforce finds. The 2000-record scope limit is based on a Salesforce limitation and future versions of Snowfakery may incorporate a workaround. +`random_record` values are never cached. NOTE: The features we are discussing in this section are for linking to records that are in the Salesforce org _before_ the recipe iteration started. These features diff --git a/snowfakery/standard_plugins/Salesforce.py b/snowfakery/standard_plugins/Salesforce.py index d4862f4b..b7ced794 100644 --- a/snowfakery/standard_plugins/Salesforce.py +++ b/snowfakery/standard_plugins/Salesforce.py @@ -3,7 +3,7 @@ from tempfile import TemporaryDirectory from pathlib import Path -from snowfakery.plugins import ParserMacroPlugin +from snowfakery.plugins import ParserMacroPlugin, memorable from snowfakery.data_generator_runtime_object_model import ( ObjectTemplate, FieldFactory, @@ -321,6 +321,7 @@ def _generate_psa(self, context, line_info, name): return new_template class Functions: + @memorable def ProfileId(self, name): query = f"select Id from Profile where Name='{name}'" return self.context.plugin.sf_connection.query_single_record(query) @@ -451,6 +452,7 @@ def random_record(self, *args, fields="Id", where=None, **kwargs): # todo: use CompositeParallelSalesforce to cache 200 at a time return self._sf_connection.query_single_record(query) + @memorable def find_record(self, *args, fields="Id", where=None, **kwargs): """Find a particular record""" query_from = self._parse_from_from_args(args, kwargs) diff --git a/tests/test_with_cci.py b/tests/test_with_cci.py index 1f44292c..39bf394c 100644 --- a/tests/test_with_cci.py +++ b/tests/test_with_cci.py @@ -125,6 +125,21 @@ def test_soql_plugin_record(self, fake_sf_client, generated_rows): assert fake_sf_client.mock_calls assert generated_rows.row_values(0, "AccountId") == "FAKEID0" + def test_soql_plugin_cache(self, fake_sf_client, generated_rows): + yaml = """ + - plugin: snowfakery.standard_plugins.Salesforce.SalesforceQuery + - object: Contact + count: 5 + fields: + FirstName: Suzy + LastName: Salesforce + AccountId: + SalesforceQuery.find_record: Account + """ + generate(StringIO(yaml), plugin_options={"org_name": "blah"}) + assert fake_sf_client.mock_calls + assert len(fake_sf_client.mock_calls) == 1, fake_sf_client.mock_calls + def test_soql_plugin_random__orgname_long(self, fake_sf_client, generated_rows): yaml = """ - plugin: snowfakery.standard_plugins.Salesforce.SalesforceQuery