When is a batch class instantiated when you schedule it?2019 Community Moderator ElectionScheduling batch...
The IT department bottlenecks progress. How should I handle this?
Aragorn's "guise" in the Orthanc Stone
What is Cash Advance APR?
Why electric field inside a cavity of a non-conducting sphere not zero?
Is it safe to use olive oil to clean the ear wax?
Redundant comparison & "if" before assignment
Non-trope happy ending?
Why does the Sun have different day lengths, but not the gas giants?
What was the exact wording from Ivanhoe of this advice on how to free yourself from slavery?
Problem with TransformedDistribution
Not using 's' for he/she/it
Does a 'pending' US visa application constitute a denial?
A social experiment. What is the worst that can happen?
Did arcade monitors have same pixel aspect ratio as TV sets?
Has any country ever had 2 former presidents in jail simultaneously?
If a character has darkvision, can they see through an area of nonmagical darkness filled with lightly obscuring gas?
Is it possible to put a rectangle as background in the author section?
Is it possible to have a strip of cold climate in the middle of a planet?
Freedom of speech and where it applies
I am looking for the correct translation of love for the phrase "in this sign love"
Open a doc from terminal, but not by its name
How much character growth crosses the line into breaking the character
How do I color the graph in datavisualization?
Delivering sarcasm
When is a batch class instantiated when you schedule it?
2019 Community Moderator ElectionScheduling batch Apexis it possible to call a single batch job from multiple schedule apex?Batch job works when run through Database.executeBatch(), but doesn't when scheduledHow reliable is execution of batch jobs?Modify batch class already in scheduleSchedule Job does not executeHelp in converting my apex class to batch jobBug? Same Batch Can Be Run From Cron & Schedule ApexSchedule a apex batch class to run every 5 minScheduled job at a specific time is running at another time as scheduled as well
QUESTION: when you instantiate a class in a scheduled apex job, is the class instantiated at the time of the schedule, or at the scheduled time of execution?
Here's my situation...
I have a custom object called Blast, which schedules a bulk SMS send. That object record includes a DateTime field that designates when the blast will happen; for this example, let's say the blast happens next Tuesday at 1:00 pm.
The blast has some unknown number of recipients -- very possibly 20-50k, which are stored as CampaignMembers. So, at the designated time, I have a batch apex class that retrieves all of the recipient info from the CampaignMember records and makes the api call.
When a user creates/updates the Blast record today and sets status to "queued", I'm creating a scheduled apex job, instantiating my batch apex class and providing the blast record, from which it pulls necessary data.
I want to build into the batch apex class a check that verifies the blast record still exists before executing the blast. But I'm not clear on whether that class is instantiated today (when the job is scheduled), or next Tuesday at 1:00pm when it has been scheduled to run.
schedulebatch
add a comment |
QUESTION: when you instantiate a class in a scheduled apex job, is the class instantiated at the time of the schedule, or at the scheduled time of execution?
Here's my situation...
I have a custom object called Blast, which schedules a bulk SMS send. That object record includes a DateTime field that designates when the blast will happen; for this example, let's say the blast happens next Tuesday at 1:00 pm.
The blast has some unknown number of recipients -- very possibly 20-50k, which are stored as CampaignMembers. So, at the designated time, I have a batch apex class that retrieves all of the recipient info from the CampaignMember records and makes the api call.
When a user creates/updates the Blast record today and sets status to "queued", I'm creating a scheduled apex job, instantiating my batch apex class and providing the blast record, from which it pulls necessary data.
I want to build into the batch apex class a check that verifies the blast record still exists before executing the blast. But I'm not clear on whether that class is instantiated today (when the job is scheduled), or next Tuesday at 1:00pm when it has been scheduled to run.
schedulebatch
add a comment |
QUESTION: when you instantiate a class in a scheduled apex job, is the class instantiated at the time of the schedule, or at the scheduled time of execution?
Here's my situation...
I have a custom object called Blast, which schedules a bulk SMS send. That object record includes a DateTime field that designates when the blast will happen; for this example, let's say the blast happens next Tuesday at 1:00 pm.
The blast has some unknown number of recipients -- very possibly 20-50k, which are stored as CampaignMembers. So, at the designated time, I have a batch apex class that retrieves all of the recipient info from the CampaignMember records and makes the api call.
When a user creates/updates the Blast record today and sets status to "queued", I'm creating a scheduled apex job, instantiating my batch apex class and providing the blast record, from which it pulls necessary data.
I want to build into the batch apex class a check that verifies the blast record still exists before executing the blast. But I'm not clear on whether that class is instantiated today (when the job is scheduled), or next Tuesday at 1:00pm when it has been scheduled to run.
schedulebatch
QUESTION: when you instantiate a class in a scheduled apex job, is the class instantiated at the time of the schedule, or at the scheduled time of execution?
Here's my situation...
I have a custom object called Blast, which schedules a bulk SMS send. That object record includes a DateTime field that designates when the blast will happen; for this example, let's say the blast happens next Tuesday at 1:00 pm.
The blast has some unknown number of recipients -- very possibly 20-50k, which are stored as CampaignMembers. So, at the designated time, I have a batch apex class that retrieves all of the recipient info from the CampaignMember records and makes the api call.
When a user creates/updates the Blast record today and sets status to "queued", I'm creating a scheduled apex job, instantiating my batch apex class and providing the blast record, from which it pulls necessary data.
I want to build into the batch apex class a check that verifies the blast record still exists before executing the blast. But I'm not clear on whether that class is instantiated today (when the job is scheduled), or next Tuesday at 1:00pm when it has been scheduled to run.
schedulebatch
schedulebatch
asked Mar 13 at 22:14
PatMcClellan__cPatMcClellan__c
836221
836221
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
It's instantiated when new SomeBatchJobName()
is called, then serialized when System.scheduleBatch(...)
is called. So the data stored in the object will be from the moment of instantiation (now), not when it is eventually pulled from the queue and executed. This is also true for scheduled and queueable jobs, too.
OK, thanks. That's what I thought would happen, just wanted to confirm. So that means any changes (between now and next Tuesday) to the data I use to instantiate the object will not be reflected. So... I'm thinking I need another class, that executes next Tuesday and instantiates the batch object then.
– PatMcClellan__c
Mar 13 at 22:30
@PatMcClellan__c Without seeing your code, hard to say, but remember you can also do other stuff in your start method besides just providing a query locator. During the start method might be a perfect time to do any other calculations or queries you need to perform to see if you want to continue or not. You can also use System.abortJob in the start method if you want to cancel your job early (the job Id comes from the BatchableContext object).
– sfdcfox
Mar 13 at 22:34
Ah... I was wondering about that. So, instead of providing the entire blast record when I instantiate, I just provide the recordId. Then, in the start method, I do a soql search on it, pull the data at that point in time, or abort if the record no longer exists.
– PatMcClellan__c
Mar 13 at 23:01
@PatMcClellan__c Yes, you should always query for the data you're going to work on as late as possible, especially when using asynchronous code that might not be called for a long time.
– sfdcfox
Mar 13 at 23:10
I can't find docs on how to pull the jobId while inside the start method. Would it be something like...BC.getJobId()
?
– PatMcClellan__c
Mar 13 at 23:37
|
show 2 more comments
For the benefit of clarity, I'll share my revised code... the object is actually called Wave, not Blast.
global class QueueWave implements Database.Batchable<SObject>, Database.Stateful {
global String waveId;
global String body;
global String messageServiceLabel;
global Integer recipientCount;
global String emailAddress;
global String waveName;
global String userId;
global QueueWave(String waveId) {
this.waveId = waveId;
}
The constructor does nothing except store the recordId at the time this batch class is scheduled. I'm using try-catch, catching QueryException which aborts the job.
global Database.QueryLocator start(Database.BatchableContext BC){
String query;
if(Wave__c.SObjectType.getDescribe().isAccessible()){
try{
Wave__c wave = [
SELECT [fields I need, which may have been updated since batch was scheduled]
FROM Wave__c
WHERE Id = :this.waveId
LIMIT 1];
//set global vars with data from the wave record
. . .
query = // build my query string here
}catch(QueryException qe){
System.debug('QueryException on ' + BC.getJobId() + ' ' + qe);
System.abortJob(BC.getJobId());
}
}
return Database.getQueryLocator(query);
}
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "459"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsalesforce.stackexchange.com%2fquestions%2f253757%2fwhen-is-a-batch-class-instantiated-when-you-schedule-it%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
It's instantiated when new SomeBatchJobName()
is called, then serialized when System.scheduleBatch(...)
is called. So the data stored in the object will be from the moment of instantiation (now), not when it is eventually pulled from the queue and executed. This is also true for scheduled and queueable jobs, too.
OK, thanks. That's what I thought would happen, just wanted to confirm. So that means any changes (between now and next Tuesday) to the data I use to instantiate the object will not be reflected. So... I'm thinking I need another class, that executes next Tuesday and instantiates the batch object then.
– PatMcClellan__c
Mar 13 at 22:30
@PatMcClellan__c Without seeing your code, hard to say, but remember you can also do other stuff in your start method besides just providing a query locator. During the start method might be a perfect time to do any other calculations or queries you need to perform to see if you want to continue or not. You can also use System.abortJob in the start method if you want to cancel your job early (the job Id comes from the BatchableContext object).
– sfdcfox
Mar 13 at 22:34
Ah... I was wondering about that. So, instead of providing the entire blast record when I instantiate, I just provide the recordId. Then, in the start method, I do a soql search on it, pull the data at that point in time, or abort if the record no longer exists.
– PatMcClellan__c
Mar 13 at 23:01
@PatMcClellan__c Yes, you should always query for the data you're going to work on as late as possible, especially when using asynchronous code that might not be called for a long time.
– sfdcfox
Mar 13 at 23:10
I can't find docs on how to pull the jobId while inside the start method. Would it be something like...BC.getJobId()
?
– PatMcClellan__c
Mar 13 at 23:37
|
show 2 more comments
It's instantiated when new SomeBatchJobName()
is called, then serialized when System.scheduleBatch(...)
is called. So the data stored in the object will be from the moment of instantiation (now), not when it is eventually pulled from the queue and executed. This is also true for scheduled and queueable jobs, too.
OK, thanks. That's what I thought would happen, just wanted to confirm. So that means any changes (between now and next Tuesday) to the data I use to instantiate the object will not be reflected. So... I'm thinking I need another class, that executes next Tuesday and instantiates the batch object then.
– PatMcClellan__c
Mar 13 at 22:30
@PatMcClellan__c Without seeing your code, hard to say, but remember you can also do other stuff in your start method besides just providing a query locator. During the start method might be a perfect time to do any other calculations or queries you need to perform to see if you want to continue or not. You can also use System.abortJob in the start method if you want to cancel your job early (the job Id comes from the BatchableContext object).
– sfdcfox
Mar 13 at 22:34
Ah... I was wondering about that. So, instead of providing the entire blast record when I instantiate, I just provide the recordId. Then, in the start method, I do a soql search on it, pull the data at that point in time, or abort if the record no longer exists.
– PatMcClellan__c
Mar 13 at 23:01
@PatMcClellan__c Yes, you should always query for the data you're going to work on as late as possible, especially when using asynchronous code that might not be called for a long time.
– sfdcfox
Mar 13 at 23:10
I can't find docs on how to pull the jobId while inside the start method. Would it be something like...BC.getJobId()
?
– PatMcClellan__c
Mar 13 at 23:37
|
show 2 more comments
It's instantiated when new SomeBatchJobName()
is called, then serialized when System.scheduleBatch(...)
is called. So the data stored in the object will be from the moment of instantiation (now), not when it is eventually pulled from the queue and executed. This is also true for scheduled and queueable jobs, too.
It's instantiated when new SomeBatchJobName()
is called, then serialized when System.scheduleBatch(...)
is called. So the data stored in the object will be from the moment of instantiation (now), not when it is eventually pulled from the queue and executed. This is also true for scheduled and queueable jobs, too.
answered Mar 13 at 22:24
sfdcfoxsfdcfox
261k12207452
261k12207452
OK, thanks. That's what I thought would happen, just wanted to confirm. So that means any changes (between now and next Tuesday) to the data I use to instantiate the object will not be reflected. So... I'm thinking I need another class, that executes next Tuesday and instantiates the batch object then.
– PatMcClellan__c
Mar 13 at 22:30
@PatMcClellan__c Without seeing your code, hard to say, but remember you can also do other stuff in your start method besides just providing a query locator. During the start method might be a perfect time to do any other calculations or queries you need to perform to see if you want to continue or not. You can also use System.abortJob in the start method if you want to cancel your job early (the job Id comes from the BatchableContext object).
– sfdcfox
Mar 13 at 22:34
Ah... I was wondering about that. So, instead of providing the entire blast record when I instantiate, I just provide the recordId. Then, in the start method, I do a soql search on it, pull the data at that point in time, or abort if the record no longer exists.
– PatMcClellan__c
Mar 13 at 23:01
@PatMcClellan__c Yes, you should always query for the data you're going to work on as late as possible, especially when using asynchronous code that might not be called for a long time.
– sfdcfox
Mar 13 at 23:10
I can't find docs on how to pull the jobId while inside the start method. Would it be something like...BC.getJobId()
?
– PatMcClellan__c
Mar 13 at 23:37
|
show 2 more comments
OK, thanks. That's what I thought would happen, just wanted to confirm. So that means any changes (between now and next Tuesday) to the data I use to instantiate the object will not be reflected. So... I'm thinking I need another class, that executes next Tuesday and instantiates the batch object then.
– PatMcClellan__c
Mar 13 at 22:30
@PatMcClellan__c Without seeing your code, hard to say, but remember you can also do other stuff in your start method besides just providing a query locator. During the start method might be a perfect time to do any other calculations or queries you need to perform to see if you want to continue or not. You can also use System.abortJob in the start method if you want to cancel your job early (the job Id comes from the BatchableContext object).
– sfdcfox
Mar 13 at 22:34
Ah... I was wondering about that. So, instead of providing the entire blast record when I instantiate, I just provide the recordId. Then, in the start method, I do a soql search on it, pull the data at that point in time, or abort if the record no longer exists.
– PatMcClellan__c
Mar 13 at 23:01
@PatMcClellan__c Yes, you should always query for the data you're going to work on as late as possible, especially when using asynchronous code that might not be called for a long time.
– sfdcfox
Mar 13 at 23:10
I can't find docs on how to pull the jobId while inside the start method. Would it be something like...BC.getJobId()
?
– PatMcClellan__c
Mar 13 at 23:37
OK, thanks. That's what I thought would happen, just wanted to confirm. So that means any changes (between now and next Tuesday) to the data I use to instantiate the object will not be reflected. So... I'm thinking I need another class, that executes next Tuesday and instantiates the batch object then.
– PatMcClellan__c
Mar 13 at 22:30
OK, thanks. That's what I thought would happen, just wanted to confirm. So that means any changes (between now and next Tuesday) to the data I use to instantiate the object will not be reflected. So... I'm thinking I need another class, that executes next Tuesday and instantiates the batch object then.
– PatMcClellan__c
Mar 13 at 22:30
@PatMcClellan__c Without seeing your code, hard to say, but remember you can also do other stuff in your start method besides just providing a query locator. During the start method might be a perfect time to do any other calculations or queries you need to perform to see if you want to continue or not. You can also use System.abortJob in the start method if you want to cancel your job early (the job Id comes from the BatchableContext object).
– sfdcfox
Mar 13 at 22:34
@PatMcClellan__c Without seeing your code, hard to say, but remember you can also do other stuff in your start method besides just providing a query locator. During the start method might be a perfect time to do any other calculations or queries you need to perform to see if you want to continue or not. You can also use System.abortJob in the start method if you want to cancel your job early (the job Id comes from the BatchableContext object).
– sfdcfox
Mar 13 at 22:34
Ah... I was wondering about that. So, instead of providing the entire blast record when I instantiate, I just provide the recordId. Then, in the start method, I do a soql search on it, pull the data at that point in time, or abort if the record no longer exists.
– PatMcClellan__c
Mar 13 at 23:01
Ah... I was wondering about that. So, instead of providing the entire blast record when I instantiate, I just provide the recordId. Then, in the start method, I do a soql search on it, pull the data at that point in time, or abort if the record no longer exists.
– PatMcClellan__c
Mar 13 at 23:01
@PatMcClellan__c Yes, you should always query for the data you're going to work on as late as possible, especially when using asynchronous code that might not be called for a long time.
– sfdcfox
Mar 13 at 23:10
@PatMcClellan__c Yes, you should always query for the data you're going to work on as late as possible, especially when using asynchronous code that might not be called for a long time.
– sfdcfox
Mar 13 at 23:10
I can't find docs on how to pull the jobId while inside the start method. Would it be something like...
BC.getJobId()
?– PatMcClellan__c
Mar 13 at 23:37
I can't find docs on how to pull the jobId while inside the start method. Would it be something like...
BC.getJobId()
?– PatMcClellan__c
Mar 13 at 23:37
|
show 2 more comments
For the benefit of clarity, I'll share my revised code... the object is actually called Wave, not Blast.
global class QueueWave implements Database.Batchable<SObject>, Database.Stateful {
global String waveId;
global String body;
global String messageServiceLabel;
global Integer recipientCount;
global String emailAddress;
global String waveName;
global String userId;
global QueueWave(String waveId) {
this.waveId = waveId;
}
The constructor does nothing except store the recordId at the time this batch class is scheduled. I'm using try-catch, catching QueryException which aborts the job.
global Database.QueryLocator start(Database.BatchableContext BC){
String query;
if(Wave__c.SObjectType.getDescribe().isAccessible()){
try{
Wave__c wave = [
SELECT [fields I need, which may have been updated since batch was scheduled]
FROM Wave__c
WHERE Id = :this.waveId
LIMIT 1];
//set global vars with data from the wave record
. . .
query = // build my query string here
}catch(QueryException qe){
System.debug('QueryException on ' + BC.getJobId() + ' ' + qe);
System.abortJob(BC.getJobId());
}
}
return Database.getQueryLocator(query);
}
add a comment |
For the benefit of clarity, I'll share my revised code... the object is actually called Wave, not Blast.
global class QueueWave implements Database.Batchable<SObject>, Database.Stateful {
global String waveId;
global String body;
global String messageServiceLabel;
global Integer recipientCount;
global String emailAddress;
global String waveName;
global String userId;
global QueueWave(String waveId) {
this.waveId = waveId;
}
The constructor does nothing except store the recordId at the time this batch class is scheduled. I'm using try-catch, catching QueryException which aborts the job.
global Database.QueryLocator start(Database.BatchableContext BC){
String query;
if(Wave__c.SObjectType.getDescribe().isAccessible()){
try{
Wave__c wave = [
SELECT [fields I need, which may have been updated since batch was scheduled]
FROM Wave__c
WHERE Id = :this.waveId
LIMIT 1];
//set global vars with data from the wave record
. . .
query = // build my query string here
}catch(QueryException qe){
System.debug('QueryException on ' + BC.getJobId() + ' ' + qe);
System.abortJob(BC.getJobId());
}
}
return Database.getQueryLocator(query);
}
add a comment |
For the benefit of clarity, I'll share my revised code... the object is actually called Wave, not Blast.
global class QueueWave implements Database.Batchable<SObject>, Database.Stateful {
global String waveId;
global String body;
global String messageServiceLabel;
global Integer recipientCount;
global String emailAddress;
global String waveName;
global String userId;
global QueueWave(String waveId) {
this.waveId = waveId;
}
The constructor does nothing except store the recordId at the time this batch class is scheduled. I'm using try-catch, catching QueryException which aborts the job.
global Database.QueryLocator start(Database.BatchableContext BC){
String query;
if(Wave__c.SObjectType.getDescribe().isAccessible()){
try{
Wave__c wave = [
SELECT [fields I need, which may have been updated since batch was scheduled]
FROM Wave__c
WHERE Id = :this.waveId
LIMIT 1];
//set global vars with data from the wave record
. . .
query = // build my query string here
}catch(QueryException qe){
System.debug('QueryException on ' + BC.getJobId() + ' ' + qe);
System.abortJob(BC.getJobId());
}
}
return Database.getQueryLocator(query);
}
For the benefit of clarity, I'll share my revised code... the object is actually called Wave, not Blast.
global class QueueWave implements Database.Batchable<SObject>, Database.Stateful {
global String waveId;
global String body;
global String messageServiceLabel;
global Integer recipientCount;
global String emailAddress;
global String waveName;
global String userId;
global QueueWave(String waveId) {
this.waveId = waveId;
}
The constructor does nothing except store the recordId at the time this batch class is scheduled. I'm using try-catch, catching QueryException which aborts the job.
global Database.QueryLocator start(Database.BatchableContext BC){
String query;
if(Wave__c.SObjectType.getDescribe().isAccessible()){
try{
Wave__c wave = [
SELECT [fields I need, which may have been updated since batch was scheduled]
FROM Wave__c
WHERE Id = :this.waveId
LIMIT 1];
//set global vars with data from the wave record
. . .
query = // build my query string here
}catch(QueryException qe){
System.debug('QueryException on ' + BC.getJobId() + ' ' + qe);
System.abortJob(BC.getJobId());
}
}
return Database.getQueryLocator(query);
}
edited Mar 14 at 15:27
answered Mar 14 at 0:13
PatMcClellan__cPatMcClellan__c
836221
836221
add a comment |
add a comment |
Thanks for contributing an answer to Salesforce Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsalesforce.stackexchange.com%2fquestions%2f253757%2fwhen-is-a-batch-class-instantiated-when-you-schedule-it%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown